Skip to content

Connector tools only for the Bots the owner enables; shared Bots never get them - #346

Merged
Birfy merged 4 commits into
mainfrom
feat/335-connector-bot-toggle
Oct 18, 2026
Merged

Birfy merged 4 commits into
mainfrom
feat/335-connector-bot-toggle

Conversation

@Birfy

@Birfy Birfy commented Oct 18, 2026

Copy link
Copy Markdown
Collaborator

Fixes #335

What changed

Connector tools (mcp__github__*, mcp__composio__*) were mounted for every Bot and Session, including a Bot shared with a friend. Now:

  • Per-Bot switch, default off. bot.connectors = { github?, composio? }; absent means off. Switches: Settings → Connectors → Choose Bots (a switch per local Bot; the row says "On for N of M Bots") and a Connectors card in each Bot's settings. zh strings added.
  • Shared Bots never get them, whatever the switch says. sharedWith() (reach.js) is the one definition: a grant, or a current room with a friend's Bot, another person, another owner's Bot or a room code. It is read on every call. A grant also clears the Bot's switches, and turning one on for a shared Bot is refused naming who it is shared with.
  • Enforced in the tool path, not the UI: a ctx.tools.guard refuses every connector call from a Bot Session (own chat, group Session, earlier part; sub-agents follow their parent), and restrict({deny}) removes the tools from that Agent's list (on create, on resolveAgent, on a switch/grant change, on tools/change when a connector mounts later). The DSH Agent and plain DSH Sessions (owner-only) are untouched.
  • Upgrade behaviour: every existing Bot starts off, including ones that used the tools (connectors are new since Connect many apps through one Composio key; footer button becomes Connect apps #323). New/duplicated Bots start off; Disconnect turns the connector off for every Bot. bot-connectors is SHARED_BLOCKED.
  • Composio session address is no longer in state.json. Only { id, apps } is kept. Composio's reference lists mcp.url as required in the create and GET /tool_router/session/{id} answers (x-api-key), so the mount asks for the address by id + key, accepts only https on the API host, and keeps it in memory. Composio's docs show the address always paired with the key header and call the exported url+headers config a secret, but never say the address alone cannot authenticate, so it is not stored. An older record's url is rewritten away at start; the in-memory address is redacted from connector errors.

Docs: core-host (rule, upgrade, session address), core-client, team, README en/zh. Changelog fragment: changelog.d/335-connector-bots.security.md.

Verification at the tip

Tip SHA: 8242518
Ran: ARCH_DOCS_BASE=origin/main TZ=UTC npm run verify (Node 24) → exit 0: core 889/889, social 230/230, tree 56/56, timeline 80/80, .github/scripts 86/86, interop 37/37, live-e2e 16/16; arch-docs OK; ds-bot-patches check OK; build-client --check OK. New: connector-access.test.js (10: default off, toggled-on local Bot gets only its connector, shared Bot via grant/room gets none even with the flag forced on, sub-agents, late-mounting connector, disconnect, duplicate), connector-bots.test.js (6), connector session tests (+4, 6 updated), notices test (+1), authz and install order rows. The new test files fail on current main (the module does not exist there).
Not run: the dev/bench.sh smoke test (needs a DSH checkout at ../deepseek-harness, not on this box); live Composio/GitHub calls (no key; the session-address flow is tested against a stand-in for the documented API); a visual check of the new rows in a browser.
Reviewer notes: T3 expected (reach.js, authz.js, host tool path). core/ and patches/ds-bot/ are generated together; new core files are stored whole in patches/ds-bot/files/.

Birfy added 3 commits October 18, 2026 12:50
…hem on for (#335)

Connector tools (mcp__github__*, mcp__composio__*) were mounted for every Bot and Session, including a
Bot shared with a friend. They are now a per-Bot switch, default off, and a Bot that is shared with
anyone never gets them: a guard refuses the calls and the Agent's tool list drops them. Upgrade: every
Bot starts off. The Composio session address is no longer kept in state.json; the id is stored and the
address is asked for at each mount.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-18T12:55:01.833049Z 8242518 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 82425181b2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const botOfAgent = (agent) => {
let id = agent?.id ?? agent?.session?.id
let header = agent?.session?.header
for (let depth = 0; id !== undefined && depth < MAX_DEPTH; depth += 1) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Fail closed for deeply nested Bot sub-agents

When a Bot creates eight nested sub-agents, this loop reaches its limit immediately before checking the root Bot session, so botOfAgent() returns undefined. Both the tool-list path and the guard then treat that Agent as an owner-only plain session, leaving globally mounted connector tools visible and executable even when the originating Bot is shared or has the connector disabled. An unresolved parent chain must remain restricted rather than falling back to the plain-session policy.

Useful? React with 👍 / 👎.

Comment on lines +168 to +169
// A Bot's sharing changed (a grant was added or removed): apply it to the Agents it already has.
const refreshConnectorTools = botId => eachAgent(agent => { if (botOfAgent(agent)?.id === botId) hideConnectorTools(agent) })

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Refresh masks when room sharing changes

This refresh path is invoked for connector switches and grants, but room participant changes also change sharedWith() and never call it. If a friend, another person, or another owner's Bot joins after an Agent is created, that Agent continues advertising connector tools even though calls are refused; more importantly, when that participant leaves, a still-enabled connector remains hidden and unusable until an unrelated tool change, Agent recreation, or restart. The room add/remove and inbound-room transition paths need to refresh affected Bots' masks.

Useful? React with 👍 / 👎.

@Birfy

Birfy commented Oct 18, 2026

Copy link
Copy Markdown
Collaborator Author

Maintenance review at tip 8242518 (posted from the author's account, so it is not an independent PASS and records none).

Verdict: needs change before the one-click approval (risk tier T3). I read connector-access.js, reach.js, connectors.js, channel.js, authz.js and delivery.js; the design matches #335 (off by default, never on for a shared Bot, switch only through bot-connectors, which is shared-blocked). I confirm Codex's two findings:

  1. Fail-open when the Bot cannot be resolved (real, fix before merge). botOfAgent returns undefined when the parent chain runs out (the 8-step cap, or a parent Session whose header is no longer in ctx.sessions). The guard and denied() then treat that Agent as a plain owner session and allow the connector tools, even if the originating Bot is shared or has the connector off. For a gate whose purpose is "a shared Bot never gets the owner's apps", an unresolved sub-agent chain should stay restricted (deny), and only a chain that really ends at a non-Bot session should keep the tools. Please add a test for a sub-agent whose parent cannot be resolved.
  2. Stale tool list after room changes (lower severity). refreshConnectorTools runs on grant changes and switches, not when a friend/person/Bot joins or leaves a room. The call-time guard still refuses correctly (it reads sharedWith at call time), so this is not an access hole; the tool list can advertise tools that then fail, and after the peer leaves a still-enabled connector stays hidden until a restart or a new Agent. Refreshing the affected Bots' masks on room add/remove would close it.

CI is green at this tip. Needs a fix commit for item 1, a fresh independent review at the new tip, then the maintainer's approval.

…ned; follow room sharing in the tool list (#335)

Docs: docs/subsystems/core-host.md updated.
@Birfy
Birfy added this pull request to the merge queue Oct 18, 2026
Merged via the queue into main with commit d75dc2d Oct 18, 2026
8 checks passed
@Birfy
Birfy deleted the feat/335-connector-bot-toggle branch October 18, 2026 13:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Connector tools (Composio, GitHub) reach every Bot, including Bots shared under a per-Bot grant; make them per-Bot opt-in

1 participant