Skip to content

Commit 0888fa7

Browse files
committed
Merge main into dev3/1271-catch-fail-closed (#1271)
#1275 landed on main and changed the same page, the same refusal string and the same assertions. Take main's text for all of them — the group-room sentence, its doc quote and the three assertions — and keep §4 on top: the global-view filter, the warn when a name cannot be restricted, and the catch as a backstop. No §2 text is reverted. Refs #1271
2 parents 78eafc5 + 6a88b8a commit 0888fa7

6 files changed

Lines changed: 18 additions & 11 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
A connector call refused in a group room now says the tool is not available there, instead of pointing the caller at its own chat — a friend's Bot has no composio tools in its chat either ([#1271](https://github.com/ThinkFlowLab/FlyBot/issues/1271)).

‎core/src/host/connector-access.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ export function install(rt) {
101101
// a Bot that reads it knows where the tool went, and the refusal is the same for every origin.
102102
if (roomHidden(who, name)) {
103103
return id === COMPOSIO
104-
? `The ${COMPOSIO} connector is available in your own chat, not in a group room (#1237).`
104+
? `The ${COMPOSIO} connector is not available in a group room (#1237).`
105105
: `Schedules are managed in your own chat, not in a group room (#1237).`
106106
}
107107
if (id === undefined) return undefined

‎core/test/connector-access.test.js‎

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ test('default: a connected connector is on for every Bot, old or new, in its cha
145145
// #1237: it is the one Session that carries neither the composio tools nor the schedule ones.
146146
assert.deepEqual([...hiddenFrom('room-writer')].sort(), [...COMPOSIO].sort(), 'the mask lands with the Agent, not with the turn')
147147
const done = await turn('room-writer', { kind: 'bot', form: 'relay', role: 'room', roomId: 'room', exchangeId: 'rr', senderName: 'You', hop: 0, origin: 'owner' })
148-
assert.match(guard(COMPOSIO[0], 'room-writer'), /own chat, not in a group room/)
148+
assert.match(guard(COMPOSIO[0], 'room-writer'), /not available in a group room/)
149149
assert.equal(guard(GITHUB[0], 'room-writer'), undefined, 'github stays: a room judges pull requests')
150150
done()
151151
assert.equal(guard(COMPOSIO[0], 'plain'), undefined)
@@ -210,9 +210,11 @@ test('#1237: a room turn drops the composio and schedule tools and keeps github
210210
// notice a renamed or emptied SCHEDULE_TOOLS; these are the DSH names (0.2.0-rc.2).
211211
for (const name of ['schedule_create', 'schedule_list', 'schedule_update', 'schedule_delete']) assert.ok(allHidden('room-writer').has(name), `${name} is hidden in a room`)
212212
for (const name of [...GITHUB, ...OWN]) assert.equal(guard(name, 'room-writer'), undefined, `${name}`)
213-
for (const name of [...COMPOSIO, ...SCHEDULE]) assert.match(guard(name, 'room-writer'), /own chat, not in a group room/, name)
213+
for (const name of [...SCHEDULE]) assert.match(guard(name, 'room-writer'), /own chat, not in a group room/, name)
214214
assert.equal(guard('schedule_list', 'room-writer'), 'Schedules are managed in your own chat, not in a group room (#1237).')
215-
assert.equal(guard(COMPOSIO[0], 'room-writer'), 'The composio connector is available in your own chat, not in a group room (#1237).')
215+
// #1271: a friend's Bot that holds a room Session is refused here too, and it has no composio tools in
216+
// its own chat either, so the refusal no longer points there.
217+
for (const name of [...COMPOSIO]) assert.equal(guard(name, 'room-writer'), 'The composio connector is not available in a group room (#1237).', name)
216218
})
217219

218220
test('#1237: a Bot\'s own chat keeps composio and the schedule tools', async (t) => {
@@ -277,7 +279,8 @@ test('#601: a group round of any origin keeps the github tools, also with a frie
277279
begin('room-writer')
278280
await tick()
279281
for (const name of GITHUB) assert.equal(guard(name, 'room-writer'), undefined, `${origin}: ${name}`)
280-
for (const name of [...COMPOSIO, ...SCHEDULE]) assert.match(guard(name, 'room-writer'), /own chat, not in a group room/, `${origin}: ${name}`)
282+
for (const name of [...SCHEDULE]) assert.match(guard(name, 'room-writer'), /own chat, not in a group room/, `${origin}: ${name}`)
283+
for (const name of [...COMPOSIO]) assert.match(guard(name, 'room-writer'), /not available in a group room/, `${origin}: ${name}`)
281284
assert.deepEqual([...allHidden('room-writer')].sort(), [...COMPOSIO, ...SCHEDULE].sort(), `${origin}: the mask is the same in every origin`)
282285
end('room-writer')
283286
}

‎docs/subsystems/core-host.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ The connector tools are mounted once for the whole DSH profile, so which Bot may
355355
- **Boundaries that stay.** A friend's Bot (`bot.remote`) never gets the tools. An Agent whose Bot cannot be determined (a parent Session that is gone, or a parent chain deeper than eight) gets none either (fail closed), so an unresolved sub-agent never reads as a plain owner Session. A chain that really ends at a Session of no Bot (the DSH Agent, a plain Session) keeps them. A sub-agent Session is followed to its parent Bot (`whoseAgent`, also on `rt.whoseAgent` for `secrets.js`).
356356
- **Enforcement is two layers.** A `ctx.tools.guard` refuses every `mcp__github__*` / `mcp__composio__*` call that is not allowed, and `agent.ctx.tools.restrict({ deny })` takes the same tools out of that Agent's tool list. The mask is refreshed when the Agent is created, on every `resolveAgent` (delivery), when a switch changes, and on `tools/change` (a connector that mounts or goes away later). Turns and sharing no longer change it. DSH's hidden "Tool added/removed" notices for these tools are not drawn in the chat (`cells.js`).
357357
- **A mask that cannot be rebuilt keeps the previous one (#1271).** `hideConnectorTools` lifts the old mask before it reads `schemas()` again, so a `restrict()` that refuses a name would otherwise leave the Agent with no mask and every tool listed again. The rebuild now names only what the global view (`schemas()` with no Agent) can restrict, and if a restriction still fails the catch puts the previous deny list back: the guard refuses a call either way, and a list that widens by itself is a wrong answer. A denied name the Agent itself serves stays listed — `restrict()` cannot name a scope's own tool — and the warn line says so.
358-
- **A group room turn carries no composio tools and no schedule tools (#1237).** The eight tools — `mcp__composio__*` and `schedule_create` / `schedule_list` / `schedule_update` / `schedule_delete` — are 20,355 characters of schema (≈5.2k tokens at the measured 3.92 chars/token) on every call, and a room round uses neither: an outside app is reached from the Bot's own chat, and a schedule belongs to a Session (#628), so managing it from a member's room Session would write to the wrong place. The rule is one function in `connector-access.js`: the same `denied()` that answers the per-Bot switches also names these eight when `rt.groupOwners` — the store's Session → room index — has the Agent's Session, so the mask rides the existing refresh points (Agent created, `resolveAgent` on every delivery, a switch, `tools/change` for a plugin that mounts later) and the guard refuses a call with `… available in your own chat, not in a group room`. The github tools stay (a room judges pull requests), and the Bot's own chat, its earlier parts and its sub-agents are unchanged. Taking the github tools out too is a separate decision, in #1237's 「不做」.
358+
- **A group room turn carries no composio tools and no schedule tools (#1237).** The eight tools — `mcp__composio__*` and `schedule_create` / `schedule_list` / `schedule_update` / `schedule_delete` — are 20,355 characters of schema (≈5.2k tokens at the measured 3.92 chars/token) on every call, and a room round uses neither: an outside app is reached from the Bot's own chat, and a schedule belongs to a Session (#628), so managing it from a member's room Session would write to the wrong place. The rule is one function in `connector-access.js`: the same `denied()` that answers the per-Bot switches also names these eight when `rt.groupOwners` — the store's Session → room index — has the Agent's Session, so the mask rides the existing refresh points (Agent created, `resolveAgent` on every delivery, a switch, `tools/change` for a plugin that mounts later) and the guard refuses a call with `… not available in a group room` — the room answer comes before the friend's-Bot one, and a friend's Bot has no composio tools in its own chat either, so the refusal does not send it there. The github tools stay (a room judges pull requests), and the Bot's own chat, its earlier parts and its sub-agents are unchanged. Taking the github tools out too is a separate decision, in #1237's 「不做」.d3-1274m/docs/subsystems/core-host.md
359359
- **Bots prefer the connector for GitHub.** The Bot rules (`prompt.js`) say to use the GitHub connector tools first and `$DSH_SECRET_GITHUB_TOKEN` with `gh` or `git` in the shell only for what they cannot do, such as `git push`.
360360
- **Defaults and disconnect.** New and duplicated Bots (and tree children) start on. The Connectors row reads "On for all N Bots", "On for K of N Bots" or "Off for every Bot"; **Choose Bots** and each Bot's **Connectors** card show one switch per Bot and connector. **Disconnect** clears every Bot's record for that connector. The switches change only through the `bot-connectors` channel endpoint, which Bots do not call; it is in `SHARED_BLOCKED`, so with more than one member it is refused like the other connector writes.
361361
- **Whose request a turn answers** (`turn-origin.js`, recorded by `turns.js`, carried by relays, posts and resume records) is still tracked: standing approvals (`approvals.js`) and resumed turns read it. It no longer decides connector tools or secrets.

‎patches/ds-bot/files/src/host/connector-access.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ export function install(rt) {
101101
// a Bot that reads it knows where the tool went, and the refusal is the same for every origin.
102102
if (roomHidden(who, name)) {
103103
return id === COMPOSIO
104-
? `The ${COMPOSIO} connector is available in your own chat, not in a group room (#1237).`
104+
? `The ${COMPOSIO} connector is not available in a group room (#1237).`
105105
: `Schedules are managed in your own chat, not in a group room (#1237).`
106106
}
107107
if (id === undefined) return undefined

‎patches/ds-bot/files/test/connector-access.test.js‎

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ test('default: a connected connector is on for every Bot, old or new, in its cha
145145
// #1237: it is the one Session that carries neither the composio tools nor the schedule ones.
146146
assert.deepEqual([...hiddenFrom('room-writer')].sort(), [...COMPOSIO].sort(), 'the mask lands with the Agent, not with the turn')
147147
const done = await turn('room-writer', { kind: 'bot', form: 'relay', role: 'room', roomId: 'room', exchangeId: 'rr', senderName: 'You', hop: 0, origin: 'owner' })
148-
assert.match(guard(COMPOSIO[0], 'room-writer'), /own chat, not in a group room/)
148+
assert.match(guard(COMPOSIO[0], 'room-writer'), /not available in a group room/)
149149
assert.equal(guard(GITHUB[0], 'room-writer'), undefined, 'github stays: a room judges pull requests')
150150
done()
151151
assert.equal(guard(COMPOSIO[0], 'plain'), undefined)
@@ -210,9 +210,11 @@ test('#1237: a room turn drops the composio and schedule tools and keeps github
210210
// notice a renamed or emptied SCHEDULE_TOOLS; these are the DSH names (0.2.0-rc.2).
211211
for (const name of ['schedule_create', 'schedule_list', 'schedule_update', 'schedule_delete']) assert.ok(allHidden('room-writer').has(name), `${name} is hidden in a room`)
212212
for (const name of [...GITHUB, ...OWN]) assert.equal(guard(name, 'room-writer'), undefined, `${name}`)
213-
for (const name of [...COMPOSIO, ...SCHEDULE]) assert.match(guard(name, 'room-writer'), /own chat, not in a group room/, name)
213+
for (const name of [...SCHEDULE]) assert.match(guard(name, 'room-writer'), /own chat, not in a group room/, name)
214214
assert.equal(guard('schedule_list', 'room-writer'), 'Schedules are managed in your own chat, not in a group room (#1237).')
215-
assert.equal(guard(COMPOSIO[0], 'room-writer'), 'The composio connector is available in your own chat, not in a group room (#1237).')
215+
// #1271: a friend's Bot that holds a room Session is refused here too, and it has no composio tools in
216+
// its own chat either, so the refusal no longer points there.
217+
for (const name of [...COMPOSIO]) assert.equal(guard(name, 'room-writer'), 'The composio connector is not available in a group room (#1237).', name)
216218
})
217219

218220
test('#1237: a Bot\'s own chat keeps composio and the schedule tools', async (t) => {
@@ -277,7 +279,8 @@ test('#601: a group round of any origin keeps the github tools, also with a frie
277279
begin('room-writer')
278280
await tick()
279281
for (const name of GITHUB) assert.equal(guard(name, 'room-writer'), undefined, `${origin}: ${name}`)
280-
for (const name of [...COMPOSIO, ...SCHEDULE]) assert.match(guard(name, 'room-writer'), /own chat, not in a group room/, `${origin}: ${name}`)
282+
for (const name of [...SCHEDULE]) assert.match(guard(name, 'room-writer'), /own chat, not in a group room/, `${origin}: ${name}`)
283+
for (const name of [...COMPOSIO]) assert.match(guard(name, 'room-writer'), /not available in a group room/, `${origin}: ${name}`)
281284
assert.deepEqual([...allHidden('room-writer')].sort(), [...COMPOSIO, ...SCHEDULE].sort(), `${origin}: the mask is the same in every origin`)
282285
end('room-writer')
283286
}

0 commit comments

Comments
 (0)