You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit ec928f4
Browse filesBrowse the repository at this point in the historyBrowse files
Copy file name to clipboardExpand all lines: FEATURES.md
+89-15Lines changed: 89 additions & 15 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -364,9 +364,10 @@ A categorized catalog of what's shipped. Cross-linked to `TEST-PLAN.md` checks.
364
364
-**Slack settings for authorized users:** replies requested by anyone allowed to use the agent add a
365
365
requester-bound **⚙️ Settings** footer button. Its Block Kit console mirrors the web
366
366
setup concepts across five pages — **General Settings**, **Resume Session**, **MCP**, **Skills**,
367
-
**Secrets** — picked from a single *Page* dropdown rather than a row of buttons that wrapped onto
368
-
a second line as pages were added. Legacy page ids (`runtime`, `access`, `network`) still resolve,
369
-
so a Settings view opened before the merge keeps navigating.
367
+
**Secrets** — shown as one row of tabs (*General · Resume · MCP · Skills · Secrets*) at the top,
368
+
the open page highlighted; short names keep the five on one row of the modal. Legacy page ids
369
+
(`runtime`, `access`, `network`) and the former *Page* dropdown still resolve, so a Settings view
370
+
opened before either change keeps navigating.
370
371
**General Settings** is everything that decides how the conversation runs: its Engine & model
371
372
scopes, then **Access** (with the network switch and the VPN row). Engine & model edits in place — six dropdowns,
372
373
no nested form, each saving the
@@ -375,7 +376,9 @@ A categorized catalog of what's shipped. Cross-linked to `TEST-PLAN.md` checks.
375
376
inside a thread, **that thread's own pins** (the per-thread overrides the `/model` wizard's "just
376
377
this thread" scope and the `claude`/`codex` directive write, which beat the channel at run time).
377
378
An unset field preselects the label of what it inherits — the gateway default, an org DM
378
-
template's value, or "Follow channel (…)" for a thread — so nothing reads as a blank; each
379
+
template's value, or "Follow channel (…)" for a thread — so nothing reads as a blank (an unset
380
+
model or effort names what the channel's OWN harness would use, so a Claude-pinned channel on a
381
+
Codex-default gateway reads the Claude default, never the Codex one); each
379
382
scope's model and effort lists follow the harness THAT scope resolves to, so a Codex-pinned
380
383
thread inside a Claude channel offers Codex models. Changing one field drops only the dependents
381
384
it invalidates (a harness change always clears the model, and the effort when the new harness
@@ -1807,8 +1810,32 @@ A categorized catalog of what's shipped. Cross-linked to `TEST-PLAN.md` checks.
1807
1810
object to pass straight through. The caller names which identity will run the destination tool
1808
1811
(`user` → `composio-user`, `agent` → `composio-agent`) and the key resolved for THAT identity is
1809
1812
the one spent, through the same precedence the MCP config uses; a named identity with no key is
1810
-
reported rather than silently replaced by the other one. The key never enters the container,
1811
-
never reaches the model, and never appears in an error message. Nothing is published.
1813
+
reported rather than silently replaced by the other one. Every run that has a Composio identity
1814
+
is also told this handoff rule in its per-run identity line (not only in the gateway-usage guide),
1815
+
so an engine that never opens the guide's sharing page still stages instead of relaying base64.
1816
+
The key never enters the container, never reaches the model, and never appears in an error
1817
+
message. Nothing is published.
1818
+
The REST upload accepts only a Composio PROJECT API key. The keys this gateway stores in personal
1819
+
mode are CONSUMER keys (`ck_…`, the hosted MCP's credential), which that endpoint rejects, so
1820
+
with a consumer key the gateway stages through the hosted MCP's own workbench instead: the daemon
1821
+
opens its own MCP session on the same key, writes the file into the sandbox in base64 chunks of
1822
+
768 KB (one request above ~5 MB is rejected), checks the md5 and mints the key with
1823
+
`get_mount_file_s3_key` — a key the model's separate session can then use. Bytes still never
1824
+
pass through the model. The sandbox path is a random directory plus a name reduced to safe
1825
+
characters, so a file name can never become Python source. Consumer-key staging is capped at
1826
+
25 MB (project keys keep 100 MB), within an overall 10-minute deadline; the MCP session is ended
1827
+
afterwards, but the sandbox copy is kept because the s3key IS that file's storage (deleting it
1828
+
breaks the upload — verified live). A known property of this route: the copy stays in that
1829
+
identity's Composio file storage, so on `composio-agent` backed by the organization key it is
1830
+
listable from any conversation sharing that key (its random directory name is not a secret) —
1831
+
stage on `composio-user`, or give the channel its own Composio key, when that matters. The bytes are read from the SAME descriptor the confinement
1832
+
check proved (O_NOFOLLOW + /proc/self/fd), bounded to the cap: the tool used to close that
1833
+
descriptor and reopen the file by path, so a symlink swapped in by the container between proof
1834
+
and read could have sent any file the daemon can read. Every error text is scrubbed of the key
1835
+
and of long base64 runs before it reaches the model. A path outside the working folder answers `Staging refused:`;
1836
+
anything that goes wrong after that answers `Staging failed:`, and the guide tells the model a
1837
+
refusal is final (no copying the file in to get around it) and a failure is reported, not
1838
+
worked around by pushing the bytes through a tool itself.
1812
1839
→ TEST-PLAN: Composio file staging.
1813
1840
-**Temporary public file links (`create_public_file_link`).** For destinations that ingest by URL
1814
1841
rather than by body, and for a person who simply wants a link. One file from the channel's own
@@ -2360,7 +2387,19 @@ are retired, bullet by bullet; everything else stands.
2360
2387
the thread-bound background, progress and approval tools) with `composio-user` for the
2361
2388
developer's own accounts, `composio-agent` for the channel's and the selected catalog servers,
2362
2389
`--strict-mcp-config` so the operator's own claude.ai connectors never load, and the run
2363
-
environment (`resolveRunEnv` + `safeSpawnEnv`) sourced by the `claude` wrapper. The files are per
2390
+
environment (`resolveRunEnv` + `safeSpawnEnv`) sourced by the `claude` wrapper. Codex gets the
2391
+
same session through its own `codex` wrapper (QA-0925: over SSH it had no gateway MCP, no Composio
2392
+
and no secrets): the session also writes `codex-args.sh` — exactly the `-c mcp_servers.*` /
2393
+
`apps.*` overrides a chat turn's Codex gets, with a Codex-minted gateway capability and the
2394
+
Composio/toolbox credentials in a 0600 `codex-secrets.json` their header helpers read, never argv
2395
+
— and the wrapper sources the secrets, prepends the overrides and, when started from HOME, `/` or
2396
+
a parent of the channel folder, starts Codex in the channel folder (its `AGENTS.md`); a turn's
2397
+
sandbox/approval flags are not carried, the developer answers Codex's own prompts. As in a turn,
2398
+
`apps._default.enabled=false` keeps the shared Codex sign-in's ChatGPT connectors (`codex_apps`,
2399
+
the operator's) off except apps the channel selected, and a selected server with no safe
2400
+
definition is left out. `with-secrets <command>` runs any other command with the developer's channel
2401
+
secrets. A Codex preparation failure is reported (`codexProblem` on `ssh_session_start`) and
2402
+
never costs the Claude session. The files are per
2364
2403
developer under `<artifacts>/ssh/users/<id>/`, chosen by the `CG_SSH_USER` name sshd sets from
2365
2404
the developer's authorized_keys line (`PermitUserEnvironment CG_SSH_USER`), and removed when
2366
2405
their last session ends, refreshed every 20 minutes AND within `CONFIG_REFRESH_DEBOUNCE_MS` of a
@@ -2379,7 +2418,11 @@ are retired, bullet by bullet; everything else stands.
2379
2418
container's namespaces; no container and no extra host port ever listens; the channel is named
2380
2419
in the ProxyCommand, so one key reaches several channels at once. A container with a live
2381
2420
session is never idle-stopped or evicted, and a rebuild waits for it like for a run; a dead peer
2382
-
is reaped by `ClientAlive` in about three minutes. Sessions are `ssh_sessions` rows and
2421
+
is reaped by `ClientAlive` in about three minutes. Each session also seeds VS Code's remote
2422
+
machine setting `files.dialog.defaultPath` with the channel's effective work folder (a custom
2423
+
folder included), so Remote-SSH's File → Open Folder starts there instead of `/home/agent`;
2424
+
the merge is best effort, follows a changed folder, and never overrides a developer's own value.
2425
+
Sessions are `ssh_sessions` rows and
2383
2426
`ssh_session_start`/`ssh_session_end` events; refusals are `ssh_attach_refused` with the reason
2384
2427
the developer saw. The image ships `openssh-server` (spec 1.4.0) and marks the `agent` account
2385
2428
key-only (`*`, not useradd's locked `!`). It carries no SSH host private key: the package's
@@ -2859,10 +2902,18 @@ are retired, bullet by bullet; everything else stands.
2859
2902
rather than by reading the shared identity that holds other people's accounts (and the mirror:
2860
2903
"your X" never touches `composio-user`); that `COMPOSIO_MANAGE_CONNECTIONS` initiates connections
2861
2904
rather than listing them; and that only the gateway's `run_in_background` /
2862
-
`run_agent_in_background` / `create_schedule` can report back after a turn ends — stated here
2905
+
`run_agent_in_background` / `create_schedule` can report back after a turn ends; and that no run
2906
+
runs or recommends `podman system prune`/`reset`, `podman image prune -a`, `podman volume prune`
2907
+
or `docker system prune`, each of which can delete channel homes or the runtime image — disk
2908
+
cleanup goes through `npm run runtime:storage`, with `-- --apply` only when an admin asks — stated here
2863
2909
because a skill body is read only when the model opens it, and one engine reliably did not
2864
-
(retest, 2026-09-06). They ride clean mode too, are engine-neutral,
2865
-
and stay under 4 KB with the switches so the always-on prompt weight is read rather than skimmed. Editable three ways: the admin UI Instructions tab (edits the real
2910
+
(retest, 2026-09-06; the prune rule after a 2026-09-25 run that skipped the skill and recommended
2911
+
both prunes). They ride clean mode too, are engine-neutral,
2912
+
and stay under 4 KB with the switches so the always-on prompt weight is read rather than skimmed
2913
+
(4,085 bytes in the measured configuration after the prune rule — the header note, the rules'
2914
+
intro and the network line were tightened to make room; the longest switch combination, Admin +
2915
+
Auto with Lean off, is 4,120 bytes, down from 4,126 before, and a test stops it growing. The next
2916
+
always-on rule has to trade space for it). Editable three ways: the admin UI Instructions tab (edits the real
2866
2917
file; managed block shown read-only with a Settings link; hash-guarded against concurrent
2867
2918
writes), by hand, or by asking the agent — the `update_channel_instructions` gateway MCP tool
2868
2919
appends a rule in any mode (replace = admin-only). New sessions and `/clear` pick the file up
@@ -2952,9 +3003,26 @@ are retired, bullet by bullet; everything else stands.
2952
3003
pending (an armed *clear* toggle, a typed password or key), because a pending action captured as
2953
3004
"already saved" would silently never run. → TEST-PLAN: Admin UI.
2954
3005
-**Google Drive two-way sync (scheduled)**: a per-channel Drive folder link (channel settings)
2955
-
is bisync'd on a timer into a dedicated `Drive/` subfolder of that channel's working folder —
2956
-
never the folder root, so the confinement scaffolding (`.claude/`, `CLAUDE.md`, `MEMORY.md`,
2957
-
`memory/`, `uploads/`) is never synced or overwritten. Auth is a Workspace service account
3006
+
is bisync'd on a timer with that channel's WHOLE working folder (it used to be a `Drive/`
3007
+
subfolder; QA-0925). A filters file, matched case-insensitively and applied in both directions,
3008
+
keeps out agent instructions, skills and MCP config at any depth (`CLAUDE.md`, `AGENTS.md`,
0 commit comments