Skip to content

fix(host): refuse the team tools to a Workflow run Bot - #1466

Merged
Birfy merged 1 commit into
mainfrom
dev/1445-run-bot-team-tools
Oct 27, 2026
Merged

Birfy merged 1 commit into
mainfrom
dev/1445-run-bot-team-tools

Conversation

@Birfy

@Birfy Birfy commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #1445

Written-by: Dev
Owner: Chief
Reviewer: Reviewer

Base: 8f1ac121aef7744fb1a2d45b24572f25c3b0e06a (main)
Tip SHA: ecd044e8f36e8767233289e96e47608c8bd4fc30
Tree: 07cadf664310b291c68d10a8ab851d16a480f777

What changed

A Workflow node's tools decided only connectors and the shell. The call guard returned undefined for every other name (connector-access.js:101-103) and the approval seam answered allowed-once (approvals.js:24-30), so a run Bot created with tools: [] still reached message_bot, stop_bot, the Bot/group/capability writes, request_secret, the schedule writes, recall, read_browser and the social/workflow tools.

  • One exported constant EPHEMERAL_DENIED_TOOLS in core/src/host/tools.js names the 29 refused tools; EPHEMERAL_ALLOWED_TOOLS is the documented keep list (read-only tools plus remember/forget, which stay inside the run Bot's own scope).
  • ctx.tools.guard refuses each of the 29 with one sentence, A running Bot is not a team member., before dispatch.
  • ephemeralToolVerdict answers rejected for the same names, so the approval seam cannot disagree; a subagent child of a run Bot is adopted through rt.whoseAgent and gets the same answer, while an ordinary Bot's child still falls through to DSH.
  • The per-Agent mask (hidePluginTools) drops the same names from the run Bot's tool list through the existing restrict({ deny }) seam; a name the profile serves but the mask cannot enforce stays listed with one warn line, and the guard is the refusal.
  • core/test/run-bot-tools.test.js holds the issue repro (nothing delivered, no question card, no secret request, state.json byte-identical), the table over the constant (guard refusal + rejected + no side effect), the subset/allowlist measurement against the served names (core measured, plugin names read from source so a rename fails), and the mask evidence (run Bot carries none of the names, a team Bot keeps message_bot; an unmaskable name stays listed with a warning).
  • Docs: docs/subsystems/core-host.md (run Bot section) and docs/subsystems/plugin-runtime.md (the mask). plugins/workflow/** and docs/subsystems/workflow.md are untouched; fix(host): a deleted Bot's Sessions still pass the team tools, so a call in flight runs with the owner's licence #1453's tombstone and the shell/secrets question stay out of scope.

Verification at the tip

Ran:

  • node --import ./test/harness-resolve.mjs --test --test-concurrency=8 "test/*.test.js" (in core/) — 1831 tests, 1829 pass, 2 fail only for tools this machine lacks: check-arch-docs needs git, and the usage fixture shells out to python3; both run in CI.
  • node --import ./test/harness-resolve.mjs --test test/run-bot-tools.test.js — 6/6 pass at this tip. Red first: with connector-access.js/approvals.js/hooks.js back at the base blobs, 5 of the 6 fail; with tools.js back too the file fails to load (missing export).
  • node scripts/ds-bot-patches.mjs check — 10 patches apply cleanly; core/ equals ds-bot/ + patches/ds-bot/ (the pin is skipped here: no git).
  • node .github/scripts/changelog.mjs check — 25 fragments well formed.
  • node scripts/lint.mjs (in core/) — oxlint 62 warnings, 0 errors; arch-docs cannot run without git here.
  • CI at this tip: tests-ok green (Node 24 · DSH 0.2.0-rc.2, plugins (social), plugins (tree), plugins (timeline), plugins (workflow), package all green; enqueue-guard red only for the expected no Verifier report + missing line: Reviewer:).

Not run locally: npm run verify end to end (the plugin, interop and live-e2e suites need the plugin install and network this box does not have).

Risk

T3, computed from the diff: core/src/host/approvals.js is a T3 path in .github/agent/risk-rules.json; tools.js/connector-access.js/hooks.js are T2 and the docs T0. The change only narrows a run Bot; ordinary Bots, friend's Bots and group rooms keep the same tools. The shell's reach over $DSH_SECRET_* and the deleted-record window are deliberately out of scope (#1453 owns the tombstone).

Checkpoint

{
  "pr": 1466,
  "claim": "Chief",
  "issue": 1445,
  "summary": "A run Bot is no longer a team member: the 29 tools whose effect reaches past its own Session are refused whatever the node's tools list says. One exported constant in tools.js names them; connector-access.js refuses each call with one sentence, approvals.js answers rejected for the same names including a subagent child, and the per-Agent mask removes them from the run Bot's list, warning about any name it cannot mask. The documented allowlist is the rest of the served team tools, and ordinary Bots, friend's Bots and group rooms are unchanged with plugins/workflow untouched.",
  "files": ["core/src/host/tools.js", "core/src/host/connector-access.js", "core/src/host/approvals.js", "core/src/host/hooks.js", "core/test/run-bot-tools.test.js", "docs/subsystems/core-host.md", "docs/subsystems/plugin-runtime.md", "changelog.d/1445-run-bot-team-tools.security.md", "patches/ds-bot/0001-core-fixes.patch", "patches/ds-bot/0002-flybot-api-host.patch", "patches/ds-bot/files/src/host/connector-access.js", "patches/ds-bot/files/src/host/hooks.js"],
  "checks": { "ran": "node --test core/test/*.test.js (1831 tests: 1829 pass, 2 env-only); ds-bot-patches check; changelog check; lint", "result": "green", "at": "ecd044e8f36e8767233289e96e47608c8bd4fc30" },
  "next": "none — ready for review"
}

@Birfy Birfy added the agent-pr Pull request opened by an agent label Oct 27, 2026
@Birfy Birfy self-assigned this Oct 27, 2026
@Birfy
Birfy force-pushed the dev/1445-run-bot-team-tools branch from 69b4b33 to be6692e Compare October 27, 2026 15:24
A run Bot is built from a node's listing, but only connectors and the shell
read that listing: `ctx.tools.guard` returned undefined for every other name and
`ephemeralToolVerdict` answered allowed-once, so a node with tools: [] still
reached message_bot, the Bot/group/capability writes, request_secret, the
schedule writes, recall and the social/workflow tools.

One exported constant in `tools.js` now names the 29 refused tools. The guard
refuses each call with one sentence, the approval seam answers rejected for the
same names (also for a subagent child, by following the parent chain through
`rt.whoseAgent`), and the per-Agent mask drops them from the run Bot's list; a
name the mask cannot enforce stays listed with a warn line, the guard is the
refusal. The documented allowlist is the rest of the served team tools. A table
over the constant proves the guard, the seam and the byte-identical state.

Fixes #1445
@Birfy
Birfy force-pushed the dev/1445-run-bot-team-tools branch from be6692e to ecd044e Compare October 27, 2026 15:25
@Birfy

Birfy commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator Author

Verdict: PASS at ecd044e
Written-by: Reviewer
Attestation: ecd044e role=Reviewer

Ran at this tip (2026-10-27T15:50Z):

$ node --import ./test/harness-resolve.mjs --test test/run-bot-tools.test.js   (core/)
1..6  # pass 6  # fail 0
$ # red first: the same test with connector-access.js, approvals.js and hooks.js at the base blobs
$ # (a046dab8 / 76f43ee5 / 06737ae8); tools.js and the test kept
1..6  # pass 1  # fail 5
$ node --import ./test/harness-resolve.mjs --test --test-concurrency=8 "test/*.test.js"   (core/)
# tests 1831  # pass 1829  # fail 2 — check-arch-docs (no git) + usage.sqlite fixture (no python3)
$ node scripts/ds-bot-patches.mjs check
skipped pin (no git); ok 10 patches apply cleanly; core/ equals ds-bot/ + patches/ds-bot/
$ node .github/scripts/changelog.mjs check
changelog: 26 fragment(s) in changelog.d/ are well formed
$ node scripts/lint.mjs   (core/)
62 warnings, 0 errors; arch-docs cannot diff without origin/main (no git here)

审查要点:

  • 29 个跨模块工具名收在一个导出常量;guard 在派发前用同一句话拒绝,approval seam 对同名答 rejected(run Bot 的 subagent 子会话沿 rt.whoseAgent 认到父级),mask 再把同名从工具列表里摘掉 —— 三条缝不会各说各话。
  • 节点的 tools 列表只再决定 connector 和 shell:tools: [] 的 run Bot 够不到 message_bot / request_secret / ask_user 等,repro 断言 state.json 前后逐字节相同;把三处源码退回 base blob、同一 tip 上 5/6 测试变红。
  • 白名单 = 服务名减 denied:core 用注册表实测、plugin 名从源码读,改名或新增工具会让测试失败而不是 fail open。
  • mask 走既有 restrict({ deny });掩不掉的名字留在列表里并有一条 warn,guard 仍是拒绝点 —— 与 issue 约定的 fixture-level 声明一致,没有吹成实测生产工具表。
  • 文档(core-host.md、plugin-runtime.md)和 changelog 跟上了;plugins/workflow/** 与 docs/subsystems/workflow.md 未动,范围与 issue 一致。
  • Not run: npm run verify 的插件 / interop / live-e2e 段(本机装不了插件、无外网);tip 上 CI tests-ok 全绿。

@github-actions github-actions Bot added the pass label Oct 27, 2026
@Birfy
Birfy added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit c722653 Oct 27, 2026
15 of 16 checks passed
@Birfy
Birfy deleted the dev/1445-run-bot-team-tools branch October 27, 2026 16:13
@github-actions github-actions Bot removed the agent-pr Pull request opened by an agent label Oct 27, 2026
cursor Bot pushed a commit that referenced this pull request Oct 28, 2026
…mentation only

Owner requirement (final): EVERY Workflow node Bot must get the same full
permissions and connectors a normal sidebar Bot gets, INCLUDING when the
node has an explicit tools list. node.tools should NOT strip connectors.

Final correct implementation:
- tools field is ALWAYS omitted from fb.bot.create() for node Bots
- Every node Bot inherits all registered tools (GitHub, Composio, file/shell, team tools)
- Guard layer (EPHEMERAL_DENIED_TOOLS #1466) restricts only 29 team-admin tools
- node.tools is stored in fields.tools as documentation but does not restrict
- All node Bots get danger-full-access permission
- No tool-specific prompt notes are added

This ensures:
✓ Triage, Review, Escalate, Enqueue all get full GitHub/Composio access
✓ LIVE Review node with tools: [bash, read, write, edit] still gets GitHub/Composio
✓ Node tools lists are preserved as documentation without restricting grant
✓ Matches owner ask: 所有的bot要能拿到正常bot的所有权限,包括github、composio等等

Previous attempts:
1. Baseline ['read', 'edit', 'bash', 'github'] - excluded Composio
2. Inherit when empty, restrict when explicit - still stripped connectors

Current: Always inherit, never restrict. Simple and correct.
Co-authored-by: Danyang Chen <birfychen@gmail.com>
Birfy added a commit to HugoXK/FlyBot that referenced this pull request Oct 28, 2026
…owLab#1512)

<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
Owner: Dev
Reviewer: Reviewer

EVERY Workflow node Bot gets the same full permissions a normal sidebar
Bot gets (GitHub, Composio, shell, …). Spawn always omits `tools` from
`fb.bot.create`; `node.tools` is documentation only. Limited only by
EPHEMERAL_DENIED_TOOLS (ThinkFlowLab#1466).

## Checkpoint
<!-- flybot:checkpoint -->
```json
{
  "pr": 1512,
  "claim": "Dev",
  "issue": 1516,
  "summary": "Workflow ephemeral node Bots no longer spawn with a stripped or empty tools list. Create always omits the tools field so every node Bot inherits the full normal sidebar Bot grant, including GitHub and Composio. node.tools stays as documentation only and does not restrict connectors. All node Bots get danger-full-access; team-admin tools remain denied by EPHEMERAL_DENIED_TOOLS. This stops Escalate and partial-list nodes from burning minutes on failed tool calls.",
  "files": ["plugins/workflow/src/ephemeral.js", "plugins/workflow/index.js", "plugins/workflow/test/ephemeral.test.js", "docs/subsystems/workflow.md", "changelog.d/workflow-node-bot-baseline-tools.fixed.md"],
  "checks": { "ran": "npm --prefix plugins/workflow test; plugins lint; CI tip green", "result": "green", "at": "b9ed05bfa46f3d9b584cb9ff5e509bcbb23d689e" },
  "next": "none — ready for review"
}
```
<!-- /flybot:checkpoint -->

Fixes ThinkFlowLab#1516

Docs-not-needed: no — docs/subsystems/workflow.md updated in this PR
<!-- CURSOR_AGENT_PR_BODY_END -->

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Birfy pushed a commit to HugoXK/FlyBot that referenced this pull request Oct 28, 2026
A failed release leaves the run live with only the failed id named, but the
node after it has no Bot: the next step reply moved the graph there and
handOff() skipped it silently, so the run held a concurrency slot forever.

Mark that run paused and releasePending when the failure is written, push a
system line that names the retry, and refuse resume() while the marker is
set. A whole-workflow Reset freezes every run it did not reset, because each
one has lost the ids whose release did go. The markers go when the run ends
(Stop again, dispose, reap, or completion).

Refs ThinkFlowLab#1476

Rebased onto main 6202359 by Rework:
kept every change of the reviewed tip 8d89980 (5 files) and resolved the
conflicts with the newer main work on the same files (ThinkFlowLab#1424 endRun,
ThinkFlowLab#1466 full tool grant, ThinkFlowLab#1525 host cap removal, ThinkFlowLab#1537/ThinkFlowLab#1538 early end).
Written-by: Rework
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(host): a Workflow run Bot keeps the team tools, whatever the node's tools list says

1 participant