mixdog 1.4.0 → 1.4.2

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 (493) hide show
  1. package/package.json +3 -2
  2. package/scripts/build-shipped-history.mjs +115 -33
  3. package/scripts/check-voice-runtime-inputs.mjs +25 -0
  4. package/scripts/gate-local.mjs +183 -63
  5. package/scripts/latest-guard.mjs +21 -9
  6. package/scripts/lib/gate-scheduler.mjs +138 -32
  7. package/scripts/lib/gate-timing-reporter.mjs +16 -0
  8. package/scripts/lib/isolated-test-env.mjs +10 -5
  9. package/scripts/lib/run-node-tests.mjs +91 -66
  10. package/scripts/lib/scoped-temp.d.mts +16 -0
  11. package/scripts/lib/scoped-temp.mjs +124 -0
  12. package/scripts/lib/stale-temp.mjs +44 -0
  13. package/scripts/lib/test-failure-records.mjs +14 -1
  14. package/scripts/lib/test-failure-summary.mjs +16 -4
  15. package/scripts/lib/test-spawn-ledger.mjs +12 -3
  16. package/scripts/post-release-check.mjs +1 -6
  17. package/scripts/pptx-kit-index.mjs +17 -5
  18. package/scripts/suite-health.mjs +115 -24
  19. package/scripts/test-data-dir.mjs +10 -7
  20. package/scripts/test.mjs +9 -14
  21. package/src/agents/advisor/AGENT.md +4 -4
  22. package/src/agents/advisor/agent.json +1 -1
  23. package/src/agents/explore/AGENT.md +22 -0
  24. package/src/agents/explore/agent.json +6 -0
  25. package/src/app.mjs +4 -0
  26. package/src/cli.mjs +7 -0
  27. package/src/defaults/agents.json +7 -0
  28. package/src/defaults/shipped-history.json +23 -2
  29. package/src/defaults/skills/browser-use/SKILL.md +10 -5
  30. package/src/defaults/skills/code-tidy/SKILL.md +71 -102
  31. package/src/defaults/skills/code-tidy/references/agent-cleanup.md +66 -114
  32. package/src/defaults/skills/code-tidy/references/cleanup-report.md +19 -35
  33. package/src/defaults/skills/code-tidy/references/dead-code.md +3 -9
  34. package/src/defaults/skills/code-tidy/references/test-suite-slop.md +16 -27
  35. package/src/defaults/skills/computer-use/SKILL.md +16 -4
  36. package/src/defaults/skills/goal-management/SKILL.md +1 -1
  37. package/src/defaults/skills/setup/references/actions.md +12 -6
  38. package/src/defaults/skills/setup/references/surfaces.md +2 -2
  39. package/src/defaults/skills/setup-guide/SKILL.md +5 -2
  40. package/src/defaults/skills/setup-guide/model-recommendations.json +13 -0
  41. package/src/defaults/skills/skill-creator/SKILL.md +85 -190
  42. package/src/defaults/skills/skill-creator/references/design.md +70 -162
  43. package/src/defaults/skills/skill-creator/references/evaluation.md +33 -92
  44. package/src/defaults/skills/test-suite/SKILL.md +194 -0
  45. package/src/defaults/skills/test-suite/references/case-management.md +51 -0
  46. package/src/defaults/skills/test-suite/references/flaky-policy.md +39 -0
  47. package/src/defaults/skills/test-suite/references/reliability.md +49 -0
  48. package/src/defaults/skills/test-suite/references/suite-design.md +78 -0
  49. package/src/defaults/skills/test-suite/scripts/inventory.mjs +335 -0
  50. package/src/defaults/skills/visualize/SKILL.md +6 -2
  51. package/src/lib/rules-builder.cjs +35 -42
  52. package/src/output-styles/common.md +2 -2
  53. package/src/output-styles/simple.md +1 -1
  54. package/src/rules/agent/AGENT.md +5 -4
  55. package/src/rules/lead/LEAD.md +12 -7
  56. package/src/rules/shared/10-tool-workflow.md +3 -1
  57. package/src/rules/shared/20-research.md +3 -6
  58. package/src/rules/shared/40-editing.md +1 -0
  59. package/src/rules/shared/45-destructive.md +0 -2
  60. package/src/rules/shared/50-execution.md +2 -0
  61. package/src/rules/shared/60-verification.md +10 -9
  62. package/src/rules/shared/70-delivery.md +8 -9
  63. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/abort-link.mjs +5 -1
  64. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +10 -1
  65. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +156 -24
  66. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +13 -0
  67. package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +4 -1
  68. package/src/runtime/agent/orchestrator/cache-break-trace.mjs +2 -0
  69. package/src/runtime/agent/orchestrator/config.mjs +12 -7
  70. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +8 -8
  71. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +5 -26
  72. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +3 -1
  73. package/src/runtime/agent/orchestrator/internal-agents.mjs +3 -14
  74. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +122 -12
  75. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +5 -3
  76. package/src/runtime/agent/orchestrator/mcp/client-config.mjs +13 -2
  77. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +152 -58
  78. package/src/runtime/agent/orchestrator/mcp/client.mjs +51 -18
  79. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +88 -54
  80. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +42 -26
  81. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +54 -8
  82. package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +1 -1
  83. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +37 -31
  84. package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +8 -2
  85. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +6 -2
  86. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -2
  87. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  88. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +31 -21
  89. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +3 -1
  90. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +14 -3
  91. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +6 -0
  92. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -2
  93. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +3 -1
  94. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +2 -2
  95. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
  96. package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +1 -10
  97. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +1 -1
  98. package/src/runtime/agent/orchestrator/providers/cursor.mjs +3 -0
  99. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +40 -0
  100. package/src/runtime/agent/orchestrator/providers/custom-provider-keys.mjs +34 -0
  101. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +3 -0
  102. package/src/runtime/agent/orchestrator/providers/devin-auth.mjs +14 -7
  103. package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +3 -2
  104. package/src/runtime/agent/orchestrator/providers/devin.mjs +3 -0
  105. package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +2 -2
  106. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +4 -1
  107. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +248 -41
  108. package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -11
  109. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +45 -6
  110. package/src/runtime/agent/orchestrator/providers/lib/first-output-window.mjs +58 -0
  111. package/src/runtime/agent/orchestrator/providers/lib/listen-on.mjs +14 -0
  112. package/src/runtime/agent/orchestrator/providers/lib/oauth-page.mjs +0 -18
  113. package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +138 -6
  114. package/src/runtime/agent/orchestrator/providers/mimo-oauth-login.mjs +1 -10
  115. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +1 -1
  116. package/src/runtime/agent/orchestrator/providers/model-cache.mjs +100 -33
  117. package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +1 -1
  118. package/src/runtime/agent/orchestrator/providers/oauth-secret-fields.fixtures.mjs +45 -0
  119. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
  120. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +9 -2
  121. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +7 -0
  122. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +14 -3
  123. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +4 -2
  124. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
  125. package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +5 -3
  126. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +16 -3
  127. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
  128. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +5 -2
  129. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +13 -3
  130. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +13 -2
  131. package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +81 -42
  132. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +8 -0
  133. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +160 -31
  134. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +22 -12
  135. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +7 -1
  136. package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +49 -22
  137. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +1 -1
  138. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +3 -1
  139. package/src/runtime/agent/orchestrator/providers/registry.mjs +45 -2
  140. package/src/runtime/agent/orchestrator/providers/vertex-ai-auth.mjs +622 -0
  141. package/src/runtime/agent/orchestrator/providers/vertex-ai-maas.mjs +185 -0
  142. package/src/runtime/agent/orchestrator/providers/vertex-ai-models.mjs +445 -0
  143. package/src/runtime/agent/orchestrator/providers/vertex-ai.mjs +567 -0
  144. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +41 -13
  145. package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +2 -0
  146. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +8 -6
  147. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +7 -3
  148. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
  149. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +6 -1
  150. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +14 -5
  151. package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +1 -0
  152. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +5 -2
  153. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +30 -5
  154. package/src/runtime/agent/orchestrator/session/agent-room.mjs +171 -37
  155. package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +1 -3
  156. package/src/runtime/agent/orchestrator/session/cache-warm.mjs +102 -12
  157. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
  158. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -8
  159. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +23 -0
  160. package/src/runtime/agent/orchestrator/session/compact/observation-supersession.mjs +103 -23
  161. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +160 -36
  162. package/src/runtime/agent/orchestrator/session/context-utils.mjs +13 -1
  163. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +164 -23
  164. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +139 -19
  165. package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +38 -7
  166. package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +3 -0
  167. package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +100 -22
  168. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +4 -3
  169. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -6
  170. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +33 -3
  171. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +9 -2
  172. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -1
  173. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +13 -5
  174. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +14 -0
  175. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +13 -1
  176. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +23 -4
  177. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +2 -0
  178. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +23 -1
  179. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +39 -21
  180. package/src/runtime/agent/orchestrator/session/loop/turn-limits.mjs +13 -8
  181. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +50 -28
  182. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +3 -3
  183. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +3 -1
  184. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +2 -2
  185. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +1 -0
  186. package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +2 -1
  187. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +30 -13
  188. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +63 -6
  189. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +7 -0
  190. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +15 -0
  191. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +35 -13
  192. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +24 -14
  193. package/src/runtime/agent/orchestrator/session/manager/session-scratch-environment.mjs +70 -0
  194. package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +0 -3
  195. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +10 -6
  196. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +4 -0
  197. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +3 -0
  198. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +56 -29
  199. package/src/runtime/agent/orchestrator/session/manager/turn-reap.mjs +125 -0
  200. package/src/runtime/agent/orchestrator/session/mixy-name.mjs +11 -5
  201. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +5 -1
  202. package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +30 -7
  203. package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.mjs +154 -0
  204. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +7 -0
  205. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +4 -0
  206. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +15 -0
  207. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +37 -8
  208. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +252 -39
  209. package/src/runtime/agent/orchestrator/session/store-transcript-cache.mjs +163 -64
  210. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +164 -4
  211. package/src/runtime/agent/orchestrator/session/store-transcript-worker.mjs +142 -18
  212. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +61 -2
  213. package/src/runtime/agent/orchestrator/stall-policy.mjs +23 -0
  214. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +6 -2
  215. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +2 -2
  216. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -2
  217. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +6 -1
  218. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +46 -20
  219. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +7 -1
  220. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +67 -31
  221. package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +25 -13
  222. package/src/runtime/agent/orchestrator/tools/builtin/lib/scoped-search-flight.mjs +9 -5
  223. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +58 -20
  224. package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +7 -1
  225. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +6 -3
  226. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +1 -1
  227. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +23 -12
  228. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +6 -1
  229. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +1 -8
  230. package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +20 -15
  231. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +96 -20
  232. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +59 -10
  233. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/federation.mjs +3 -1
  234. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -1
  235. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
  236. package/src/runtime/agent/orchestrator/tools/lib/isolated-test-env.mjs +4 -3
  237. package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.mjs +99 -2
  238. package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +2 -0
  239. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +103 -13
  240. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +3 -1
  241. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +49 -15
  242. package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +45 -27
  243. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +34 -10
  244. package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +28 -4
  245. package/src/runtime/agent/orchestrator/tools/lib/wait-until-fixture.mjs +26 -0
  246. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -2
  247. package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +13 -13
  248. package/src/runtime/channels/lib/webhook/signature.mjs +4 -1
  249. package/src/runtime/channels/lib/whisper-port.mjs +29 -11
  250. package/src/runtime/channels/lib/whisper-server.mjs +10 -1
  251. package/src/runtime/computer-bridge/action-schema.mjs +2 -2
  252. package/src/runtime/computer-bridge/core-actions.mjs +2 -2
  253. package/src/runtime/computer-bridge/error-recovery.mjs +5 -4
  254. package/src/runtime/computer-bridge/linux-action-capabilities.mjs +24 -5
  255. package/src/runtime/computer-bridge/linux-readiness.d.mts +7 -2
  256. package/src/runtime/connectors/connector-accounts-mcp.mjs +236 -103
  257. package/src/runtime/connectors/gmail-app-password.mjs +3 -3
  258. package/src/runtime/explore/explorer.mjs +93 -0
  259. package/src/runtime/explore/tool-defs.mjs +35 -0
  260. package/src/runtime/explore/tool.mjs +123 -0
  261. package/src/runtime/local-provider/server-process.mjs +2 -1
  262. package/src/runtime/media/adapters/gemini-video.mjs +4 -1
  263. package/src/runtime/media/adapters/xai-media.mjs +2 -1
  264. package/src/runtime/memory/index.mjs +4 -0
  265. package/src/runtime/memory/lib/embedding-provider.mjs +4 -1
  266. package/src/runtime/memory/lib/embedding-worker.mjs +10 -3
  267. package/src/runtime/memory/lib/memory-action-handlers/cycle-backfill-action.mjs +1 -7
  268. package/src/runtime/memory/lib/memory-chunk-quality.mjs +1 -6
  269. package/src/runtime/office/authoring/pdf-author-action.mjs +1 -6
  270. package/src/runtime/office/design/design-art-direction.mjs +29 -27
  271. package/src/runtime/office/pdf/pdf-writer.mjs +6 -1
  272. package/src/runtime/office/portable/portable-snapshot-docx.mjs +7 -1
  273. package/src/runtime/office/portable/portable-snapshot-shared.mjs +6 -1
  274. package/src/runtime/office/quality/assurance-rendered.mjs +9 -2
  275. package/src/runtime/office/quality/assurance-structure-docx.mjs +4 -2
  276. package/src/runtime/office/quality/design-review.mjs +1 -5
  277. package/src/runtime/office/quality/document-brief.mjs +1 -5
  278. package/src/runtime/office/quality/forced-break-pages.mjs +9 -6
  279. package/src/runtime/office/quality/quality-pipeline.mjs +1 -1
  280. package/src/runtime/shared/agent-route-config.mjs +17 -2
  281. package/src/runtime/shared/atomic-file.mjs +103 -2
  282. package/src/runtime/shared/background-tasks.mjs +15 -6
  283. package/src/runtime/shared/bot-owned-work.mjs +56 -0
  284. package/src/runtime/shared/connector-accounts.mjs +5 -5
  285. package/src/runtime/shared/connector-catalog.d.mts +11 -4
  286. package/src/runtime/shared/connector-catalog.mjs +60 -52
  287. package/src/runtime/shared/custom-provider-keys.mjs +20 -0
  288. package/src/runtime/shared/defaults-separation.mjs +1 -4
  289. package/src/runtime/shared/fetch-safe-listen.d.mts +16 -0
  290. package/src/runtime/shared/fetch-safe-listen.mjs +50 -0
  291. package/src/runtime/shared/file-has-data.mjs +26 -2
  292. package/src/runtime/shared/file-lock.mjs +13 -15
  293. package/src/runtime/shared/git-launch.d.mts +9 -0
  294. package/src/runtime/shared/git-launch.mjs +213 -0
  295. package/src/runtime/shared/git-runner-worker.mjs +2 -1
  296. package/src/runtime/shared/handoff-artifacts.d.mts +2 -1
  297. package/src/runtime/shared/handoff-artifacts.mjs +18 -8
  298. package/src/runtime/shared/idle-auto-clear-gate.mjs +29 -7
  299. package/src/runtime/shared/llm/cost.mjs +11 -2
  300. package/src/runtime/shared/llm/http-agent.mjs +313 -29
  301. package/src/runtime/shared/llm/model-catalog-projection.mjs +3 -2
  302. package/src/runtime/shared/llm/model-catalog.mjs +46 -6
  303. package/src/runtime/shared/llm/provider-catalog-registry.mjs +3 -0
  304. package/src/runtime/shared/llm/system-proxy.mjs +452 -0
  305. package/src/runtime/shared/loopback-listener.mjs +25 -2
  306. package/src/runtime/shared/model-route-chain.d.mts +7 -2
  307. package/src/runtime/shared/model-route-chain.mjs +22 -4
  308. package/src/runtime/shared/open-url.mjs +10 -0
  309. package/src/runtime/shared/provider-accounts.mjs +52 -2
  310. package/src/runtime/shared/session-activity-signal.mjs +14 -0
  311. package/src/runtime/shared/session-scratch.mjs +37 -0
  312. package/src/runtime/shared/staged-install-worker.mjs +5 -0
  313. package/src/runtime/shared/staged-update.mjs +47 -2
  314. package/src/runtime/shared/tool-card-model/agent-surface.mjs +1 -3
  315. package/src/runtime/shared/tool-card-model/card-status.mjs +15 -2
  316. package/src/runtime/shared/tool-result-summary.mjs +1 -1
  317. package/src/runtime/shared/tool-surface.mjs +113 -36
  318. package/src/runtime/shared/tool-work-units.mjs +6 -0
  319. package/src/runtime/shared/turn-snapshot-store.mjs +13 -6
  320. package/src/runtime/shared/turn-worktree-snapshot.mjs +414 -54
  321. package/src/runtime/shared/worktrees/activity-gate.mjs +215 -318
  322. package/src/runtime/shared/worktrees/activity-ledger-worker.mjs +56 -0
  323. package/src/runtime/shared/worktrees/activity-ledger.mjs +391 -0
  324. package/src/runtime/shared/worktrees/branch-management.mjs +9 -3
  325. package/src/runtime/shared/worktrees/contract.mjs +4 -1
  326. package/src/runtime/shared/worktrees/git.mjs +72 -0
  327. package/src/runtime/shared/worktrees/manager.mjs +285 -168
  328. package/src/runtime/shared/worktrees/test-support.mjs +6 -10
  329. package/src/runtime/tidy/install.mjs +95 -15
  330. package/src/runtime/tidy/languages.mjs +17 -15
  331. package/src/runtime/tidy/process.mjs +7 -4
  332. package/src/runtime/tidy/resolve.mjs +19 -16
  333. package/src/runtime/tidy/runners/biome.mjs +3 -3
  334. package/src/runtime/tidy/tool.mjs +1 -1
  335. package/src/runtime/tidy/worktree.mjs +10 -6
  336. package/src/runtime/visualize-bridge/client.mjs +6 -1
  337. package/src/runtime/visualize-bridge/tool-defs.mjs +7 -2
  338. package/src/runtime/web-search/index.mjs +1 -1
  339. package/src/session-runtime/agent-home-delivery.mjs +41 -0
  340. package/src/session-runtime/agent-task-sessions.mjs +458 -312
  341. package/src/session-runtime/agent-tool-routing.mjs +8 -41
  342. package/src/session-runtime/boot/tools.mjs +7 -1
  343. package/src/session-runtime/bot-status.mjs +107 -101
  344. package/src/session-runtime/bot-target.mjs +38 -0
  345. package/src/session-runtime/channel-config-api.mjs +6 -1
  346. package/src/session-runtime/connector-merge-migration.mjs +18 -15
  347. package/src/session-runtime/conversation-import/codex-titles.mjs +88 -0
  348. package/src/session-runtime/conversation-import/discovery.mjs +401 -36
  349. package/src/session-runtime/conversation-import/service.mjs +35 -8
  350. package/src/session-runtime/handoff-attachments.mjs +82 -31
  351. package/src/session-runtime/held-requests.mjs +3 -1
  352. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +12 -13
  353. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +1 -0
  354. package/src/session-runtime/internal-tool-executor/feature-tools.mjs +2 -0
  355. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +8 -0
  356. package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +4 -1
  357. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +145 -112
  358. package/src/session-runtime/lifecycle/cold-heir.mjs +11 -6
  359. package/src/session-runtime/lifecycle/session-catalog.mjs +9 -1
  360. package/src/session-runtime/lifecycle/teardown/session-close.mjs +6 -1
  361. package/src/session-runtime/lifecycle/teardown.mjs +1 -1
  362. package/src/session-runtime/mcp-glue/server-catalog.mjs +16 -10
  363. package/src/session-runtime/mcp-glue/server-input.mjs +4 -1
  364. package/src/session-runtime/mixy-heartbeat.mjs +1 -1
  365. package/src/session-runtime/model-route/set-route.mjs +11 -2
  366. package/src/session-runtime/provider-auth/login.mjs +2 -2
  367. package/src/session-runtime/provider-catalog-cache.mjs +82 -36
  368. package/src/session-runtime/provider-models/catalog-load.mjs +23 -6
  369. package/src/session-runtime/provider-models.mjs +4 -2
  370. package/src/session-runtime/resource-mcp-api.mjs +98 -35
  371. package/src/session-runtime/resource-mcp-connector-accounts.mjs +44 -31
  372. package/src/session-runtime/resource-mcp-secrets.mjs +28 -11
  373. package/src/session-runtime/resource-mcp-servers.mjs +5 -4
  374. package/src/session-runtime/resource-recall-api.mjs +6 -1
  375. package/src/session-runtime/runtime-feature-gates.mjs +5 -1
  376. package/src/session-runtime/services/agent-tool/execute.mjs +14 -9
  377. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +20 -4
  378. package/src/session-runtime/services/agent-tool/model-select.mjs +48 -7
  379. package/src/session-runtime/services/agent-tool/render.mjs +71 -17
  380. package/src/session-runtime/services/agent-tool/send-flow.mjs +10 -2
  381. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +8 -3
  382. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +4 -1
  383. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +6 -2
  384. package/src/session-runtime/services/agent-tool/tool-def.mjs +9 -9
  385. package/src/session-runtime/services/agent-watchdog-registry.mjs +32 -4
  386. package/src/session-runtime/services/channel-worker/daemon-spawn.mjs +2 -0
  387. package/src/session-runtime/services/daemon-candidate.mjs +4 -1
  388. package/src/session-runtime/services/memory-runtime-proxy/daemon-spawn.mjs +4 -0
  389. package/src/session-runtime/services/plugin-admin.mjs +10 -6
  390. package/src/session-runtime/services/provider-admin.mjs +283 -11
  391. package/src/session-runtime/session-turn-api.mjs +2 -0
  392. package/src/session-runtime/settings-builtin-tools-api.mjs +4 -1
  393. package/src/session-runtime/settings-system-api.mjs +1 -1
  394. package/src/session-runtime/setup-tool/agent-settings-actions.mjs +13 -3
  395. package/src/session-runtime/setup-tool/connector-widen-approval.mjs +96 -0
  396. package/src/session-runtime/setup-tool/executor.mjs +6 -2
  397. package/src/session-runtime/setup-tool/tool-defs.mjs +2 -1
  398. package/src/session-runtime/tool-policy/policy-stage.mjs +1 -0
  399. package/src/session-runtime/turn/agent-ops.mjs +23 -0
  400. package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +49 -11
  401. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +5 -1
  402. package/src/session-runtime/workflow-agents-api/agent-route.mjs +25 -5
  403. package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -3
  404. package/src/session-runtime/workspace/activity-guards.mjs +176 -0
  405. package/src/session-runtime/workspace/session-view.mjs +149 -0
  406. package/src/session-runtime/workspace/side-checkouts.mjs +203 -0
  407. package/src/session-runtime/workspace/transition-runner.mjs +188 -0
  408. package/src/session-runtime/workspace/transitions.mjs +108 -1041
  409. package/src/session-runtime/workspace/workspace-merge.mjs +132 -0
  410. package/src/session-runtime/workspace/worktree-intents.mjs +242 -0
  411. package/src/session-runtime/workspace/worktree-maintenance.mjs +80 -0
  412. package/src/session-runtime/workspace/worktree-setup.mjs +85 -0
  413. package/src/session-runtime/workspace/worktree-tool.mjs +73 -56
  414. package/src/standalone/daemon-desktop-runtime.mjs +2 -0
  415. package/src/standalone/daemon-session-control.mjs +169 -138
  416. package/src/standalone/daemon.mjs +7 -0
  417. package/src/standalone/desktop-service-registry.mjs +9 -0
  418. package/src/standalone/local-session-runtime.mjs +5 -1
  419. package/src/standalone/session-client/daemon-unavailable.mjs +3 -0
  420. package/src/standalone/session-client.mjs +5 -1
  421. package/src/standalone/session-frame-stream.mjs +83 -11
  422. package/src/standalone/session-protocol.mjs +4 -0
  423. package/src/standalone/session-service/agent-tree/agent-turns.mjs +16 -12
  424. package/src/standalone/session-service/agent-tree.mjs +45 -9
  425. package/src/standalone/session-service/projection/transcript-window.mjs +700 -64
  426. package/src/standalone/session-service/session-calls/session-action.mjs +10 -3
  427. package/src/standalone/session-service/session-calls/session-views.mjs +160 -104
  428. package/src/standalone/session-service/stored-reader.mjs +127 -40
  429. package/src/standalone/session-transport/client-registry/client-grace.mjs +12 -9
  430. package/src/tui/App.jsx +154 -164
  431. package/src/tui/app/app-view/floating-panel.jsx +10 -4
  432. package/src/tui/app/app-view/text-entry-labels.mjs +1 -0
  433. package/src/tui/app/clipboard.mjs +21 -10
  434. package/src/tui/app/model-picker/route-selection/fast-parameters.mjs +1 -3
  435. package/src/tui/app/model-picker/route-selection.mjs +5 -6
  436. package/src/tui/app/prompt-handlers/use-prompt-history-nav.mjs +1 -1
  437. package/src/tui/app/prompt-handlers/use-prompt-interrupt.mjs +3 -0
  438. package/src/tui/app/prompt-submit/provider-prompts.mjs +3 -1
  439. package/src/tui/app/provider-setup/api-key-actions.mjs +31 -1
  440. package/src/tui/app/provider-setup/oauth-actions.mjs +58 -21
  441. package/src/tui/app/provider-setup/provider-items.mjs +8 -2
  442. package/src/tui/app/provider-setup-picker.mjs +29 -18
  443. package/src/tui/app/shell-layout/row-budgets.mjs +12 -10
  444. package/src/tui/app/slash-commands.mjs +1 -0
  445. package/src/tui/app/slash-dispatch/session-commands.mjs +46 -0
  446. package/src/tui/app/slash-dispatch.mjs +1 -1
  447. package/src/tui/app/use-btw-panel.mjs +29 -0
  448. package/src/tui/app/use-context-panel-refresh.mjs +31 -0
  449. package/src/tui/app/use-model-catalog-cache.mjs +13 -10
  450. package/src/tui/app/use-mouse-input.mjs +11 -0
  451. package/src/tui/app/use-onboarding-start.mjs +8 -3
  452. package/src/tui/app/use-panel-transition.mjs +10 -1
  453. package/src/tui/app/use-pasted-buffers.mjs +28 -20
  454. package/src/tui/app/use-prompt-draft-flow.mjs +7 -0
  455. package/src/tui/app/use-prompt-layout-rows.mjs +3 -3
  456. package/src/tui/app/use-prompt-queue-history.mjs +3 -2
  457. package/src/tui/app/use-selection-focus.mjs +11 -2
  458. package/src/tui/app/use-selection-geometry.mjs +18 -9
  459. package/src/tui/app/use-selection-paint.mjs +4 -4
  460. package/src/tui/app/use-selection-stitch.mjs +3 -3
  461. package/src/tui/app/use-slash-palette.mjs +62 -0
  462. package/src/tui/app/use-terminal-chrome.mjs +2 -2
  463. package/src/tui/app/use-transcript-activity.mjs +47 -32
  464. package/src/tui/app/use-transcript-follow.mjs +12 -4
  465. package/src/tui/app/use-transcript-scroll.mjs +46 -17
  466. package/src/tui/app/use-transcript-swap-reset.mjs +27 -0
  467. package/src/tui/app/use-transcript-window/geometry.mjs +19 -5
  468. package/src/tui/app/use-transcript-window/instance-state.mjs +4 -0
  469. package/src/tui/app/use-transcript-window/selection-shift.mjs +5 -1
  470. package/src/tui/app/use-transcript-window.mjs +21 -11
  471. package/src/tui/app/use-tui-ready.mjs +18 -0
  472. package/src/tui/app/use-welcome-prompt-hint.mjs +5 -0
  473. package/src/tui/app/use-workflow-tab-cycle.mjs +5 -3
  474. package/src/tui/components/Picker.jsx +27 -3
  475. package/src/tui/components/QuestionPanel.jsx +13 -4
  476. package/src/tui/components/SignInUrl.jsx +68 -0
  477. package/src/tui/components/TextEntryPanel.jsx +76 -46
  478. package/src/tui/components/sign-in-url-layout.mjs +24 -0
  479. package/src/tui/dist/index.mjs +1963 -1361
  480. package/src/tui/session/goal-turn-state.mjs +6 -2
  481. package/src/tui/session/session-api/controls.mjs +4 -0
  482. package/src/tui/session/session-api/extensions.mjs +8 -0
  483. package/src/tui/session/session-api/intake/abort.mjs +2 -1
  484. package/src/tui/session/session-api/intake.mjs +2 -0
  485. package/src/tui/session/session-api/integrations.mjs +2 -2
  486. package/src/tui/session/session-api/routes-agent-definitions.mjs +1 -0
  487. package/src/tui/session/session-api/settings.mjs +2 -2
  488. package/src/tui/session/session-flow/queue/take-entries.mjs +11 -0
  489. package/src/tui/session/transcript-spill/spill-writer.mjs +15 -0
  490. package/src/tui/session/transcript-spill.mjs +92 -4
  491. package/src/tui/session/transcript-store.mjs +2 -0
  492. package/src/ui/statusline-agents.mjs +1 -3
  493. package/src/defaults/skills/skill-creator/references/source-review.md +0 -94
@@ -278,14 +278,21 @@ function currentSkillResolver(sessionRef, { preview = false } = {}) {
278
278
  return (name) => {
279
279
  const delivery = currentSkillDelivery(name, cwd, config, skills);
280
280
  if (!delivery) return null;
281
- if (preview) return { body: delivery.body, native: undefined };
282
281
  const { resource } = delivery;
282
+ // The tools whose use counts as use of the skill: its tool dependencies,
283
+ // an MCP server as `mcp:<server>`.
284
+ const tools = (resource.toolDependencies || []).flatMap(({ type, value }) => {
285
+ if (type === 'tool') return [value];
286
+ if (type === 'mcp') return [`mcp:${value}`];
287
+ return [];
288
+ });
289
+ if (preview) return { body: delivery.body, native: undefined, tools };
283
290
  const envelope = loadSkillToolDependencies(
284
291
  buildSkillToolEnvelope(name, resource.content, resource.dir, resource),
285
292
  sessionRef
286
293
  );
287
294
  const native = parseNativeToolSearchPayload('Skill', envelope.result);
288
- return { body: delivery.body, native, summary: native?.summary ?? envelope.result };
295
+ return { body: delivery.body, native, summary: native?.summary ?? envelope.result, tools };
289
296
  };
290
297
  }
291
298
 
@@ -19,7 +19,7 @@ import { providerToolExclusionReason } from './provider-tool-exclusions.mjs';
19
19
  import { workflowOverlayDenial } from '../manager/workflow-switch-notice.mjs';
20
20
  import { agentMcpToolPermission, connectorPolicyAgentOf, projectMcpToolDisabled } from '../../mcp/agent-connectors.mjs';
21
21
 
22
- const WORKER_DENIED_TOOLS = new Set(['agent', 'ask_user', 'session']);
22
+ const WORKER_DENIED_TOOLS = new Set(['agent', 'ask_user', 'handoff_user', 'session']);
23
23
  const BRIDGE_DISCOVERY_TOOL_NAMES = new Set(['load_tool']);
24
24
 
25
25
  const IMAGE_PATH_RE = /\.(?:png|jpe?g|gif|webp)(?:$|[?#])/i;
@@ -31,6 +31,13 @@ function exclusionsFor(sessionRef) {
31
31
 
32
32
  const isSessionRef = (value) => value !== null && typeof value === 'object';
33
33
 
34
+ // A schema 400 drop holds for the schema rule that refused it: a route on
35
+ // another wire family (a fallback hop, a model switch) sends the tool again.
36
+ function droppedOnRoute(sessionRef, name) {
37
+ const entry = isSessionRef(sessionRef) ? sessions.get(sessionRef)?.dropped.get(String(name || '')) : null;
38
+ return entry && entry.family === routeSchemaFamily(sessionRef) ? entry : null;
39
+ }
40
+
34
41
  /** A new provider request: exclusions describe the latest request only. */
35
42
  export function beginProviderToolExclusions(sessionRef) {
36
43
  if (isSessionRef(sessionRef)) sessions.get(sessionRef)?.current.clear();
@@ -64,15 +71,16 @@ export function providerToolExclusionReason(sessionRef, name) {
64
71
  if (!isSessionRef(sessionRef)) return null;
65
72
  const state = sessions.get(sessionRef);
66
73
  const key = String(name || '');
67
- const exclusion = state?.current.get(key) || state?.dropped.get(key);
74
+ const exclusion = state?.current.get(key) || droppedOnRoute(sessionRef, key);
68
75
  return exclusion ? unavailableText(exclusion) : null;
69
76
  }
70
77
 
71
78
  /** The provider rejected `exclusion.name`'s schema with an HTTP 400: leave the
72
- * tool out of every later request of the session and tell the user once. */
79
+ * tool out of the session's later requests on this wire family and tell the
80
+ * user once. */
73
81
  export function dropSessionTool(sessionRef, exclusion, onStageChange) {
74
82
  if (!isSessionRef(sessionRef)) return;
75
- exclusionsFor(sessionRef).dropped.set(exclusion.name, exclusion);
83
+ exclusionsFor(sessionRef).dropped.set(exclusion.name, { ...exclusion, family: routeSchemaFamily(sessionRef) });
76
84
  recordProviderToolExclusion(sessionRef, exclusion, onStageChange);
77
85
  }
78
86
 
@@ -82,7 +90,7 @@ export function withoutDroppedTools(sessionRef, { sendTools, requestToolScope })
82
90
  const dropped = isSessionRef(sessionRef) ? sessions.get(sessionRef)?.dropped : null;
83
91
  if (!dropped?.size) return { sendTools, requestToolScope };
84
92
  const prefix = providerNativeToolPrefixCount(sendTools);
85
- const kept = sendTools.filter((tool, index) => index < prefix || !dropped.has(tool?.name));
93
+ const kept = sendTools.filter((tool, index) => index < prefix || !droppedOnRoute(sessionRef, tool?.name));
86
94
  if (kept.length === sendTools.length) return { sendTools, requestToolScope };
87
95
  const next = finalizeProviderRequestTools(kept, prefix);
88
96
  return { sendTools: next, requestToolScope: { ...requestToolScope, requestTools: next } };
@@ -128,7 +136,7 @@ function schemaExclusion(session, family, tool) {
128
136
  * tools are never excluded by the schema rule. */
129
137
  export function routeToolExclusionReason(session, tool) {
130
138
  if (!tool) return null;
131
- const dropped = isSessionRef(session) ? sessions.get(session)?.dropped.get(String(tool.name || '')) : null;
139
+ const dropped = droppedOnRoute(session, tool.name);
132
140
  if (dropped) return unavailableText(dropped);
133
141
  const exclusion = schemaExclusion(session, routeSchemaFamily(session), tool);
134
142
  return exclusion ? unavailableText(exclusion) : null;
@@ -17,6 +17,8 @@ import { buildRequestSendOptions } from '../manager/ask-turn-loop-options.mjs';
17
17
  import { prepareTurnEffortConfiguration } from '../../providers/effort-configuration.mjs';
18
18
  import { resolveSessionContextMeta } from '../manager/context-meta.mjs';
19
19
  import { stripProviderReplayMessages } from '../manager/provider-replay.mjs';
20
+ import { isAgentOwner } from '../../agent-owner.mjs';
21
+ import { rebuildToolSurfaceForProviderWithMigration } from './tool-exposure-migration.mjs';
20
22
 
21
23
  // The short reason a notice gives for leaving a model, by provider kind.
22
24
  const HOP_REASONS = {
@@ -149,6 +151,11 @@ export async function advanceAgentRoute(state, error) {
149
151
  updated.routeFallbackFailures = [...(session.routeFallbackFailures || []), failed];
150
152
  await pinSessionWireRoute(updated, provider);
151
153
  state.throwIfAborted();
154
+ // The route change replaced the session's tools with its whole resolved list.
155
+ // A delegated agent's surface was already re-applied by updateSessionRoute
156
+ // (applyAgentToolExposure); a Lead-class session gets its deferred surface
157
+ // restored for the new provider, as a manual model switch does.
158
+ if (!isAgentOwner(updated)) rebuildToolSurfaceForProviderWithMigration(updated, next.provider, state.messages);
152
159
  // The live turn (prompt, assistant tool calls, tool results) carries on;
153
160
  // only the previous provider's native replay state is dropped from it.
154
161
  state.messages.splice(0, state.messages.length, ...stripProviderReplayMessages(state.messages));
@@ -164,6 +171,13 @@ export async function advanceAgentRoute(state, error) {
164
171
  state.cacheBreakTraceKeys.clear();
165
172
  state.transportRetriesUsed = 0;
166
173
  state.transportRetryMax = 0;
174
+ // The one-shot recoveries the left model spent belong to its requests; the
175
+ // next model's first request gets its own (its window, images and schemas).
176
+ state.contextOverflowRetryUsed = false;
177
+ state.imageStripUsed = false;
178
+ state.imageStrip = null;
179
+ state.thinkingReplayRepairUsed = false;
180
+ state.toolSchemaDropUsed = false;
167
181
  for (const key of Object.keys(oldOptions)) delete state.opts[key];
168
182
  Object.assign(
169
183
  state.opts,
@@ -4,7 +4,12 @@
4
4
  * consume a retry verdict, and settle a completed send into the loop state.
5
5
  */
6
6
  import { traceCacheBreak } from '../../cache-break-trace.mjs';
7
- import { anthropicPrefixChanges } from '../../providers/lib/anthropic-prefix-trace.mjs';
7
+ import {
8
+ anthropicPrefixChanges,
9
+ latestAnthropicPrefix,
10
+ persistableAnthropicPrefix,
11
+ seedAnthropicPrefix,
12
+ } from '../../providers/lib/anthropic-prefix-trace.mjs';
8
13
  import { resolveLiveToolCwd } from './tool-exec.mjs';
9
14
  import { notifySkillsApplied, prepareExplicitSkills, recordAppliedSkills } from '../explicit-skills.mjs';
10
15
  import { addUsage, usageDeltaEvent } from './usage.mjs';
@@ -179,6 +184,9 @@ export async function sendProviderRequest(state, round) {
179
184
  } catch {
180
185
  // the send-started observer is advisory
181
186
  }
187
+ // Diagnostic only: a restarted process compares its first request with the
188
+ // last prefix fingerprint the session record kept (anthropic-prefix-trace.mjs).
189
+ seedAnthropicPrefix(sessionId, sessionRef?.providerPrefixBaseline);
182
190
  // The request a /btw side question forks: kept while it is in flight, with
183
191
  // its reply once it proceeds, and dropped again if it fails.
184
192
  const sideQuestion = captureSideQuestionRequest({
@@ -374,6 +382,10 @@ function traceActualCacheMiss(state, sent, response, { previousGuardState, previ
374
382
  );
375
383
  }
376
384
  sessionRef.lastProviderCachedPrefixTokens = cachePrefixTokens;
385
+ // The fingerprint of this request survives a restart on the record, so the
386
+ // next process's first miss can still name what moved.
387
+ const fingerprint = persistableAnthropicPrefix(latestAnthropicPrefix(sessionId));
388
+ if (fingerprint) sessionRef.providerPrefixBaseline = fingerprint;
377
389
  }
378
390
 
379
391
  /** Fold a completed send into the state: prefix guard, image-strip
@@ -21,9 +21,20 @@ const isArgsObject = (value) => value && typeof value === 'object' && !Array.isA
21
21
 
22
22
  async function askApproval(
23
23
  call,
24
- { callerSessionId, executeOpts, toolApprovalHook, decision, request = null, gate = null, onApproved = null }
24
+ {
25
+ callerSessionId,
26
+ executeOpts,
27
+ toolApprovalHook,
28
+ decision,
29
+ request = null,
30
+ gate = null,
31
+ onApproved = null,
32
+ account = '',
33
+ }
25
34
  ) {
26
35
  const askReason = String(decision?.reason || 'approval requested by hook').trim();
36
+ // The connector account the call is bound to rides on the card.
37
+ if (account) request = { ...(request || {}), account };
27
38
  // While the user decides, the session is waiting on them, not working.
28
39
  const releaseApprovalWait = beginSessionApprovalWait(callerSessionId);
29
40
  let askOutcome;
@@ -72,9 +83,13 @@ export const CONNECTOR_APPROVAL_REASON = 'This connector tool is set to ask befo
72
83
  * remembered outbound grant answered it) is not asked a second time. With the
73
84
  * "Allow all actions" developer option on, `needs_approval` is not asked
74
85
  * either (a payment or permanent delete was asked by the outbound gate above).
86
+ * `account` is the connector account the call is bound to, for the card.
75
87
  * @returns {Promise<{ denial: string } | null>} the denial, or null to run
76
88
  */
77
- export function applyConnectorApproval(call, { agentId, callerSessionId, executeOpts, toolApprovalHook }) {
89
+ export function applyConnectorApproval(
90
+ call,
91
+ { agentId, callerSessionId, executeOpts, toolApprovalHook, account = '' }
92
+ ) {
78
93
  if (call.outboundApproved) return null;
79
94
  if (!actingBotId(callerSessionId)) return null;
80
95
  if (agentMcpToolPermission(agentId, call.name) !== 'needs_approval') return null;
@@ -89,6 +104,7 @@ export function applyConnectorApproval(call, { agentId, callerSessionId, execute
89
104
  toolApprovalHook,
90
105
  decision: { reason: CONNECTOR_APPROVAL_REASON },
91
106
  request: rememberable ? { kind: 'approval', agentId: botId, remember: ['once', 'agent'] } : null,
107
+ account,
92
108
  onApproved: async (approval) => {
93
109
  if (!rememberable || approval?.remember !== 'agent') return;
94
110
  try {
@@ -130,10 +146,11 @@ export async function applyBeforeToolHook({
130
146
  beforeToolHook,
131
147
  toolApprovalHook,
132
148
  mcpScopeId = null,
149
+ account = '',
133
150
  }) {
134
151
  const call = { name, args, cwd };
135
- // ask_user is itself the user prompt; it never goes through the ordinary approval gate.
136
- if (name === 'ask_user') return call;
152
+ // ask_user and handoff_user are themselves the user prompt; they never go through the ordinary approval gate.
153
+ if (name === 'ask_user' || name === 'handoff_user') return call;
137
154
  try {
138
155
  const hookDecision = beforeToolHook
139
156
  ? await runAbortable(executeOpts.signal, () =>
@@ -183,6 +200,8 @@ export async function applyBeforeToolHook({
183
200
  decision,
184
201
  request,
185
202
  gate,
203
+ // Only while the call is still the one bound to it.
204
+ account: call.name === name ? account : '',
186
205
  });
187
206
  if (denied) return denied;
188
207
  }
@@ -67,6 +67,8 @@ function dispatchMcp(name, args, { cwd, callerSessionId, sessionRef, executeOpts
67
67
  signal: executeOpts.signal || null,
68
68
  ownerKey: callerSessionId,
69
69
  scopeId: mcpScopeId,
70
+ // A connector account defaults to (and becomes) this conversation's last used.
71
+ conversation: callerSessionId,
70
72
  });
71
73
  if (!isBuiltinMailRead(name, mcpScopeId)) return call;
72
74
  // The runtime's own record of the links in a mail it read for this session:
@@ -5,7 +5,7 @@
5
5
  // tool-exec/dispatch.mjs — executor per tool family
6
6
  // tool-exec/after-hook.mjs — PostToolUse result override
7
7
  // tool-exec/read-only-deadline.mjs — bounded read-only I/O with partial-output grace
8
- import { isMcpTool } from '../../mcp/client.mjs';
8
+ import { isMcpTool, resolveMcpToolAccount } from '../../mcp/client.mjs';
9
9
  import { connectorPolicyAgentOf } from '../../mcp/agent-connectors.mjs';
10
10
  import { prepareDeferredToolCallThrough } from './deferred-call-through.mjs';
11
11
  import { refreshDeferredMcpToolCatalog } from '../../runtime-core/tool-catalog.mjs';
@@ -58,6 +58,24 @@ async function executeToolOwned(name, args, cwd, callerSessionId, sessionRef, ex
58
58
  callArgs = guarded.args;
59
59
  completionToolOpts.shellGuardNotes = guarded.notes;
60
60
  }
61
+ // A connector call is bound to its account before any hook or card sees it,
62
+ // so an approval approves that account, whatever is used last meanwhile.
63
+ // It is bound by its unique name; `account` is its label, for display.
64
+ let bound = '';
65
+ let account = '';
66
+ if (isMcpTool(name)) {
67
+ try {
68
+ const chosen = resolveMcpToolAccount(name, callArgs, {
69
+ scopeId: sessionRef?.mcpScopeId || null,
70
+ conversation: callerSessionId,
71
+ });
72
+ bound = chosen?.name || '';
73
+ account = chosen?.label || '';
74
+ } catch (error) {
75
+ return `Error: ${error?.message || String(error)}`;
76
+ }
77
+ if (bound) callArgs = { ...callArgs, account: bound };
78
+ }
61
79
  const hooked = await applyBeforeToolHook({
62
80
  name,
63
81
  args: callArgs,
@@ -67,6 +85,7 @@ async function executeToolOwned(name, args, cwd, callerSessionId, sessionRef, ex
67
85
  beforeToolHook: hookFrom(executeOpts, sessionRef, 'beforeToolHook'),
68
86
  toolApprovalHook,
69
87
  mcpScopeId: sessionRef?.mcpScopeId || null,
88
+ account,
70
89
  });
71
90
  if (hooked.denial) return hooked.denial;
72
91
  const connectorDenied = await applyConnectorApproval(hooked, {
@@ -74,8 +93,11 @@ async function executeToolOwned(name, args, cwd, callerSessionId, sessionRef, ex
74
93
  callerSessionId,
75
94
  executeOpts,
76
95
  toolApprovalHook,
96
+ account: hooked.name === name ? account : '',
77
97
  });
78
98
  if (connectorDenied) return connectorDenied.denial;
99
+ // A hook's rewrite of the same tool keeps the bound account.
100
+ if (bound && hooked.name === name) hooked.args = { ...hooked.args, account: bound };
79
101
  // A decision can settle after its turn was cancelled. It must never
80
102
  // authorize a new mutation, even if the hook ignores the abort signal.
81
103
  throwIfAborted(executeOpts.signal);
@@ -46,12 +46,44 @@ export function markToolSurfaceMigrated(session, intent = TOOL_SURFACE_MIGRATION
46
46
  session.pendingCacheBreakIntent = intent;
47
47
  }
48
48
 
49
- /** Provider switch: rebuild the surface for `provider`, migrating bridge ↔ native. */
50
- export function rebuildToolSurfaceForProviderWithMigration(session, provider) {
49
+ /**
50
+ * Run `apply` with `messages` (the transcript a running turn works on) as the
51
+ * session's messages, so a fixed-guidance rewrite lands there; the canonical
52
+ * transcript takes the rewritten system prefix. Without `messages`, `apply`
53
+ * runs on the session's own. `apply` runs synchronously.
54
+ */
55
+ function applyOnWorkingMessages(session, messages, apply) {
56
+ const stored = session.messages;
57
+ const working = Array.isArray(messages) ? messages : stored;
58
+ if (Array.isArray(working)) session.messages = working;
59
+ try {
60
+ apply();
61
+ } finally {
62
+ if (Array.isArray(working) && session.messages !== working) {
63
+ // The rewrite may drop an emptied system message: keep the array
64
+ // identity the request loop holds.
65
+ working.splice(0, working.length, ...session.messages);
66
+ }
67
+ session.messages = stored;
68
+ }
69
+ // The rewritten prompt messages are fresh objects; the canonical transcript
70
+ // the save path persists takes them too, so the next save writes the new
71
+ // prompt together with the new exposure.
72
+ if (Array.isArray(stored) && Array.isArray(working) && stored !== working) syncSystemPrefix(stored, working);
73
+ }
74
+
75
+ /**
76
+ * Provider switch: rebuild the surface for `provider`, migrating bridge ↔
77
+ * native. `messages`: the running turn's transcript, when a switch happens
78
+ * mid-turn (a route fallback hop).
79
+ */
80
+ export function rebuildToolSurfaceForProviderWithMigration(session, provider, messages) {
51
81
  if (!session) return false;
52
82
  const migrating = toolExposureMigrationNeeded(session, provider);
53
- rebuildDeferredToolSurfaceForProvider(session, provider);
54
- if (migrating) markToolSurfaceMigrated(session);
83
+ applyOnWorkingMessages(session, messages, () => {
84
+ rebuildDeferredToolSurfaceForProvider(session, provider);
85
+ if (migrating) markToolSurfaceMigrated(session);
86
+ });
55
87
  return migrating;
56
88
  }
57
89
 
@@ -75,26 +107,12 @@ function hasUnansweredToolCalls(messages) {
75
107
  */
76
108
  export function migrateToolExposureAtBoundary(session, messages) {
77
109
  if (!session || !toolExposureMigrationNeeded(session) || hasUnansweredToolCalls(messages)) return false;
78
- const stored = session.messages;
79
- const working = Array.isArray(messages) ? messages : stored;
80
- if (Array.isArray(working)) session.messages = working;
81
- try {
110
+ applyOnWorkingMessages(session, messages, () =>
82
111
  applyDeferredToolSurface(session, session.deferredSurfaceMode || 'lead', [], {
83
112
  provider: session.provider,
84
113
  model: session.model,
85
- });
86
- } finally {
87
- if (Array.isArray(working) && session.messages !== working) {
88
- // The rewrite may drop an emptied system message: keep the request
89
- // transcript array identity the loop holds.
90
- working.splice(0, working.length, ...session.messages);
91
- }
92
- session.messages = stored;
93
- }
94
- // The rewritten prompt messages are fresh objects; the canonical transcript
95
- // the save path persists takes them too, so the next save writes the new
96
- // prompt together with the new exposure.
97
- if (Array.isArray(stored) && Array.isArray(working) && stored !== working) syncSystemPrefix(stored, working);
114
+ })
115
+ );
98
116
  invalidateForToolSurfaceMigration(session);
99
117
  return true;
100
118
  }
@@ -1,12 +1,14 @@
1
1
  /**
2
2
  * turn-limits.mjs — a turn's caps (sendOpts.turnLimits; a heartbeat turn's,
3
- * manager/ask-turn-loop-options.mjs): `maxToolCalls` tool calls and
4
- * `wrapUpAt` (epoch ms). A call past the cap, or one made after the wrap-up
5
- * time, is answered without running. Once a cap is reached the model is told
6
- * to reply, and every later request of the turn forbids tool use
7
- * (toolChoice 'none'). A response that still calls tools after that ends the
8
- * turn with no reply, its calls answered unrun. A capped turn runs no call
9
- * eagerly (eager-dispatch.mjs), so none past the cap starts while streaming.
3
+ * manager/ask-turn-loop-options.mjs): `maxToolCalls` tool calls,
4
+ * `wrapUpAt` (epoch ms) and `maxToolRounds` tool-calling responses (an
5
+ * explorer's, runtime/explore/explorer.mjs). A call past the cap, or one made
6
+ * after the wrap-up time, is answered without running. Once a cap is reached
7
+ * the model is told to reply, and every later request of the turn forbids
8
+ * tool use (toolChoice 'none'). A response that still calls tools after that
9
+ * ends the turn with no reply, its calls answered unrun. A turn capped by
10
+ * calls or time runs no call eagerly (eager-dispatch.mjs), so none past the
11
+ * cap starts while streaming; a round cap cuts only between rounds.
10
12
  */
11
13
 
12
14
  const NOTICE =
@@ -21,6 +23,8 @@ export function turnLimitsOf(opts) {
21
23
  return {
22
24
  maxToolCalls: Number.isInteger(limits.maxToolCalls) && limits.maxToolCalls >= 0 ? limits.maxToolCalls : Infinity,
23
25
  wrapUpAt: Number.isFinite(limits.wrapUpAt) ? limits.wrapUpAt : Infinity,
26
+ maxToolRounds:
27
+ Number.isInteger(limits.maxToolRounds) && limits.maxToolRounds >= 0 ? limits.maxToolRounds : Infinity,
24
28
  };
25
29
  }
26
30
 
@@ -49,7 +53,8 @@ export function settleTurnLimits(state, overrun, now = Date.now()) {
49
53
  }
50
54
  const limits = turnLimitsOf(state.opts);
51
55
  if (!limits || state.turnLimitNoticed) return;
52
- if (state.toolCallsTotal < limits.maxToolCalls && now < limits.wrapUpAt) return;
56
+ if (state.toolCallsTotal < limits.maxToolCalls && now < limits.wrapUpAt && state.iterations < limits.maxToolRounds)
57
+ return;
53
58
  state.turnLimitNoticed = true;
54
59
  state.messages.push({ role: 'user', content: NOTICE, meta: { source: 'turn-limit' } });
55
60
  }
@@ -19,6 +19,7 @@ import { armTurnDurability, claimTurnRuntime } from './ask-turn-open.mjs';
19
19
  import { prepareAskTurn, runAskAgentLoop } from './ask-turn-execute.mjs';
20
20
  import { commitAskTurn, publishAskTurn } from './ask-turn-settle.mjs';
21
21
  import { finalizeAskTurnFailure } from './ask-turn-failure.mjs';
22
+ import { armTurnTimeoutOnProviderError, reapTurnShellJobs } from './turn-reap.mjs';
22
23
  import { cacheWarmTurnKind, endCacheWarmTurn, failedTurnProvenance, noteCacheWarmTurn } from '../cache-warm.mjs';
23
24
 
24
25
  export { _api_call_with_interrupt } from './ask-call-interrupt.mjs';
@@ -125,6 +126,9 @@ async function runAskTurn({ sessionId, opened, input, askOpts, onToolCall, cwdOv
125
126
  });
126
127
  return { result: terminalResultPreview, drained };
127
128
  } catch (err) {
129
+ // The turn's terminal error: a provider timeout/stall that ended it (a
130
+ // retry the turn recovered from never reaches here) stops its shell work.
131
+ armTurnTimeoutOnProviderError(sessionId, opened.runtime, opened.turnSignal, err);
128
132
  await finalizeAskTurnFailure({
129
133
  sessionId,
130
134
  err,
@@ -138,12 +142,49 @@ async function runAskTurn({ sessionId, opened, input, askOpts, onToolCall, cwdOv
138
142
  transcriptMeta: transcript.raw,
139
143
  askStartedAt,
140
144
  });
145
+ reapTurnShellJobs(sessionId, opened.runtime);
141
146
  throw err;
142
147
  } finally {
143
- releaseWorkspace?.();
148
+ // The result is already published; this only holds the session lock until
149
+ // the reader lease is removed, so the next turn's admission follows it. A
150
+ // failed release (the admission thread stopped) never replaces the turn's
151
+ // own outcome.
152
+ try {
153
+ await releaseWorkspace?.();
154
+ } catch {
155
+ // Intentionally ignored: see the comment above.
156
+ }
144
157
  }
145
158
  }
146
159
 
160
+ /** The prompt-cache warmer learns idle gaps from real turns only; a retry is
161
+ * classified by the failed turn's stored provenance. The rebuilt retry
162
+ * message keeps that provenance (ask-turn-execute.mjs). Returns the warmer's
163
+ * turn handle. */
164
+ function noteTurnForCacheWarm(sessionId, claimed, input) {
165
+ const retryProvenance = failedTurnProvenance(claimed.preSession?.messages, input);
166
+ if (retryProvenance) input.retryProvenance = retryProvenance;
167
+ return noteCacheWarmTurn(sessionId, cacheWarmTurnKind(input, claimed.preSession?.messages));
168
+ }
169
+
170
+ /** Clear the controller only if it's still ours (closeSession may have
171
+ * swapped it). The rest of the runtime entry stays intact so agent type=list
172
+ * can still surface the final stage (done/error/cancelling). */
173
+ function detachAskRuntime(sessionId, askGeneration) {
174
+ const entry = _getRuntimeEntry(sessionId);
175
+ if (entry && entry.generation === askGeneration) {
176
+ _unlinkParentAbortListener(entry);
177
+ entry.controller = null;
178
+ // Detach the live session reference; ask is over.
179
+ entry.session = null;
180
+ }
181
+ // Final-stage runtime diagnostics are useful only while the turn is
182
+ // unwinding. Once its controller is detached, retaining the full entry
183
+ // (and any accidental references hanging from it) for the host lifetime
184
+ // turns one-shot agent traffic into an unbounded manager Map.
185
+ _evictTerminalSessionRuntime(sessionId);
186
+ }
187
+
147
188
  export async function askSession(sessionId, prompt, context, onToolCall, cwdOverride, explicitPrefetch, askOpts = {}) {
148
189
  const askStartedAt = Date.now();
149
190
  const transcript = createAskTranscriptMeta(askOpts?.transcriptMeta);
@@ -180,8 +221,9 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
180
221
  // Hoisted so the outer finally (which runs once after the whole turn loop)
181
222
  // can compare against the last turn's generation.
182
223
  let askGeneration = 0;
183
- let crashRecoveryChecked = false;
184
- let sessionStartEmitted = false;
224
+ // Crash-checkpoint recovery and the session-start event belong to the first
225
+ // turn of this ask only.
226
+ let firstTurn = true;
185
227
  // A failed-turn retry rewinds only the turn it retries, never a drained
186
228
  // follow-up turn.
187
229
  let retryFailedTurn = askOpts.retryFailedTurn === true;
@@ -218,17 +260,11 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
218
260
  const claimed = claimTurnRuntime({
219
261
  sessionId,
220
262
  askOpts,
221
- recoverCheckpoint: !crashRecoveryChecked,
222
- emitStart: !sessionStartEmitted,
263
+ recoverCheckpoint: firstTurn,
264
+ emitStart: firstTurn,
223
265
  });
224
- // The prompt-cache warmer learns idle gaps from real turns only; a
225
- // retry is classified by the failed turn's stored provenance.
226
- // The rebuilt retry message keeps that provenance (ask-turn-execute.mjs).
227
- const retryProvenance = failedTurnProvenance(claimed.preSession?.messages, input);
228
- if (retryProvenance) input.retryProvenance = retryProvenance;
229
- warmTurn = noteCacheWarmTurn(sessionId, cacheWarmTurnKind(input, claimed.preSession?.messages));
230
- crashRecoveryChecked = true;
231
- sessionStartEmitted = true;
266
+ warmTurn = noteTurnForCacheWarm(sessionId, claimed, input);
267
+ firstTurn = false;
232
268
  askGeneration = claimed.askGeneration;
233
269
  const opened = {
234
270
  ...claimed,
@@ -287,21 +323,7 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
287
323
  queue.releaseUnstarted();
288
324
  // A thrown setup/provider path must never leave a delayed checkpoint
289
325
  // writer alive after the turn lock is released.
290
- // Clear the controller only if it's still ours (closeSession may have
291
- // swapped it). Leave the rest of the runtime entry intact so agent type=list
292
- // can still surface the final stage (done/error/cancelling).
293
- const entry = _getRuntimeEntry(sessionId);
294
- if (entry && entry.generation === askGeneration) {
295
- _unlinkParentAbortListener(entry);
296
- entry.controller = null;
297
- // Detach the live session reference; ask is over.
298
- entry.session = null;
299
- }
300
- // Final-stage runtime diagnostics are useful only while the turn is
301
- // unwinding. Once its controller is detached, retaining the full entry
302
- // (and any accidental references hanging from it) for the host lifetime
303
- // turns one-shot agent traffic into an unbounded manager Map.
304
- _evictTerminalSessionRuntime(sessionId);
326
+ detachAskRuntime(sessionId, askGeneration);
305
327
  unlock();
306
328
  }
307
329
  }
@@ -231,9 +231,9 @@ export function buildAgentLoopOptions({
231
231
  },
232
232
  onStreamDelta: (kind = 'semantic') => {
233
233
  markSessionStreamDelta(sessionId, kind).catch(() => {});
234
- // Raw transport is an internal health signal, not model progress.
235
- // Preserve the public callback's historical semantic-only contract.
236
- if (kind !== 'transport') relay(askOpts?.onStreamDelta, kind);
234
+ // Raw transport and keepalives are internal health signals, not model
235
+ // progress. Preserve the public callback's historical semantic-only contract.
236
+ if (kind !== 'transport' && kind !== 'keepalive') relay(askOpts?.onStreamDelta, kind);
237
237
  },
238
238
  };
239
239
  }
@@ -225,7 +225,9 @@ export function publishAskTurn({ sessionId, opened, terminalResultPreview, askOp
225
225
  // (which inspects entry.emptyFinal first) classifies the
226
226
  // terminal state correctly even if it ticks during unwind.
227
227
  const isEmptyFinal =
228
- !terminalResultPreview.content && !terminalResultPreview.reasoningContent && !terminalResultPreview.reactionCompleted;
228
+ !terminalResultPreview.content &&
229
+ !terminalResultPreview.reasoningContent &&
230
+ !terminalResultPreview.reactionCompleted;
229
231
  if (isEmptyFinal) {
230
232
  markSessionEmptyFinal(sessionId);
231
233
  }
@@ -13,7 +13,7 @@
13
13
  import { loadConfig } from '../../config.mjs';
14
14
  import { findPreset } from '../../runtime-core/config-helpers.mjs';
15
15
  import { normalizeWorkflowRoute } from '../../runtime-core/workflow.mjs';
16
- import { canonicalRouteProvider } from '../../../../shared/model-route-chain.mjs';
16
+ import { canonicalRouteProvider, executionFallbacks } from '../../../../shared/model-route-chain.mjs';
17
17
  import { storedBotRoute } from '../../../../shared/agent-store.mjs';
18
18
  import { configuredAgentRouteCandidates } from '../../../../shared/agent-route-config.mjs';
19
19
  import { CODING_MODE } from '../agent-mode.mjs';
@@ -48,5 +48,5 @@ export function ownerConfiguredRoute({ sessionMode, agentId, config = null }) {
48
48
  export function ownerRouteFallbacks({ sessionMode, agentId, provider, model, config = null }) {
49
49
  const route = ownerConfiguredRoute({ sessionMode, agentId, config });
50
50
  if (!route?.fallbacks?.length) return [];
51
- return sameRouteModel(route, { provider, model }) ? route.fallbacks : [];
51
+ return sameRouteModel(route, { provider, model }) ? executionFallbacks(route) : [];
52
52
  }
@@ -578,6 +578,7 @@ function isReferenceFilesMessage(message) {
578
578
  }
579
579
 
580
580
  export { isProtectedContextUserMessage };
581
+ export { BP3_PART_SEPARATOR, bp3SystemMessage, bpEnvSystemMessage, replaceSystemMessageContent };
581
582
 
582
583
  // Compact summary messages (role:'user', content startsWith SUMMARY_PREFIX)
583
584
  // are synthetic anchors, not a real human turn — they must not count as
@@ -12,6 +12,7 @@
12
12
  import { findPreset } from '../../runtime-core/config-helpers.mjs';
13
13
  import { normalizeWorkflowRoute } from '../../runtime-core/workflow.mjs';
14
14
  import { configuredAgentRouteCandidates } from '../../../../shared/agent-route-config.mjs';
15
+ import { executionFallbacks } from '../../../../shared/model-route-chain.mjs';
15
16
  import { CODING_MODE, resolveSessionMode } from '../agent-mode.mjs';
16
17
  import { _liveSessions } from '../store/live-state.mjs';
17
18
  import { ownerConfiguredRoute, sameRouteModel } from './owner-route-fallbacks.mjs';
@@ -64,7 +65,7 @@ export function liveRouteFallbacks(session, config) {
64
65
  if (!source)
65
66
  return { chain: Array.isArray(session?.routeFallbacks) ? session.routeFallbacks : [], fromSettings: false };
66
67
  const onSourcePrimary = sameRouteModel(source, sessionRoutePrimary(session));
67
- return { chain: onSourcePrimary ? source.fallbacks || [] : [], fromSettings: true };
68
+ return { chain: onSourcePrimary ? executionFallbacks(source) : [], fromSettings: true };
68
69
  }
69
70
 
70
71
  /** Index in `chain` of the next route to try: the first after the session's