Skip to content

Commit ec928f4

Browse files
Merge pull request #111 from makeitfutureDev/release/0.5.6
Release 0.5.6
2 parents 15e831b + f25453f commit ec928f4

36 files changed

Lines changed: 1907 additions & 227 deletions

‎CHANGELOG.md‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,53 @@ product overview.
1616
> | Makeitfuture Sustainable Use License 1.1 | 2026-08-20 | never published |
1717
> | Makeitfuture Sustainable Use License 1.0 | 2026-08-06 | never published |
1818
19+
## 0.5.6 — 2026-09-25
20+
21+
- Codex over SSH now gets what a chat turn's Codex gets: the gateway tools, your own and the
22+
channel's Composio accounts, the channel's MCP servers and your channel secrets, starting in
23+
the channel folder. `with-secrets <command>` runs any other command with those secrets.
24+
- Slack **⚙️ Settings** pages are tabs again: one row — General · Resume · MCP · Skills · Secrets —
25+
with the open page highlighted, instead of the *Page* dropdown.
26+
- Google Drive sync now syncs the channel's whole folder instead of a `Drive/` subfolder. Agent
27+
instructions and skills, channel memory, secrets, `.git` and dependency trees never sync, a
28+
`.driveignore` file adds a channel's own exclusions, and symlinks are never followed. rclone now
29+
runs in a throwaway container that sees only the channel folder, so a planted link can never
30+
write onto the host (this also closes that hole in the old subfolder sync). A folder that
31+
contains the home or the gateway's data is refused.
32+
- VS Code over SSH now starts in the channel's folder: File → Open Folder in a Remote-SSH window
33+
opens at the channel folder (or its custom folder) instead of the container's home.
34+
- Handing a generated file to Drive, Gmail or another Composio tool now goes through the gateway's
35+
staging on every engine: the rule rides each run's Composio identity notice, so Codex no longer
36+
falls back to pushing the file through Composio's workbench as base64.
37+
- Google Drive sync no longer stalls on a folder that was empty when it was linked. The first sync
38+
of two empty sides succeeded, but every later sync then failed (rclone: "Empty prior Path1
39+
listing"), so files never moved. A sync record that holds no files now triggers a fresh resync.
40+
- Slack **⚙️ Settings** no longer claims a channel pinned to Claude inherits the Codex default
41+
model (or the reverse): an unset model or effort now names what the channel's own harness would
42+
actually use. The web admin's VPN status also stops saying "not configured" twice.
43+
44+
- Sending a generated file to Drive, Gmail and other Composio tools works again — or rather, works
45+
for the first time on a gateway using personal Composio tokens. `stage_file_for_composio` sent the
46+
stored token to Composio's REST upload, which only accepts project API keys; the tokens stored
47+
here are consumer (MCP) keys, so every stage failed with "Invalid API key" and the model told the
48+
user their key was broken. With a consumer key the gateway now stages through the hosted MCP's own
49+
workbench, daemon-side and in chunks, so the file still never passes through the conversation
50+
(limit 25 MB there, 100 MB with a project key). A real problem now reads `Staging failed:`;
51+
`Staging refused:` is kept for files that are not the conversation's own (outside its folder, a
52+
symlink, not a regular file).
53+
- Security: staging a file for Composio now reads the exact file whose location it checked. It
54+
used to check the file, close it and open it again by name, and the conversation's container can
55+
write its own folder — so in that gap the name could be swapped for a link to a file elsewhere on
56+
the host, which the gateway would then have read and uploaded. Found by review during the
57+
2026-09-25 QA campaign. The tool first shipped in 0.5.3, and on a gateway using consumer keys (the
58+
default) no stage could complete before this release, so the gap could not have been used there.
59+
- The assistant no longer suggests a blanket container prune when asked about disk space. The
60+
rule against `podman system prune`/`reset`, `podman image prune -a`, `podman volume prune` and
61+
`docker system prune` lived only in the operating guide, and a run that answered without opening
62+
the guide recommended two of them — on this host they can delete channel homes or the runtime
63+
image. The rule now sits among the few hard rules every run of every engine reads, pointing at
64+
`npm run runtime:storage` (removal only with `-- --apply` when an admin asks).
65+
1966
## 0.5.5 — 2026-09-24
2067

2168
- The runtime image no longer carries SSH host private keys. Installing `openssh-server` generated

‎FEATURES.md‎

Lines changed: 89 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -364,9 +364,10 @@ A categorized catalog of what's shipped. Cross-linked to `TEST-PLAN.md` checks.
364364
- **Slack settings for authorized users:** replies requested by anyone allowed to use the agent add a
365365
requester-bound **⚙️ Settings** footer button. Its Block Kit console mirrors the web
366366
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.
370371
**General Settings** is everything that decides how the conversation runs: its Engine & model
371372
scopes, then **Access** (with the network switch and the VPN row). Engine & model edits in place — six dropdowns,
372373
no nested form, each saving the
@@ -375,7 +376,9 @@ A categorized catalog of what's shipped. Cross-linked to `TEST-PLAN.md` checks.
375376
inside a thread, **that thread's own pins** (the per-thread overrides the `/model` wizard's "just
376377
this thread" scope and the `claude`/`codex` directive write, which beat the channel at run time).
377378
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
379382
scope's model and effort lists follow the harness THAT scope resolves to, so a Codex-pinned
380383
thread inside a Claude channel offers Codex models. Changing one field drops only the dependents
381384
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.
18071810
object to pass straight through. The caller names which identity will run the destination tool
18081811
(`user` → `composio-user`, `agent` → `composio-agent`) and the key resolved for THAT identity is
18091812
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.
18121839
→ TEST-PLAN: Composio file staging.
18131840
- **Temporary public file links (`create_public_file_link`).** For destinations that ingest by URL
18141841
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.
23602387
the thread-bound background, progress and approval tools) with `composio-user` for the
23612388
developer's own accounts, `composio-agent` for the channel's and the selected catalog servers,
23622389
`--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
23642403
developer under `<artifacts>/ssh/users/<id>/`, chosen by the `CG_SSH_USER` name sshd sets from
23652404
the developer's authorized_keys line (`PermitUserEnvironment CG_SSH_USER`), and removed when
23662405
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.
23792418
container's namespaces; no container and no extra host port ever listens; the channel is named
23802419
in the ProxyCommand, so one key reaches several channels at once. A container with a live
23812420
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
23832426
`ssh_session_start`/`ssh_session_end` events; refusals are `ssh_attach_refused` with the reason
23842427
the developer saw. The image ships `openssh-server` (spec 1.4.0) and marks the `agent` account
23852428
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.
28592902
rather than by reading the shared identity that holds other people's accounts (and the mirror:
28602903
"your X" never touches `composio-user`); that `COMPOSIO_MANAGE_CONNECTIONS` initiates connections
28612904
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
28632909
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
28662917
file; managed block shown read-only with a Settings link; hash-guarded against concurrent
28672918
writes), by hand, or by asking the agent — the `update_channel_instructions` gateway MCP tool
28682919
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.
29523003
pending (an armed *clear* toggle, a typed password or key), because a pending action captured as
29533004
"already saved" would silently never run. → TEST-PLAN: Admin UI.
29543005
- **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`,
3009+
`AGENTS.override.md`, `CLAUDE.local.md`, `.mcp.json`, `.claude/`, `.agents/`, `.codex/`), channel
3010+
memory (`MEMORY.md`, `memory/`), secrets (`.env*`, `.ssh/`, key and credential files, per-run env
3011+
folders), `.git` (file or folder), `.worktrees`, dependency trees and `*.rclonelink`, so a Drive
3012+
editor cannot plant instructions for the agent and no local secret is pushed. A `.driveignore`
3013+
at the folder root adds the channel's own exclusions (every line becomes an exclude). Every
3014+
symlink in the folder is excluded for the pass, and rclone runs in a throwaway container
3015+
(`podman run --rm`, all capabilities dropped) that mounts only the channel folder at its real
3016+
path and the sync state, filters (read-only) and key (read-only) at a random path per pass: a
3017+
link planted to anywhere else — even mid-pass — resolves inside that container and can never
3018+
write onto the host or into the sync state. A timed-out pass force-removes its container. A folder
3019+
that is or contains the operator's home, the gateway root or the workspace root, a hidden
3020+
configuration folder of the home (`~/.ssh`, `~/.config`, …), or a workspace folder that is not
3021+
this channel's own (another channel's, the platform parent, `.runtime`) is refused — so is a Lean
3022+
(clean-mode) channel, whose runs use a bare folder inside the gateway root. The first sync of a link merges both sides,
3023+
the newer copy winning where both hold the same path; the completed `--resync` records what it
3024+
was made for (local root, Drive folder, filters), and changing any of them makes the next pass a
3025+
fresh `--resync` from a clean state dir instead of a bisync against the old pair's listings. Auth is a Workspace service account
29583026
(+ optional domain-wide-delegation subject), passed to `rclone bisync` via flags — no interactive
29593027
`rclone config`, no per-user OAuth, no secret in the child env. The key is entered by **pasting
29603028
the service-account JSON** into the settings page: stored write-only (validated as a real SA key,
@@ -2987,7 +3055,13 @@ are retired, bullet by bullet; everything else stands.
29873055
through daemon IPC (`drivesync` kind) so the pass runs in the daemon on both MCP transports,
29883056
shares the per-channel in-flight guard and outlives the turn; it waits ~40 s for the outcome and
29893057
otherwise says the pass is still running. `get_channel_drive_folder` now also reports the last
2990-
pass (time, ok/failed, first-resync, reason). Manual passes obey the same global switch, key and
3058+
pass (time, ok/failed, first-resync, reason). A pass whose prior rclone listings record no file
3059+
(including the empty listings rclone set aside as `.lst-err` after refusing them) runs as a
3060+
`--resync` again, so a folder linked while empty on both sides starts syncing as soon as either
3061+
side gets a file instead of failing every tick with rclone's exit 7. Any other missing listing
3062+
stays an error: after a deliberate delete-everything rclone sets aside NON-empty listings, and a
3063+
resync there would copy the deleted files back.
3064+
Manual passes obey the same global switch, key and
29913065
rclone checks as the schedule; a pass already running for a channel is never doubled, and a
29923066
manual sweep never stacks on the scheduled one. Status surfaces show a concise diagnostic, never
29933067
the raw rclone tail. Admin API: `POST /api/channels/:id/sync-now`, `GET

0 commit comments

Comments
 (0)