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
[chore] The tmux, kitty and iTerm2 Operators are one agent wearing three names — declare backend-exclusive tools (no code) #403
terminal_list_targets takes backend: "all" | "tmux" | "kitty" | "iterm2" and defaults to all (connectors/terminal.ts:50). So the tmux Operator, the kitty Operator and the iTerm2 Operator are the same agent with three names and three descriptions, each of which is false about the other two.
Observed: the owner asked the tmux Operator about tmux sessions and it correctly reported two iTerm2 windows, because that is genuinely what it can reach.
The fix
A tmux-only connector already exists in the registry (registry.tsid: "tmux") with six backend-exclusive tools — tmux_list_sessions, tmux_capture_pane, tmux_run_command, tmux_send_keys, tmux_new_session, tmux_kill_session. Declaring those in the tmux Operator's capabilities.tools makes it a tmux agent by construction: there is no iTerm2 code path behind them to reach.
Because it is free and it makes one agent honest now, and because it clarifies the general ticket: the reason kitty and iTerm2 cannot be fixed the same way is precisely the argument for the primitive. Shipping a third and fourth parallel connector to express "one backend" is the workaround that does not scale.
The known cost recorded in 0099's header is filed as #409, verified and with the fix designed — please read it before pushing.
Two things the migration's note does not have, both measured:
tmux-operator is published, not draft (live D1 + GET /v1/agents — it is one of 15 catalog agents). The kitty/iTerm2 rows being draft does not bound the blast radius: the next subscriber gets a Terminal tab that has never worked.
Teaching the tab the tmux_* names is not a rename.terminal_* takes target: "tmux:main", tmux_* takes session: "main"; and /tmux/list rows carry no backend/id, both of which parseTargets (TmuxTab.tsx:59-61) requires — so a naive swap yields a silently empty list, which is a worse failure than the 403.
Nothing here asks 0099 to change. The consent carry is right and covers cda75e28 (which holds terminal write but no tmux row today — verified), and leaving surfaces:["tmux"] declared is the right call. #409 argues the console fix should ship alongside rather than wait for #402/#404, and adds the generalisation: TmuxTab.tsx is the only unguarded hardcoded tool name in the console tree — McpConnections.tsx:226 already does it correctly — so this is the moment to ratchet it at one.
Shipped in 78d2cfe as part of 782f00c, together with #409 — deliberately as one unit, because #403 alone would have shipped a dead Terminal tab on a published catalog agent (tmux-operator is live, not draft; confirmed against /v1/agents, 15 agents).
Verified in production: migration 0099_tmux_operator_backend_exclusive_tools.sql applied to the remote D1 — 🚣 Executed 5 commands, ✅ — in the deploy for 782f00c.
Checkbox 1 ✅ — the six tmux_* names were verified against TMUX_TOOLS in lib/connectors/tmux.ts rather than transcribed from the ticket.
Two things the ticket did not anticipate, both handled:
Write consent would have been silently revoked. Consent is keyed by connector. 0073 copied tmux→terminal and kept the originals, so a pre-0073 instance has both — but the instance subscribed on 2026-08-07 has only a terminal row. The migration mirrors 0073 back, scoped by a.slug = 'tmux-operator' so an unrelated agent's terminal grant cannot become a tmux grant. Checked on a sqlite fixture: the new instance gains tmux/write, the other agent's instance does not, re-running is a no-op.
Checkbox 2 ✅ — honest descriptions rather than unpublishing. Both kitty-operator and iterm-operator are already draft in prod, so unpublishing is a no-op that leaves the misleading copy in place; the sentence travels with the row if anyone publishes it later. Each now states it is not limited to the backend in its name.
Checkbox 3 ✅ — lintAgentClaims re-run over all three rows: 0 violations, before and after. Worth recording why that tick is not a verdict: the lint only asks whether a runtime claim is backed by runtime/workflow, and all three genuinely declare runtime: "coding". What they overstate is which resource the runtime reaches — the defect class this ticket is about, which the lint structurally cannot see. That is now written down as a test rather than left to be rediscovered.
Activity
docs: capability constraints — declaring what an agent may PASS, not …
serge-ivo commentedon Aug 8, 2026
The known cost recorded in 0099's header is filed as #409, verified and with the fix designed — please read it before pushing.
Two things the migration's note does not have, both measured:
tmux-operatorispublished, not draft (live D1 +GET /v1/agents— it is one of 15 catalog agents). The kitty/iTerm2 rows being draft does not bound the blast radius: the next subscriber gets a Terminal tab that has never worked.tmux_*names is not a rename.terminal_*takestarget: "tmux:main",tmux_*takessession: "main"; and/tmux/listrows carry nobackend/id, both of whichparseTargets(TmuxTab.tsx:59-61) requires — so a naive swap yields a silently empty list, which is a worse failure than the 403.Nothing here asks 0099 to change. The consent carry is right and covers
cda75e28(which holdsterminalwrite but notmuxrow today — verified), and leavingsurfaces:["tmux"]declared is the right call. #409 argues the console fix should ship alongside rather than wait for #402/#404, and adds the generalisation:TmuxTab.tsxis the only unguarded hardcoded tool name in the console tree —McpConnections.tsx:226already does it correctly — so this is the moment to ratchet it at one.fix(agents): the tmux Operator is a tmux agent by construction, not b…
serge-ivo commentedon Aug 8, 2026
Shipped in
78d2cfeas part of782f00c, together with #409 — deliberately as one unit, because #403 alone would have shipped a dead Terminal tab on a published catalog agent (tmux-operatoris live, not draft; confirmed against/v1/agents, 15 agents).Verified in production: migration
0099_tmux_operator_backend_exclusive_tools.sqlapplied to the remote D1 —🚣 Executed 5 commands, ✅ — in the deploy for782f00c.Checkbox 1 ✅ — the six
tmux_*names were verified againstTMUX_TOOLSinlib/connectors/tmux.tsrather than transcribed from the ticket.Two things the ticket did not anticipate, both handled:
0073copiedtmux→terminaland kept the originals, so a pre-0073 instance has both — but the instance subscribed on 2026-08-07 has only aterminalrow. The migration mirrors 0073 back, scoped bya.slug = 'tmux-operator'so an unrelated agent'sterminalgrant cannot become a tmux grant. Checked on a sqlite fixture: the new instance gainstmux/write, the other agent's instance does not, re-running is a no-op.Checkbox 2 ✅ — honest descriptions rather than unpublishing. Both
kitty-operatoranditerm-operatorare alreadydraftin prod, so unpublishing is a no-op that leaves the misleading copy in place; the sentence travels with the row if anyone publishes it later. Each now states it is not limited to the backend in its name.Checkbox 3 ✅ —
lintAgentClaimsre-run over all three rows: 0 violations, before and after. Worth recording why that tick is not a verdict: the lint only asks whether a runtime claim is backed byruntime/workflow, and all three genuinely declareruntime: "coding". What they overstate is which resource the runtime reaches — the defect class this ticket is about, which the lint structurally cannot see. That is now written down as a test rather than left to be rediscovered.Closing: delivered and verified in production.
feat(capabilities): tmux gets a constraint vocabulary, so the one PUB…