mixdog 1.3.5 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (341) hide show
  1. package/NOTICE.md +2 -2
  2. package/README.md +2 -2
  3. package/package.json +2 -11
  4. package/scripts/daemon-stub.mjs +1 -1
  5. package/scripts/gate-local.mjs +4 -2
  6. package/scripts/native-release-status.mjs +2 -2
  7. package/scripts/recall-history-cases.json +1 -1
  8. package/scripts/suite-health.mjs +10 -1
  9. package/scripts/test.mjs +7 -11
  10. package/src/agents/reviewer/AGENT.md +7 -11
  11. package/src/agents/reviewer/agent.json +1 -1
  12. package/src/defaults/shipped-history.json +17 -1
  13. package/src/defaults/skills/browser-use/SKILL.md +59 -11
  14. package/src/defaults/skills/code-tidy/SKILL.md +142 -270
  15. package/src/defaults/skills/code-tidy/references/agent-cleanup.md +4 -4
  16. package/src/defaults/skills/code-tidy/references/cleanup-report.md +31 -76
  17. package/src/defaults/skills/code-tidy/references/dead-code.md +6 -12
  18. package/src/defaults/skills/computer-use/SKILL.md +33 -9
  19. package/src/defaults/skills/setup/SKILL.md +22 -0
  20. package/src/defaults/skills/setup/references/actions.md +39 -5
  21. package/src/defaults/skills/setup/references/surfaces.md +11 -2
  22. package/src/defaults/skills/visualize/SKILL.md +2 -1
  23. package/src/lib/rules-builder.cjs +10 -8
  24. package/src/rules/agent/AGENT.md +6 -6
  25. package/src/rules/lead/LEAD.md +18 -20
  26. package/src/rules/shared/05-tool-calls.md +5 -6
  27. package/src/rules/shared/10-tool-workflow.md +2 -0
  28. package/src/rules/shared/60-verification.md +7 -5
  29. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -3
  30. package/src/runtime/agent/orchestrator/agent-runtime/title-completion.mjs +3 -1
  31. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +3 -1
  32. package/src/runtime/agent/orchestrator/config.mjs +17 -1
  33. package/src/runtime/agent/orchestrator/context/collect.mjs +3 -0
  34. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +72 -67
  35. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +54 -1
  36. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +3 -3
  37. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +176 -72
  38. package/src/runtime/agent/orchestrator/mcp/client.mjs +19 -0
  39. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +181 -133
  40. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +42 -6
  41. package/src/runtime/agent/orchestrator/providers/anthropic-cache-warm.mjs +165 -0
  42. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  43. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +4 -1
  44. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +14 -14
  45. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +145 -66
  46. package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +25 -3
  47. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +64 -33
  48. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +135 -87
  49. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +123 -102
  50. package/src/runtime/agent/orchestrator/providers/cache-warm-admission.mjs +90 -0
  51. package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +25 -9
  52. package/src/runtime/agent/orchestrator/providers/cursor.mjs +22 -13
  53. package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +17 -3
  54. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +52 -10
  55. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +8 -1
  56. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
  57. package/src/runtime/agent/orchestrator/providers/npm-cli-version.mjs +11 -3
  58. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +2 -1
  59. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -9
  60. package/src/runtime/agent/orchestrator/providers/openai-ws-send/send-context.mjs +22 -3
  61. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +43 -5
  62. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/handshake-failure.mjs +3 -1
  63. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/stream-failure.mjs +6 -3
  64. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +65 -1
  65. package/src/runtime/agent/orchestrator/providers/quota-reading.mjs +38 -12
  66. package/src/runtime/agent/orchestrator/providers/registry.mjs +26 -0
  67. package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +39 -7
  68. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +6 -0
  69. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +12 -7
  70. package/src/runtime/agent/orchestrator/runtime-core/compaction-config-keys.mjs +62 -0
  71. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +37 -16
  72. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-delta.mjs +100 -23
  73. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +2 -0
  74. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +19 -4
  75. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +125 -30
  76. package/src/runtime/agent/orchestrator/session/cache-warm.mjs +792 -0
  77. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +133 -22
  78. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +475 -58
  79. package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +42 -17
  80. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +4 -1
  81. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +11 -5
  82. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +0 -2
  83. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -4
  84. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +1 -1
  85. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +18 -22
  86. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +32 -3
  87. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +3 -1
  88. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +1 -1
  89. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +17 -3
  90. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +7 -2
  91. package/src/runtime/agent/orchestrator/session/loop/tool-exec/after-hook.mjs +2 -1
  92. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +3 -2
  93. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +16 -2
  94. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +16 -1
  95. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +10 -0
  96. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -10
  97. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +12 -3
  98. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -2
  99. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +19 -1
  100. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +133 -90
  101. package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +31 -4
  102. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +30 -18
  103. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +23 -13
  104. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +11 -2
  105. package/src/runtime/agent/orchestrator/session/manager/provider-replay.mjs +13 -0
  106. package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +129 -0
  107. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +20 -2
  108. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +2 -0
  109. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +8 -0
  110. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +1 -1
  111. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +7 -2
  112. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +102 -15
  113. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +18 -5
  114. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +19 -8
  115. package/src/runtime/agent/orchestrator/session/prompt-cache-idle-gaps.mjs +113 -0
  116. package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +127 -0
  117. package/src/runtime/agent/orchestrator/session/routine-result.mjs +36 -9
  118. package/src/runtime/agent/orchestrator/session/sensitive-projection.mjs +100 -0
  119. package/src/runtime/agent/orchestrator/session/sensitive-values.mjs +257 -0
  120. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +20 -7
  121. package/src/runtime/agent/orchestrator/session/side-question.mjs +18 -0
  122. package/src/runtime/agent/orchestrator/session/skill-update-notice.mjs +60 -0
  123. package/src/runtime/agent/orchestrator/session/store/live-state.mjs +6 -3
  124. package/src/runtime/agent/orchestrator/session/store/save-delta.mjs +9 -5
  125. package/src/runtime/agent/orchestrator/session/store/serialize.mjs +9 -8
  126. package/src/runtime/agent/orchestrator/session/store/session-persistence-events.mjs +29 -0
  127. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +8 -6
  128. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +12 -2
  129. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +44 -14
  130. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +84 -3
  131. package/src/runtime/agent/orchestrator/session/stored-attachment-history.mjs +32 -0
  132. package/src/runtime/agent/orchestrator/session/thinking-binding.mjs +108 -0
  133. package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +2 -2
  134. package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +2 -1
  135. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +18 -1
  136. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-status.mjs +9 -7
  137. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-text.mjs +2 -2
  138. package/src/runtime/agent/orchestrator/session/turn-failure.mjs +5 -1
  139. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +5 -2
  140. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +12 -1
  141. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/misuse-guard.mjs +240 -0
  142. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +1 -1
  143. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/shell-cwd.mjs +38 -0
  144. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +25 -5
  145. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +65 -7
  146. package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +7 -1
  147. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +36 -13
  148. package/src/runtime/browser-bridge/action-schema.mjs +13 -2
  149. package/src/runtime/browser-bridge/client.mjs +73 -8
  150. package/src/runtime/browser-bridge/input-fields.mjs +8 -9
  151. package/src/runtime/browser-bridge/mail-links.mjs +107 -0
  152. package/src/runtime/browser-bridge/tool-defs.mjs +1 -1
  153. package/src/runtime/channels/lib/config.mjs +6 -61
  154. package/src/runtime/channels/lib/crash-log.mjs +1 -1
  155. package/src/runtime/channels/lib/event-pipeline.mjs +4 -19
  156. package/src/runtime/channels/lib/event-queue.mjs +3 -18
  157. package/src/runtime/channels/lib/owned-runtime/automation-services.mjs +2 -2
  158. package/src/runtime/channels/lib/owned-runtime/automation-start.mjs +3 -4
  159. package/src/runtime/channels/lib/owned-runtime/config-reload.mjs +4 -59
  160. package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +33 -115
  161. package/src/runtime/channels/lib/owned-runtime/owned-stop.mjs +5 -22
  162. package/src/runtime/channels/lib/owned-runtime/state.mjs +2 -8
  163. package/src/runtime/channels/lib/owned-runtime.mjs +6 -23
  164. package/src/runtime/channels/lib/parent-bridge.mjs +2 -2
  165. package/src/runtime/channels/lib/runtime-paths.mjs +4 -43
  166. package/src/runtime/channels/lib/scheduler.mjs +33 -147
  167. package/src/runtime/channels/lib/schedules-db.mjs +34 -11
  168. package/src/runtime/channels/lib/status-snapshot.mjs +1 -1
  169. package/src/runtime/channels/lib/tool-dispatch.mjs +1 -2
  170. package/src/runtime/channels/lib/webhooks-db.mjs +5 -9
  171. package/src/runtime/channels/lib/worker-bootstrap.mjs +0 -1
  172. package/src/runtime/channels/lib/worker-ipc.mjs +0 -1
  173. package/src/runtime/channels/lib/worker-main.mjs +10 -34
  174. package/src/runtime/computer-bridge/action-schema.mjs +42 -1
  175. package/src/runtime/computer-bridge/actions.mjs +9 -1
  176. package/src/runtime/computer-bridge/client.mjs +180 -8
  177. package/src/runtime/computer-bridge/core-actions.mjs +2 -2
  178. package/src/runtime/computer-bridge/error-recovery.mjs +27 -2
  179. package/src/runtime/computer-bridge/linux-readiness.mjs +65 -23
  180. package/src/runtime/computer-bridge/pending-continuation.mjs +1 -1
  181. package/src/runtime/connectors/connector-accounts-mcp.mjs +230 -0
  182. package/src/runtime/connectors/gmail-app-password.mjs +172 -39
  183. package/src/runtime/connectors/google-mcp.mjs +10 -12
  184. package/src/runtime/memory/lib/pg/adapter.mjs +77 -70
  185. package/src/runtime/memory/lib/pg/supervisor.mjs +24 -12
  186. package/src/runtime/memory/lib/runtime-fetcher.mjs +23 -5
  187. package/src/runtime/session-search/session-search-db.mjs +73 -9
  188. package/src/runtime/session-search/session-search-ingest.mjs +110 -20
  189. package/src/runtime/shared/abort-race.mjs +89 -0
  190. package/src/runtime/shared/agent-avatar.d.mts +0 -1
  191. package/src/runtime/shared/agent-kind.mjs +4 -3
  192. package/src/runtime/shared/bounded-download.mjs +8 -3
  193. package/src/runtime/shared/config.mjs +2 -13
  194. package/src/runtime/shared/connector-accounts.mjs +20 -10
  195. package/src/runtime/shared/connector-catalog.d.mts +29 -0
  196. package/src/runtime/shared/connector-catalog.mjs +150 -0
  197. package/src/runtime/shared/connector-triggers.mjs +22 -19
  198. package/src/runtime/shared/defaults-separation.mjs +2 -14
  199. package/src/runtime/shared/delegated-approval-host.mjs +103 -11
  200. package/src/runtime/shared/err-text.mjs +24 -15
  201. package/src/runtime/shared/file-permissions.d.mts +0 -8
  202. package/src/runtime/shared/file-permissions.mjs +0 -11
  203. package/src/runtime/shared/git-direct.test-support.mjs +19 -0
  204. package/src/runtime/shared/heartbeat-interval.d.mts +4 -0
  205. package/src/runtime/shared/heartbeat-interval.mjs +19 -0
  206. package/src/runtime/shared/idle-auto-clear-gate.mjs +104 -0
  207. package/src/runtime/shared/llm/usage-rollup.mjs +1 -1
  208. package/src/runtime/shared/mixy-presence.mjs +22 -0
  209. package/src/runtime/shared/orchestration.d.mts +2 -0
  210. package/src/runtime/shared/pg-schema-db.mjs +27 -16
  211. package/src/runtime/shared/profile-config.cjs +1 -1
  212. package/src/runtime/shared/prompt-origin.d.mts +4 -0
  213. package/src/runtime/shared/prompt-origin.mjs +21 -0
  214. package/src/runtime/shared/provider-accounts.mjs +7 -1
  215. package/src/runtime/shared/provider-display-name.mjs +29 -0
  216. package/src/runtime/shared/provider-timeout-phase.mjs +2 -0
  217. package/src/runtime/shared/routine-trigger.d.mts +2 -0
  218. package/src/runtime/shared/routine-trigger.mjs +10 -0
  219. package/src/runtime/shared/sensitive-path-rules.d.mts +2 -10
  220. package/src/runtime/shared/sensitive-path-rules.mjs +19 -41
  221. package/src/runtime/shared/session-activity-signal.mjs +34 -1
  222. package/src/runtime/shared/session-sidebar-host.d.mts +1 -1
  223. package/src/runtime/shared/session-sidebar-host.mjs +7 -3
  224. package/src/runtime/shared/session-turn-prelude.mjs +18 -0
  225. package/src/runtime/shared/tool-result-summary.mjs +1 -14
  226. package/src/runtime/shared/worktrees/test-support.mjs +9 -4
  227. package/src/runtime/terminal-bridge/client.mjs +10 -1
  228. package/src/runtime/visualize-bridge/client.mjs +27 -8
  229. package/src/runtime/visualize-bridge/tool-defs.mjs +22 -8
  230. package/src/runtime/visualize-bridge/tool-meta.d.mts +5 -0
  231. package/src/runtime/visualize-bridge/tool-meta.mjs +22 -0
  232. package/src/runtime/web-search/lib/fetch-pipeline.mjs +2 -2
  233. package/src/session-runtime/agent-home-delivery.mjs +448 -185
  234. package/src/session-runtime/agent-task-sessions.mjs +329 -180
  235. package/src/session-runtime/boot/apis.mjs +9 -0
  236. package/src/session-runtime/boot/infrastructure.mjs +4 -0
  237. package/src/session-runtime/boot/providers.mjs +3 -0
  238. package/src/session-runtime/boot/tools.mjs +6 -1
  239. package/src/session-runtime/config-lifecycle.mjs +6 -0
  240. package/src/session-runtime/connector-merge-migration.mjs +305 -0
  241. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +132 -28
  242. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +2 -0
  243. package/src/session-runtime/lifecycle/inheritance.mjs +16 -3
  244. package/src/session-runtime/lifecycle/session-catalog.mjs +3 -1
  245. package/src/session-runtime/lifecycle/teardown/runtime-stops.mjs +50 -4
  246. package/src/session-runtime/lifecycle/teardown.mjs +23 -5
  247. package/src/session-runtime/mcp-glue/server-catalog.mjs +55 -3
  248. package/src/session-runtime/mcp-oauth-login.mjs +5 -3
  249. package/src/session-runtime/mixy-heartbeat.mjs +153 -33
  250. package/src/session-runtime/notification-bus.mjs +31 -117
  251. package/src/session-runtime/notification-completion-queue.mjs +54 -0
  252. package/src/session-runtime/notification-listeners.mjs +76 -0
  253. package/src/session-runtime/owned-automation-run.mjs +2 -1
  254. package/src/session-runtime/prewarm/channel-start.mjs +3 -7
  255. package/src/session-runtime/resource-mcp-api.mjs +143 -224
  256. package/src/session-runtime/resource-mcp-connector-accounts.mjs +163 -0
  257. package/src/session-runtime/resource-mcp-oauth-api.mjs +217 -0
  258. package/src/session-runtime/resource-mcp-secrets.mjs +82 -0
  259. package/src/session-runtime/resource-mcp-servers.mjs +78 -0
  260. package/src/session-runtime/resource-mcp-store.mjs +49 -0
  261. package/src/session-runtime/routine-owner.mjs +40 -9
  262. package/src/session-runtime/services/agent-tool/close-flow.mjs +3 -0
  263. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +7 -2
  264. package/src/session-runtime/services/agent-tool/tag-registry.mjs +1 -0
  265. package/src/session-runtime/services/agent-tool/worker-index/awaiting-user-sync.mjs +51 -0
  266. package/src/session-runtime/services/agent-tool/worker-index/disposal.mjs +55 -0
  267. package/src/session-runtime/services/agent-tool/worker-index/released-store.mjs +123 -0
  268. package/src/session-runtime/services/agent-tool/worker-index/restart-settlement.mjs +112 -0
  269. package/src/session-runtime/services/agent-tool/worker-index/row-reads.mjs +36 -0
  270. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -0
  271. package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +17 -4
  272. package/src/session-runtime/services/agent-tool/worker-index/row-writes.mjs +58 -0
  273. package/src/session-runtime/services/agent-tool/worker-index.mjs +41 -241
  274. package/src/session-runtime/services/agent-tool/worker-rows.mjs +3 -0
  275. package/src/session-runtime/services/channel-admin.mjs +7 -13
  276. package/src/session-runtime/services/channel-session-router.mjs +4 -2
  277. package/src/session-runtime/services/seeds.mjs +0 -28
  278. package/src/session-runtime/session-compaction-sync.mjs +27 -0
  279. package/src/session-runtime/session-create/session-options.mjs +1 -0
  280. package/src/session-runtime/settings-builtin-tools-api.mjs +11 -0
  281. package/src/session-runtime/settings-compaction-api.mjs +2 -40
  282. package/src/session-runtime/settings-system-api.mjs +13 -1
  283. package/src/session-runtime/setup-tool/agent-settings-actions.mjs +65 -0
  284. package/src/session-runtime/setup-tool/executor.mjs +145 -22
  285. package/src/session-runtime/setup-tool/extended-actions.mjs +8 -3
  286. package/src/session-runtime/setup-tool/session-route-actions.mjs +152 -0
  287. package/src/session-runtime/setup-tool/settings-contract.mjs +4 -3
  288. package/src/session-runtime/setup-tool/tool-defs.mjs +64 -5
  289. package/src/session-runtime/tool-policy/agent-profile-fold.mjs +17 -13
  290. package/src/session-runtime/tool-policy/policy-stage.mjs +16 -2
  291. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
  292. package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +4 -0
  293. package/src/standalone/channel-transport/client-registry/client-records.mjs +65 -0
  294. package/src/standalone/channel-transport/client-registry/client-registration.mjs +118 -0
  295. package/src/standalone/channel-transport/client-registry.mjs +24 -163
  296. package/src/standalone/channel-transport.mjs +1 -1
  297. package/src/standalone/daemon-channels-loader.mjs +10 -8
  298. package/src/standalone/daemon-session-control/list-page.mjs +72 -0
  299. package/src/standalone/daemon-session-control/request.mjs +51 -0
  300. package/src/standalone/daemon-session-control/room-host.mjs +57 -0
  301. package/src/standalone/daemon-session-control/route-control.mjs +48 -0
  302. package/src/standalone/daemon-session-control/send-turn.mjs +60 -0
  303. package/src/standalone/daemon-session-control/worktree-control.mjs +40 -0
  304. package/src/standalone/daemon-session-control.mjs +124 -188
  305. package/src/standalone/daemon.mjs +26 -3
  306. package/src/standalone/session-client.mjs +7 -3
  307. package/src/standalone/session-protocol.mjs +5 -0
  308. package/src/standalone/session-service/agent-tree/agent-child.mjs +1 -0
  309. package/src/standalone/session-service/quota-resume.mjs +3 -0
  310. package/src/standalone/session-service/retention/busy.mjs +65 -1
  311. package/src/standalone/session-service/session-calls/session-action.mjs +23 -2
  312. package/src/standalone/session-service/session-calls/session-views.mjs +8 -0
  313. package/src/standalone/session-tool-output.mjs +15 -7
  314. package/src/tui/app/model-options.mjs +3 -28
  315. package/src/tui/dist/index.mjs +58 -64
  316. package/src/tui/session/agent-job-feed/model-notification.mjs +3 -0
  317. package/src/tui/session/initial-state.mjs +3 -0
  318. package/src/tui/session/notice-surface.mjs +7 -1
  319. package/src/tui/session/queue-helpers.mjs +3 -42
  320. package/src/tui/session/session-api/routes-agent-definitions.mjs +14 -2
  321. package/src/tui/session/session-api/routes-write.mjs +5 -1
  322. package/src/tui/session/session-api/routes.mjs +1 -0
  323. package/src/tui/session/session-api/settings.mjs +17 -1
  324. package/src/tui/session/session-flow/auto-clear.mjs +4 -33
  325. package/src/tui/session/session-flow/drain-closed-queue.mjs +38 -0
  326. package/src/tui/session/session-flow/drain-enqueue.mjs +86 -0
  327. package/src/tui/session/session-flow/drain-retry.mjs +35 -0
  328. package/src/tui/session/session-flow/drain.mjs +118 -149
  329. package/src/tui/session/session-flow/queue/entry-shape.mjs +5 -0
  330. package/src/tui/session/session-flow/queue/take-entries.mjs +5 -2
  331. package/src/tui/session/tool-approval-delegation.mjs +64 -0
  332. package/src/tui/session/tool-approval-request.mjs +39 -0
  333. package/src/tui/session/tool-approval-settle.mjs +62 -0
  334. package/src/tui/session/tool-approval.mjs +69 -115
  335. package/src/tui/session/tui-steering-persist.mjs +8 -1
  336. package/src/tui/session/turn-stream.mjs +19 -2
  337. package/src/tui/session/turn.mjs +1 -0
  338. package/src/vendor/statusline/bin/statusline-route.mjs +1 -219
  339. package/src/vendor/statusline/src/gateway/route-meta.mjs +35 -13
  340. package/scripts/code-graph-description-contract.mjs +0 -188
  341. package/src/runtime/channels/lib/owned-runtime/ownership-refresh.mjs +0 -36
@@ -1,4 +1,6 @@
1
1
  import { clean } from './session-text.mjs';
2
+ import { parseMcpToolName } from '../../../shared/tool-primitives.mjs';
3
+ import { mcpServerLines } from '../context/deferred-tools.mjs';
2
4
 
3
5
  function compactDescription(value) {
4
6
  return String(value || '')
@@ -8,23 +10,36 @@ function compactDescription(value) {
8
10
  .slice(0, 240);
9
11
  }
10
12
 
13
+ function addedEntry(entry) {
14
+ const name = clean(entry?.name);
15
+ return name ? { name, description: compactDescription(entry?.description) } : null;
16
+ }
17
+
11
18
  function normalizedDelta(value) {
12
19
  const source = value && typeof value === 'object' ? value : {};
13
20
  const addedByName = new Map();
14
21
  for (const entry of Array.isArray(source.added) ? source.added : []) {
15
- const name = clean(entry?.name);
16
- if (name) addedByName.set(name, { name, description: compactDescription(entry?.description) });
22
+ const added = addedEntry(entry);
23
+ if (added) addedByName.set(added.name, added);
17
24
  }
18
- const removed = new Set(
19
- (Array.isArray(source.removed) ? source.removed : [])
20
- .map((entry) => clean(typeof entry === 'string' ? entry : entry?.name))
21
- .filter(Boolean)
25
+ const removed = new Set();
26
+ // Removed tool name → its server, for removals that came with one.
27
+ const removedServers = new Map(
28
+ Object.entries(source.removedServers && typeof source.removedServers === 'object' ? source.removedServers : {})
22
29
  );
30
+ for (const entry of Array.isArray(source.removed) ? source.removed : []) {
31
+ const name = clean(typeof entry === 'string' ? entry : entry?.name);
32
+ if (!name) continue;
33
+ removed.add(name);
34
+ if (typeof entry === 'object' && clean(entry?.server)) removedServers.set(name, clean(entry.server));
35
+ }
23
36
  for (const name of addedByName.keys()) removed.delete(name);
37
+ for (const name of [...removedServers.keys()]) if (!removed.has(name)) removedServers.delete(name);
24
38
  return {
25
39
  revision: Math.max(0, Number(source.revision) || 0),
26
40
  addedByName,
27
41
  removed,
42
+ removedServers,
28
43
  };
29
44
  }
30
45
 
@@ -33,19 +48,20 @@ export function mergePendingDeferredToolDelta(session, change = {}) {
33
48
  const current = normalizedDelta(session.pendingDeferredToolDelta);
34
49
  const nextRevision = Math.max(current.revision, Math.max(0, Number(session.deferredToolDeltaRevision) || 0)) + 1;
35
50
  for (const entry of Array.isArray(change.added) ? change.added : []) {
36
- const name = clean(entry?.name);
37
- if (!name) continue;
38
- if (current.removed.delete(name)) continue;
39
- current.addedByName.set(name, {
40
- name,
41
- description: compactDescription(entry?.description),
42
- });
51
+ const added = addedEntry(entry);
52
+ if (!added) continue;
53
+ if (current.removed.delete(added.name)) {
54
+ current.removedServers.delete(added.name);
55
+ continue;
56
+ }
57
+ current.addedByName.set(added.name, added);
43
58
  }
44
59
  for (const entry of Array.isArray(change.removed) ? change.removed : []) {
45
60
  const name = clean(typeof entry === 'string' ? entry : entry?.name);
46
61
  if (!name) continue;
47
62
  if (current.addedByName.delete(name)) continue;
48
63
  current.removed.add(name);
64
+ if (typeof entry === 'object' && clean(entry?.server)) current.removedServers.set(name, clean(entry.server));
49
65
  }
50
66
  const added = [...current.addedByName.values()].sort((a, b) => a.name.localeCompare(b.name));
51
67
  const removed = [...current.removed].sort();
@@ -61,27 +77,88 @@ export function mergePendingDeferredToolDelta(session, change = {}) {
61
77
  revision: nextRevision,
62
78
  added,
63
79
  removed,
80
+ ...(current.removedServers.size
81
+ ? { removedServers: Object.fromEntries([...current.removedServers].sort(([a], [b]) => a.localeCompare(b))) }
82
+ : {}),
64
83
  };
65
84
  session.updatedAt = Date.now();
66
85
  return session.pendingDeferredToolDelta;
67
86
  }
68
87
 
88
+ // MCP changes render one line per affected server, like the BP2 manifest; the tool-level sets stay exact in the snapshot for acknowledgement.
89
+ // A tool's server comes from the session's catalog definitions (an added
90
+ // tool) or the server recorded when it was removed; only a name neither knows
91
+ // falls back to the recorded/`mcp__<server>__<tool>` parse. Other tools are
92
+ // listed one by one.
93
+ function deltaLines(session, added, removed, removedServers) {
94
+ const catalogServer = new Map();
95
+ for (const key of ['deferredToolCatalog', 'deferredLateToolCatalog', 'tools']) {
96
+ for (const tool of Array.isArray(session?.[key]) ? session[key] : []) {
97
+ const name = clean(tool?.name);
98
+ const server = name && parseMcpToolName(tool)?.server;
99
+ if (server) catalogServer.set(name, server);
100
+ }
101
+ }
102
+ const serverOf = (name, hint) => hint || catalogServer.get(name) || parseMcpToolName(name)?.server || '';
103
+ const items = [];
104
+
105
+ const addedServers = new Map();
106
+ for (const entry of added) {
107
+ const server = serverOf(entry.name);
108
+ if (!server) {
109
+ items.push({
110
+ label: entry.name,
111
+ line: entry.description ? `- ${entry.name}: ${entry.description}` : `- ${entry.name}`,
112
+ });
113
+ } else if (addedServers.has(server)) addedServers.get(server).push(entry);
114
+ else addedServers.set(server, [entry]);
115
+ }
116
+ const addedLines = mcpServerLines(addedServers, session?.mcpServerInstructions);
117
+ for (const line of addedLines) items.push({ label: line.label, line: `- ${line.label}: ${line.trigger}` });
118
+ // A server whose name cannot be listed safely is listed by its tools.
119
+ const listed = new Set(addedLines.map((line) => line.label));
120
+ for (const [server, entries] of addedServers) {
121
+ if (listed.has(`mcp__${server}`)) continue;
122
+ for (const entry of entries) items.push({ label: entry.name, line: `- ${entry.name}` });
123
+ }
124
+
125
+ const connected = Array.isArray(session?.deferredMcpToolNames) ? session.deferredMcpToolNames : null;
126
+ const liveServers = new Set((connected || []).map((name) => serverOf(clean(name))).filter(Boolean));
127
+ const removedByServer = new Map();
128
+ const removedItems = [];
129
+ for (const name of removed) {
130
+ const server = serverOf(name, removedServers.get(name));
131
+ if (!server) {
132
+ removedItems.push({ label: name, line: `- ${name}` });
133
+ continue;
134
+ }
135
+ if (removedByServer.has(server)) removedByServer.get(server).push(name);
136
+ else removedByServer.set(server, [name]);
137
+ }
138
+ // Gone only when the connected set is known and none of its tools is left.
139
+ for (const [server, names] of removedByServer) {
140
+ const label = `mcp__${server}`;
141
+ const gone = connected && !liveServers.has(server);
142
+ const plural = names.length === 1 ? '' : 's';
143
+ removedItems.push({
144
+ label,
145
+ line: gone ? `- ${label}` : `- ${label}: ${names.length} tool${plural} removed`,
146
+ });
147
+ }
148
+
149
+ const render = (list) => list.sort((a, b) => a.label.localeCompare(b.label)).map((item) => item.line);
150
+ return { added: render(items), removed: render(removedItems) };
151
+ }
152
+
69
153
  export function snapshotPendingDeferredToolDelta(session) {
70
154
  const current = normalizedDelta(session?.pendingDeferredToolDelta);
71
155
  const added = [...current.addedByName.values()].sort((a, b) => a.name.localeCompare(b.name));
72
156
  const removed = [...current.removed].sort();
73
157
  if (!added.length && !removed.length) return null;
158
+ const rendered = deltaLines(session, added, removed, current.removedServers);
74
159
  const lines = ['<system-reminder>', '<deferred_tools_delta>'];
75
- if (added.length) {
76
- lines.push('Added deferred tools:');
77
- for (const entry of added) {
78
- lines.push(entry.description ? `- ${entry.name}: ${entry.description}` : `- ${entry.name}`);
79
- }
80
- }
81
- if (removed.length) {
82
- lines.push('Removed deferred tools:');
83
- for (const name of removed) lines.push(`- ${name}`);
84
- }
160
+ if (rendered.added.length) lines.push('Added deferred tools:', ...rendered.added);
161
+ if (rendered.removed.length) lines.push('Removed deferred tools:', ...rendered.removed);
85
162
  lines.push('</deferred_tools_delta>', '</system-reminder>');
86
163
  return {
87
164
  revision: current.revision,
@@ -2,6 +2,7 @@
2
2
  import { clean } from '../../../shared/clean.mjs';
3
3
  import { stripInjectedBlocks } from '../../../shared/injected-display-text.mjs';
4
4
  import { CONTROL_MARKER_RE } from '../../../shared/tool-execution-contract.mjs';
5
+ import { isRoutineQuietReply } from '../../../shared/routine-trigger.mjs';
5
6
 
6
7
  export function sessionMessageText(content) {
7
8
  if (content == null) return '';
@@ -69,6 +70,7 @@ export function isSessionPreviewNoise(text) {
69
70
  const value = String(text || '').trim();
70
71
  return (
71
72
  !value ||
73
+ isRoutineQuietReply(value) ||
72
74
  isSyntheticSessionText(value) ||
73
75
  !cleanSessionPreview(value) ||
74
76
  isLateToolAnnouncement(value) ||
@@ -26,7 +26,7 @@ import { providerToolExclusionReason, routeToolExclusionReason } from '../sessio
26
26
 
27
27
  export { isToolBridgeEnabled } from './tool-exposure.mjs';
28
28
 
29
- export const BRIDGE_LOAD_TOOL_NAME = 'load_tool';
29
+ const BRIDGE_LOAD_TOOL_NAME = 'load_tool';
30
30
  export const BRIDGE_CALL_TOOL_NAME = 'call_tool';
31
31
  const DESCRIBE_MAX_NAMES = 8;
32
32
  // Most definitions one request resolves to: every name an alias of the widest
@@ -344,7 +344,9 @@ function largestChunk(text, offset, buildPage) {
344
344
  return low;
345
345
  }
346
346
 
347
- export function describeBridgeTools(session, args = {}, { mode } = {}) {
347
+ // `instructionsFor(names)` (pure) returns the MCP instruction text the page
348
+ // carries for those entries, counted inside the result budget.
349
+ export function describeBridgeTools(session, args = {}, { mode, instructionsFor } = {}) {
348
350
  const syntaxHint = ' Use load_tool {"names":["exact_tool_name"]}; the required key is names, not name.';
349
351
  const invalid = unknownKeysError(args, DESCRIBE_KEYS, BRIDGE_LOAD_TOOL_NAME);
350
352
  if (invalid) {
@@ -409,11 +411,16 @@ export function describeBridgeTools(session, args = {}, { mode } = {}) {
409
411
  }
410
412
  const entries = [];
411
413
  const reserve = { names: reserveContinuation(docs.length, available.length) };
414
+ const instructionsOf = (list) => {
415
+ const text = instructionsFor?.(list.map((entry) => entry.name)) || '';
416
+ return text ? { mcpInstructions: text } : {};
417
+ };
412
418
  const page = (extra) => ({
413
419
  entries: [...entries, extra],
414
420
  missing,
415
421
  blocked,
416
422
  ...found,
423
+ ...instructionsOf([...entries, extra]),
417
424
  note: DESCRIBE_NOTE,
418
425
  continueWith: reserve,
419
426
  });
@@ -431,7 +438,15 @@ export function describeBridgeTools(session, args = {}, { mode } = {}) {
431
438
  // A document that fits on a page of its own starts the next page whole.
432
439
  if (
433
440
  entries.length &&
434
- fits({ entries: [whole], missing, blocked, ...found, note: DESCRIBE_NOTE, continueWith: reserve })
441
+ fits({
442
+ entries: [whole],
443
+ missing,
444
+ blocked,
445
+ ...found,
446
+ ...instructionsOf([whole]),
447
+ note: DESCRIBE_NOTE,
448
+ continueWith: reserve,
449
+ })
435
450
  ) {
436
451
  nextPosition = [entryIndex, 0];
437
452
  break;
@@ -464,7 +479,7 @@ export function describeBridgeTools(session, args = {}, { mode } = {}) {
464
479
  'the remaining definitions cannot be continued; describe fewer tools at once.'
465
480
  );
466
481
  }
467
- return { entries, missing, blocked, ...found, note: DESCRIBE_NOTE, continueWith };
482
+ return { entries, missing, blocked, ...found, ...instructionsOf(entries), note: DESCRIBE_NOTE, continueWith };
468
483
  }
469
484
 
470
485
  // ---------------------------------------------------------------------------
@@ -4,7 +4,11 @@
4
4
  import { clean } from './session-text.mjs';
5
5
  import { mergePendingDeferredToolDelta } from './deferred-tool-delta.mjs';
6
6
  import { applyInitialDeferredToolManifestToBp2 } from '../context/collect.mjs';
7
- import { listedDeferredToolNames } from '../context/deferred-tools.mjs';
7
+ import {
8
+ listedDeferredToolNames,
9
+ mcpServerManifestLabel,
10
+ sanitizeMcpInstructionText,
11
+ } from '../context/deferred-tools.mjs';
8
12
  import { parseMcpToolName } from '../../../shared/tool-primitives.mjs';
9
13
  import { getMcpServerInstructionsMap, getMcpTools, getMcpToolGeneration } from '../mcp/client.mjs';
10
14
  import { filterMcpToolsForSession } from './extension-scopes.mjs';
@@ -380,10 +384,35 @@ export function applyDeferredToolSurface(session, mode, extraTools = [], options
380
384
  session.deferredDiscoveredTools || []
381
385
  );
382
386
  }
387
+ const catalog = surfaceCatalog(session, extraTools, options);
388
+ const defaultNames = surfaceDefaultNames(session, catalog, mode);
389
+ const selected = new Set(surfaceSelectedNames(session, catalog, defaultNames, providerMode, bridge));
390
+ session.deferredToolCatalog = catalog;
391
+ session.deferredToolUsage = MEASURED_TOOL_USAGE;
392
+ session.deferredDefaultTools = sortedNamesByMeasuredUsage(defaultNames);
393
+ session.deferredProviderMode = providerMode;
394
+ session.deferredNativeTools = providerMode === 'native';
395
+ session.deferredSurfaceMode = mode;
396
+ const active = installSurfaceTools(session, catalog, selected, mode, bridge);
397
+ session.deferredCallableTools = sortedNamesByMeasuredUsage(active);
398
+ if (providerMode === 'native') {
399
+ const restored = previousExposure === TOOL_EXPOSURE_BRIDGE ? session.nativeDeferredDiscovery || [] : [];
400
+ delete session.nativeDeferredDiscovery;
401
+ const discovered = canonicalDeferredToolNames(catalog, [...(session.deferredDiscoveredTools || []), ...restored]);
402
+ session.deferredSelectedTools = active;
403
+ session.deferredDiscoveredTools = discovered.filter((name) => !selected.has(name));
404
+ } else {
405
+ setDeferredToolState(session, active);
406
+ }
407
+ refreshSurfaceManifest(session, exposureChanged);
408
+ return session;
409
+ }
410
+
411
+ // The surface's catalog, rebuilt from the full catalog, not just its eager
412
+ // projection. Current definitions still take precedence over retained
413
+ // definitions. Bridge definitions belong to the exposure, never to the catalog.
414
+ function surfaceCatalog(session, extraTools, options) {
383
415
  const byName = new Map();
384
- // Rebuild from the full catalog, not just its eager projection. Current
385
- // definitions still take precedence over retained definitions. Bridge
386
- // definitions belong to the exposure, never to the catalog.
387
416
  const candidates = filterDisallowedTools(
388
417
  filterModelEditTools(
389
418
  [...session.tools, ...(extraTools || []), ...deferredCatalogUnion(session)].filter(
@@ -398,7 +427,11 @@ export function applyDeferredToolSurface(session, mode, extraTools = [], options
398
427
  if (!name || byName.has(name)) continue;
399
428
  byName.set(name, activeToolForSurface(tool));
400
429
  }
401
- const catalog = sortedCatalogByMeasuredUsage([...byName.values()]);
430
+ return sortedCatalogByMeasuredUsage([...byName.values()]);
431
+ }
432
+
433
+ // The mode's eager tools in `catalog`, less those the session keeps loadable.
434
+ function surfaceDefaultNames(session, catalog, mode) {
402
435
  const defaultNames = defaultDeferredToolNames(catalog, mode);
403
436
  // Solo keeps `agent` in the deferred pool: loadable on request, never eager.
404
437
  // The mode the prompt was built with: a mid-conversation workflow or
@@ -413,19 +446,24 @@ export function applyDeferredToolSurface(session, mode, extraTools = [], options
413
446
  for (const name of Array.isArray(session.workflowOpenedTools) ? session.workflowOpenedTools : []) {
414
447
  defaultNames.delete(name);
415
448
  }
449
+ return defaultNames;
450
+ }
451
+
452
+ // The names the surface selects: the session's stored selection, else the
453
+ // defaults.
454
+ function surfaceSelectedNames(session, catalog, defaultNames, providerMode, bridge) {
416
455
  // Bridge sessions expose the mode's core only: a stored (legacy all-selected)
417
456
  // selection never widens the wire surface, and load_tool is the bridge's own.
418
457
  const storedNames = providerMode === 'native' || bridge ? [] : storedDeferredToolNames(session);
419
458
  let selectedNames = storedNames.length ? canonicalDeferredToolNames(catalog, storedNames) : [];
420
459
  if (!selectedNames.length || providerMode === 'native') selectedNames = sortedNamesByMeasuredUsage(defaultNames);
421
460
  if (bridge) selectedNames = selectedNames.filter((name) => name !== 'load_tool');
422
- const selected = new Set(selectedNames);
423
- session.deferredToolCatalog = catalog;
424
- session.deferredToolUsage = MEASURED_TOOL_USAGE;
425
- session.deferredDefaultTools = sortedNamesByMeasuredUsage(defaultNames);
426
- session.deferredProviderMode = providerMode;
427
- session.deferredNativeTools = providerMode === 'native';
428
- session.deferredSurfaceMode = mode;
461
+ return selectedNames;
462
+ }
463
+
464
+ // Replace `session.tools` with the selected definitions (plus the bridge's
465
+ // own) and record the exposure; returns the active tool names.
466
+ function installSurfaceTools(session, catalog, selected, mode, bridge) {
429
467
  session.tools.length = 0;
430
468
  const active = [];
431
469
  for (const tool of catalog) {
@@ -439,16 +477,12 @@ export function applyDeferredToolSurface(session, mode, extraTools = [], options
439
477
  if (bridge && catalog.length) session.tools.push(...BRIDGE_SURFACE_TOOLS);
440
478
  if (bridge) session.toolExposureMode = TOOL_EXPOSURE_BRIDGE;
441
479
  else delete session.toolExposureMode;
442
- session.deferredCallableTools = sortedNamesByMeasuredUsage(active);
443
- if (providerMode === 'native') {
444
- const restored = previousExposure === TOOL_EXPOSURE_BRIDGE ? session.nativeDeferredDiscovery || [] : [];
445
- delete session.nativeDeferredDiscovery;
446
- const discovered = canonicalDeferredToolNames(catalog, [...(session.deferredDiscoveredTools || []), ...restored]);
447
- session.deferredSelectedTools = active;
448
- session.deferredDiscoveredTools = discovered.filter((name) => !selected.has(name));
449
- } else {
450
- setDeferredToolState(session, active);
451
- }
480
+ return active;
481
+ }
482
+
483
+ // Keep the system prompt's fixed deferred-tool block (BP2) and the announced
484
+ // set in step with the surface.
485
+ function refreshSurfaceManifest(session, exposureChanged) {
452
486
  if (exposureChanged && session.messages?.some((m) => m?.role === 'system')) {
453
487
  // Replace the previous exposure's fixed block once: the native deferred
454
488
  // manifest, or the bridge roster + guidance. History stays as is.
@@ -473,7 +507,6 @@ export function applyDeferredToolSurface(session, mode, extraTools = [], options
473
507
  // the startup pool.
474
508
  session.deferredAnnouncedTools = deferredPoolToolNames(session);
475
509
  }
476
- return session;
477
510
  }
478
511
 
479
512
  export function rebuildDeferredToolSurfaceForProvider(session, provider) {
@@ -669,9 +702,8 @@ function reconcileDeferredMcpToolCatalogUncached(session, liveMcpTools) {
669
702
  const lateCatalog = Array.isArray(session.deferredLateToolCatalog) ? session.deferredLateToolCatalog : [];
670
703
  // A bridge session judges a removal by the definition it had before this
671
704
  // reconciliation (a disconnected late tool leaves the late pool below).
672
- const knownBefore = isToolBridgeEnabled(session)
673
- ? new Map(deferredCatalogUnion(session).map((tool) => [clean(tool?.name), tool]))
674
- : null;
705
+ const catalogBefore = new Map(deferredCatalogUnion(session).map((tool) => [clean(tool?.name), tool]));
706
+ const knownBefore = isToolBridgeEnabled(session) ? catalogBefore : null;
675
707
  const active = persistentToolSurfaceNames(session);
676
708
 
677
709
  const liveMcpByName = new Map();
@@ -723,12 +755,14 @@ function reconcileDeferredMcpToolCatalogUncached(session, liveMcpTools) {
723
755
  for (const name of previousNames) {
724
756
  if (nextNames.has(name)) continue;
725
757
  if (knownBefore && !rosterPermits(session, knownBefore.get(name) || { name })) continue;
726
- removed.push(name);
758
+ // The server comes from the definition the session had, so a shortened
759
+ // name never has to be parsed.
760
+ removed.push({ name, server: parseMcpToolName(catalogBefore.get(name) || name)?.server || '' });
727
761
  }
728
762
  if (!added.length && !removed.length) return null;
729
763
  mergePendingDeferredToolDelta(session, { added, removed });
730
764
  session.updatedAt = Date.now();
731
- return [...added.map((entry) => entry.name), ...removed];
765
+ return [...added, ...removed].map((entry) => entry.name);
732
766
  }
733
767
 
734
768
  /**
@@ -742,6 +776,9 @@ function reconcileDeferredMcpToolCatalogUncached(session, liveMcpTools) {
742
776
  * Returns the re-armed names, or null when there are none.
743
777
  */
744
778
  export function rearmLateDeferredTools(session) {
779
+ // The compaction also drops the load results that carried a server's
780
+ // instruction text, so the next load of its tools delivers it again.
781
+ if (session) delete session.mcpInstructionsAnnounced;
745
782
  if (!session || !Array.isArray(session.tools) || fixedSurfaceSession(session)) return null;
746
783
  if (!Array.isArray(session.deferredMcpToolNames) || !Array.isArray(session.deferredAnnouncedTools)) return null;
747
784
  // Listed in the boot manifest, or already loaded/discovered: still visible.
@@ -797,7 +834,8 @@ export function selectDeferredTools(session, names, mode, { exact = false } = {}
797
834
  const key = clean(raw);
798
835
  if (byName.has(key) || byName.has(key.toLowerCase())) return [key];
799
836
  const members = mcpServerSelection(catalog, key).map((tool) => clean(tool?.name));
800
- return members.length ? members : [key];
837
+ if (members.length) return members;
838
+ return [key];
801
839
  });
802
840
  for (const rawName of new Set(requested)) {
803
841
  const requestedName = clean(rawName);
@@ -878,14 +916,67 @@ function resolvableCatalog(session) {
878
916
  return filterSessionToolPolicy(tools, session);
879
917
  }
880
918
 
919
+ // A server's instruction text rides the first load_tool naming any of its
920
+ // tools (loaded now, already active, or described on the bridge), once until
921
+ // compaction (`mcpInstructionsAnnounced`; rearmLateDeferredTools clears it with
922
+ // the transcript that held the text): `## <server>` + body, sanitized and
923
+ // capped like the BP2 block it replaces. No text, nothing sent or recorded.
924
+ function pendingMcpInstructions(session, catalog, names) {
925
+ const map = session?.mcpServerInstructions;
926
+ if (!map || typeof map !== 'object' || !names.length) return [];
927
+ const byName = new Map(catalog.map((tool) => [clean(tool?.name), tool]));
928
+ const announced = new Set(Array.isArray(session.mcpInstructionsAnnounced) ? session.mcpInstructionsAnnounced : []);
929
+ const pending = [];
930
+ for (const name of names) {
931
+ const server = parseMcpToolName(byName.get(name) || name)?.server;
932
+ if (!server || announced.has(server) || !mcpServerManifestLabel(server)) continue;
933
+ const text = sanitizeMcpInstructionText(map[server]);
934
+ if (!text) continue;
935
+ announced.add(server);
936
+ pending.push({ server, block: `## ${server}\n${text}` });
937
+ }
938
+ return pending;
939
+ }
940
+
941
+ function announceMcpServers(session, pending) {
942
+ if (!pending.length) return;
943
+ session.mcpInstructionsAnnounced = [
944
+ ...new Set([...(session.mcpInstructionsAnnounced || []), ...pending.map((entry) => entry.server)]),
945
+ ];
946
+ }
947
+
948
+ function claimMcpServerInstructions(session, catalog, names) {
949
+ const pending = pendingMcpInstructions(session, catalog, names);
950
+ announceMcpServers(session, pending);
951
+ return pending.map((entry) => entry.block).join('\n');
952
+ }
953
+
881
954
  // Pure loader. Input is exact deferred-tool names/aliases; output reports
882
955
  // loaded / already-active / missing / blocked tools PLUS pending/failed MCP
883
956
  // servers. No listing, no ranking, no substring filter. `options.mcpStatus` is
884
957
  // the runtime getter for per-server status.
885
958
  export function renderToolSearch(args = {}, session, mode = 'full', options = {}) {
886
959
  // Bridge mode: load_tool describes (bounded, paged) and never loads.
887
- if (isToolBridgeEnabled(session)) return formatBridgeResult(describeBridgeTools(session, args, { mode }));
888
960
  const catalog = resolvableCatalog(session);
961
+ if (isToolBridgeEnabled(session)) {
962
+ // The page counts the instruction text inside its size budget; only the
963
+ // servers whose text made it into the returned page are marked announced.
964
+ const pendingFor = (names) => pendingMcpInstructions(session, catalog, names);
965
+ const described = describeBridgeTools(session, args, {
966
+ mode,
967
+ instructionsFor: (names) =>
968
+ pendingFor(names)
969
+ .map((entry) => entry.block)
970
+ .join('\n'),
971
+ });
972
+ if (!described.error && described.mcpInstructions) {
973
+ announceMcpServers(
974
+ session,
975
+ pendingFor((described.entries || []).map((entry) => clean(entry?.name)).filter(Boolean))
976
+ );
977
+ }
978
+ return formatBridgeResult(described);
979
+ }
889
980
  const requestedNames = parseToolSelection(args.names);
890
981
  const { pending: pendingMcpServers, failed: failedMcpServers } = pendingAndFailedMcpServers(options?.mcpStatus);
891
982
  const mcpFields = {
@@ -955,11 +1046,15 @@ export function renderToolSearch(args = {}, session, mode = 'full', options = {}
955
1046
  if (missing.length && failedMcpServers.length) {
956
1047
  notes.push('Some requested names may belong to a failed MCP server; those tools are unavailable.');
957
1048
  }
1049
+ // Tools the session already had active (loaded before, or eager) count too:
1050
+ // their server's text may never have been delivered.
1051
+ const mcpInstructions = claimMcpServerInstructions(session, catalog, [...loaded, ...alreadyActive]);
958
1052
  return JSON.stringify(
959
1053
  {
960
1054
  // `selected` retained for back-compat consumers (mode is always 'select').
961
1055
  selected: { mode: 'select', tools: toolSelection },
962
1056
  ...(nativeToolSearch ? { nativeToolSearch } : {}),
1057
+ ...(mcpInstructions ? { mcpInstructions } : {}),
963
1058
  loaded,
964
1059
  alreadyActive,
965
1060
  ...(alreadyActiveSchemas.length ? { alreadyActiveSchemas } : {}),