mixdog 1.3.4 → 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 (472) hide show
  1. package/LICENSES/oh-my-pi-MIT.txt +23 -0
  2. package/NOTICE.md +17 -2
  3. package/README.md +11 -0
  4. package/package.json +3 -11
  5. package/scripts/daemon-stub.mjs +1 -1
  6. package/scripts/embed-google-oauth-client.mjs +49 -0
  7. package/scripts/gate-local.mjs +4 -2
  8. package/scripts/lib/test-spawn-ledger.mjs +46 -1
  9. package/scripts/native-release-status.mjs +2 -2
  10. package/scripts/recall-history-cases.json +1 -1
  11. package/scripts/suite-health.mjs +10 -1
  12. package/scripts/test.mjs +12 -11
  13. package/src/agents/reviewer/AGENT.md +7 -11
  14. package/src/agents/reviewer/agent.json +1 -1
  15. package/src/defaults/shipped-history.json +25 -1
  16. package/src/defaults/skills/browser-use/SKILL.md +77 -17
  17. package/src/defaults/skills/code-tidy/SKILL.md +142 -270
  18. package/src/defaults/skills/code-tidy/references/agent-cleanup.md +4 -4
  19. package/src/defaults/skills/code-tidy/references/cleanup-report.md +31 -76
  20. package/src/defaults/skills/code-tidy/references/dead-code.md +6 -12
  21. package/src/defaults/skills/computer-use/SKILL.md +33 -9
  22. package/src/defaults/skills/setup/SKILL.md +29 -2
  23. package/src/defaults/skills/setup/references/actions.md +117 -28
  24. package/src/defaults/skills/setup/references/surfaces.md +77 -35
  25. package/src/defaults/skills/setup-guide/SKILL.md +3 -3
  26. package/src/defaults/skills/visualize/SKILL.md +2 -1
  27. package/src/lib/keychain-cjs.cjs +359 -29
  28. package/src/lib/rules-builder.cjs +10 -8
  29. package/src/rules/agent/AGENT.md +6 -6
  30. package/src/rules/agent-mode/AGENT_MODE.md +10 -2
  31. package/src/rules/agent-mode/MIXY.md +12 -0
  32. package/src/rules/lead/LEAD.md +18 -20
  33. package/src/rules/shared/05-tool-calls.md +5 -6
  34. package/src/rules/shared/10-tool-workflow.md +2 -0
  35. package/src/rules/shared/60-verification.md +7 -5
  36. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -3
  37. package/src/runtime/agent/orchestrator/agent-runtime/heartbeat-completion.mjs +193 -0
  38. package/src/runtime/agent/orchestrator/agent-runtime/title-completion.mjs +3 -1
  39. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +3 -1
  40. package/src/runtime/agent/orchestrator/config.mjs +34 -0
  41. package/src/runtime/agent/orchestrator/context/collect.mjs +3 -0
  42. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +72 -67
  43. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +54 -1
  44. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +3 -3
  45. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +177 -73
  46. package/src/runtime/agent/orchestrator/mcp/client.mjs +25 -2
  47. package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +31 -2
  48. package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +16 -3
  49. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +42 -7
  50. package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +21 -2
  51. package/src/runtime/agent/orchestrator/mcp/oauth-shared.mjs +23 -0
  52. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +11 -1
  53. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +230 -137
  54. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +64 -7
  55. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +18 -5
  56. package/src/runtime/agent/orchestrator/providers/anthropic-cache-warm.mjs +165 -0
  57. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  58. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +4 -1
  59. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +14 -14
  60. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +160 -69
  61. package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +25 -3
  62. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +64 -33
  63. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +135 -87
  64. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +52 -8
  65. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +10 -1
  66. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +22 -6
  67. package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +15 -4
  68. package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +17 -2
  69. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +142 -104
  70. package/src/runtime/agent/orchestrator/providers/cache-warm-admission.mjs +90 -0
  71. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
  72. package/src/runtime/agent/orchestrator/providers/cursor-wire-normalization.mjs +10 -4
  73. package/src/runtime/agent/orchestrator/providers/cursor-wire-protobuf.mjs +10 -217
  74. package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +25 -9
  75. package/src/runtime/agent/orchestrator/providers/cursor.mjs +22 -6
  76. package/src/runtime/agent/orchestrator/providers/devin-auth.mjs +409 -0
  77. package/src/runtime/agent/orchestrator/providers/devin-wire-families.mjs +187 -0
  78. package/src/runtime/agent/orchestrator/providers/devin-wire-identity.mjs +342 -0
  79. package/src/runtime/agent/orchestrator/providers/devin-wire-models.mjs +541 -0
  80. package/src/runtime/agent/orchestrator/providers/devin-wire-protobuf.mjs +305 -0
  81. package/src/runtime/agent/orchestrator/providers/devin-wire-request.mjs +558 -0
  82. package/src/runtime/agent/orchestrator/providers/devin-wire-schema.mjs +880 -0
  83. package/src/runtime/agent/orchestrator/providers/devin-wire-session.mjs +190 -0
  84. package/src/runtime/agent/orchestrator/providers/devin-wire-tool-args.mjs +351 -0
  85. package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +560 -0
  86. package/src/runtime/agent/orchestrator/providers/devin.mjs +491 -0
  87. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +3 -0
  88. package/src/runtime/agent/orchestrator/providers/gemini-output-limit.mjs +46 -0
  89. package/src/runtime/agent/orchestrator/providers/gemini-reasoning-relay.mjs +30 -0
  90. package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +7 -11
  91. package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +61 -22
  92. package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +1 -0
  93. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +71 -4
  94. package/src/runtime/agent/orchestrator/providers/gemini.mjs +25 -2
  95. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  96. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +96 -5
  97. package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +17 -3
  98. package/src/runtime/agent/orchestrator/providers/lib/tail-reminder-split.mjs +23 -5
  99. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +52 -10
  100. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +8 -1
  101. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
  102. package/src/runtime/agent/orchestrator/providers/npm-cli-version.mjs +11 -3
  103. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +19 -1
  104. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +39 -3
  105. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +3 -1
  106. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +2 -0
  107. package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +6 -1
  108. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -9
  109. package/src/runtime/agent/orchestrator/providers/openai-ws-send/send-context.mjs +22 -3
  110. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +43 -5
  111. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/handshake-failure.mjs +3 -1
  112. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/stream-failure.mjs +6 -3
  113. package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +1 -0
  114. package/src/runtime/agent/orchestrator/providers/provider-availability.mjs +19 -0
  115. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +65 -1
  116. package/src/runtime/agent/orchestrator/providers/quota-reading.mjs +38 -12
  117. package/src/runtime/agent/orchestrator/providers/registry.mjs +43 -3
  118. package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +39 -7
  119. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +6 -0
  120. package/src/runtime/agent/orchestrator/providers/wire-protobuf-codec.mjs +368 -0
  121. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +12 -7
  122. package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +2 -0
  123. package/src/runtime/agent/orchestrator/runtime-core/compaction-config-keys.mjs +62 -0
  124. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +37 -16
  125. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-delta.mjs +100 -23
  126. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +1 -1
  127. package/src/runtime/agent/orchestrator/runtime-core/reaction-tool-defs.mjs +133 -0
  128. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +2 -0
  129. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +19 -4
  130. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
  131. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +125 -30
  132. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +23 -5
  133. package/src/runtime/agent/orchestrator/runtime-core/tool-profile.mjs +35 -10
  134. package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +57 -10
  135. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +9 -6
  136. package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +1 -1
  137. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +18 -0
  138. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +0 -3
  139. package/src/runtime/agent/orchestrator/session/agent-room.mjs +10 -4
  140. package/src/runtime/agent/orchestrator/session/cache-warm.mjs +792 -0
  141. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +148 -25
  142. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +475 -58
  143. package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +42 -17
  144. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +8 -0
  145. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +47 -9
  146. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +6 -2
  147. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -4
  148. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +14 -2
  149. package/src/runtime/agent/orchestrator/session/loop/assistant-commit.mjs +8 -0
  150. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +18 -22
  151. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +32 -3
  152. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +11 -2
  153. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +4 -1
  154. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +51 -3
  155. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +17 -3
  156. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +10 -2
  157. package/src/runtime/agent/orchestrator/session/loop/tool-exec/after-hook.mjs +2 -1
  158. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +14 -4
  159. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +16 -2
  160. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +16 -1
  161. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +10 -0
  162. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -10
  163. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +23 -5
  164. package/src/runtime/agent/orchestrator/session/loop/turn-limits.mjs +60 -0
  165. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -2
  166. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +19 -1
  167. package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +2 -0
  168. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +133 -90
  169. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +14 -0
  170. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +2 -1
  171. package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +31 -4
  172. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +30 -18
  173. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +23 -13
  174. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +11 -2
  175. package/src/runtime/agent/orchestrator/session/manager/provider-replay.mjs +13 -0
  176. package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +129 -0
  177. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +20 -2
  178. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +2 -0
  179. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +8 -0
  180. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +1 -1
  181. package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +13 -0
  182. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +7 -2
  183. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +102 -15
  184. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +18 -5
  185. package/src/runtime/agent/orchestrator/session/message-ids.mjs +5 -3
  186. package/src/runtime/agent/orchestrator/session/message-reactions.mjs +89 -0
  187. package/src/runtime/agent/orchestrator/session/message-reactions.test-support.mjs +75 -0
  188. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +19 -8
  189. package/src/runtime/agent/orchestrator/session/prompt-cache-idle-gaps.mjs +113 -0
  190. package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +127 -0
  191. package/src/runtime/agent/orchestrator/session/routine-result.d.mts +0 -2
  192. package/src/runtime/agent/orchestrator/session/routine-result.mjs +47 -11
  193. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +20 -2
  194. package/src/runtime/agent/orchestrator/session/sensitive-projection.mjs +100 -0
  195. package/src/runtime/agent/orchestrator/session/sensitive-values.mjs +257 -0
  196. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +20 -7
  197. package/src/runtime/agent/orchestrator/session/side-question.mjs +18 -0
  198. package/src/runtime/agent/orchestrator/session/skill-update-notice.mjs +60 -0
  199. package/src/runtime/agent/orchestrator/session/store/live-state.mjs +6 -3
  200. package/src/runtime/agent/orchestrator/session/store/save-delta.mjs +9 -5
  201. package/src/runtime/agent/orchestrator/session/store/serialize.mjs +9 -8
  202. package/src/runtime/agent/orchestrator/session/store/session-persistence-events.mjs +29 -0
  203. package/src/runtime/agent/orchestrator/session/store-agent-worker-header-cache.mjs +85 -0
  204. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +133 -18
  205. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +12 -2
  206. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +44 -14
  207. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +84 -3
  208. package/src/runtime/agent/orchestrator/session/stored-attachment-history.mjs +32 -0
  209. package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +11 -5
  210. package/src/runtime/agent/orchestrator/session/thinking-binding.mjs +108 -0
  211. package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +2 -2
  212. package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +2 -1
  213. package/src/runtime/agent/orchestrator/session/tool-batch/state.mjs +4 -0
  214. package/src/runtime/agent/orchestrator/session/tool-batch.mjs +5 -0
  215. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +25 -2
  216. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-status.mjs +9 -7
  217. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-text.mjs +2 -2
  218. package/src/runtime/agent/orchestrator/session/turn-failure.mjs +5 -1
  219. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +5 -2
  220. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +12 -1
  221. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/misuse-guard.mjs +240 -0
  222. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +1 -1
  223. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/shell-cwd.mjs +38 -0
  224. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +25 -5
  225. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +65 -7
  226. package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +7 -1
  227. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +36 -13
  228. package/src/runtime/browser-bridge/action-schema.mjs +13 -2
  229. package/src/runtime/browser-bridge/client.mjs +73 -8
  230. package/src/runtime/browser-bridge/input-fields.mjs +8 -9
  231. package/src/runtime/browser-bridge/mail-links.mjs +107 -0
  232. package/src/runtime/browser-bridge/tool-defs.mjs +1 -1
  233. package/src/runtime/channels/lib/config.mjs +6 -61
  234. package/src/runtime/channels/lib/crash-log.mjs +1 -1
  235. package/src/runtime/channels/lib/event-pipeline.mjs +4 -19
  236. package/src/runtime/channels/lib/event-queue.mjs +3 -18
  237. package/src/runtime/channels/lib/owned-runtime/automation-services.mjs +2 -2
  238. package/src/runtime/channels/lib/owned-runtime/automation-start.mjs +3 -4
  239. package/src/runtime/channels/lib/owned-runtime/config-reload.mjs +4 -59
  240. package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +33 -115
  241. package/src/runtime/channels/lib/owned-runtime/owned-stop.mjs +5 -22
  242. package/src/runtime/channels/lib/owned-runtime/state.mjs +2 -8
  243. package/src/runtime/channels/lib/owned-runtime.mjs +6 -23
  244. package/src/runtime/channels/lib/parent-bridge.mjs +2 -2
  245. package/src/runtime/channels/lib/runtime-paths.mjs +4 -43
  246. package/src/runtime/channels/lib/schedule-errors.mjs +51 -0
  247. package/src/runtime/channels/lib/scheduler.mjs +135 -154
  248. package/src/runtime/channels/lib/schedules-db.mjs +83 -11
  249. package/src/runtime/channels/lib/status-snapshot.mjs +1 -1
  250. package/src/runtime/channels/lib/tool-dispatch.mjs +14 -2
  251. package/src/runtime/channels/lib/webhooks-db.mjs +5 -9
  252. package/src/runtime/channels/lib/worker-bootstrap.mjs +0 -1
  253. package/src/runtime/channels/lib/worker-ipc.mjs +0 -1
  254. package/src/runtime/channels/lib/worker-main.mjs +19 -34
  255. package/src/runtime/computer-bridge/action-schema.mjs +42 -1
  256. package/src/runtime/computer-bridge/actions.mjs +9 -1
  257. package/src/runtime/computer-bridge/client.mjs +180 -8
  258. package/src/runtime/computer-bridge/core-actions.mjs +2 -2
  259. package/src/runtime/computer-bridge/error-recovery.mjs +27 -2
  260. package/src/runtime/computer-bridge/linux-readiness.mjs +65 -23
  261. package/src/runtime/computer-bridge/pending-continuation.mjs +1 -1
  262. package/src/runtime/connectors/connector-accounts-mcp.mjs +230 -0
  263. package/src/runtime/connectors/gmail-app-password.mjs +172 -39
  264. package/src/runtime/connectors/google-mcp.mjs +10 -12
  265. package/src/runtime/memory/lib/pg/adapter.mjs +77 -70
  266. package/src/runtime/memory/lib/pg/supervisor.mjs +24 -12
  267. package/src/runtime/memory/lib/runtime-fetcher.mjs +23 -5
  268. package/src/runtime/office/com/com-adapter.mjs +17 -1
  269. package/src/runtime/office/com/office-com-host.ps1 +51 -1
  270. package/src/runtime/office/pdf/cfb-fixture.mjs +328 -0
  271. package/src/runtime/office/pdf/cfb-protection.mjs +311 -0
  272. package/src/runtime/office/pdf/document-preview.mjs +326 -8
  273. package/src/runtime/office/pdf/ooxml-crypto.mjs +469 -0
  274. package/src/runtime/office/pdf/ooxml-key-worker.mjs +14 -0
  275. package/src/runtime/office/portable/portable-soffice.mjs +45 -5
  276. package/src/runtime/session-search/session-search-db.mjs +73 -9
  277. package/src/runtime/session-search/session-search-ingest.mjs +110 -20
  278. package/src/runtime/shared/abort-race.mjs +89 -0
  279. package/src/runtime/shared/agent-avatar.d.mts +0 -1
  280. package/src/runtime/shared/agent-kind.mjs +4 -3
  281. package/src/runtime/shared/bot-hops.mjs +62 -2
  282. package/src/runtime/shared/bounded-download.mjs +8 -3
  283. package/src/runtime/shared/config.mjs +51 -35
  284. package/src/runtime/shared/connector-accounts.mjs +20 -10
  285. package/src/runtime/shared/connector-catalog.d.mts +33 -2
  286. package/src/runtime/shared/connector-catalog.mjs +166 -8
  287. package/src/runtime/shared/connector-triggers.mjs +22 -19
  288. package/src/runtime/shared/defaults-separation.mjs +2 -14
  289. package/src/runtime/shared/delegated-approval-host.mjs +103 -11
  290. package/src/runtime/shared/developer-options.mjs +32 -1
  291. package/src/runtime/shared/err-text.mjs +24 -15
  292. package/src/runtime/shared/file-permissions.d.mts +0 -8
  293. package/src/runtime/shared/file-permissions.mjs +0 -11
  294. package/src/runtime/shared/git-direct.test-support.mjs +19 -0
  295. package/src/runtime/shared/google-oauth-client.generated.json +4 -0
  296. package/src/runtime/shared/google-oauth-client.mjs +34 -9
  297. package/src/runtime/shared/heartbeat-interval.d.mts +4 -0
  298. package/src/runtime/shared/heartbeat-interval.mjs +19 -0
  299. package/src/runtime/shared/idle-auto-clear-gate.mjs +104 -0
  300. package/src/runtime/shared/llm/anthropic-betas.mjs +7 -0
  301. package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +12 -0
  302. package/src/runtime/shared/llm/provider-catalog-registry.mjs +1 -0
  303. package/src/runtime/shared/llm/usage-rollup.mjs +1 -1
  304. package/src/runtime/shared/llm/well-formed.mjs +66 -0
  305. package/src/runtime/shared/mixy-presence.mjs +22 -0
  306. package/src/runtime/shared/orchestration.d.mts +2 -0
  307. package/src/runtime/shared/pg-schema-db.mjs +27 -16
  308. package/src/runtime/shared/profile-config.cjs +1 -1
  309. package/src/runtime/shared/prompt-origin.d.mts +4 -0
  310. package/src/runtime/shared/prompt-origin.mjs +21 -0
  311. package/src/runtime/shared/provider-accounts.mjs +231 -59
  312. package/src/runtime/shared/provider-api-key.mjs +19 -8
  313. package/src/runtime/shared/provider-display-name.mjs +29 -0
  314. package/src/runtime/shared/provider-timeout-phase.mjs +2 -0
  315. package/src/runtime/shared/routine-trigger.d.mts +2 -0
  316. package/src/runtime/shared/routine-trigger.mjs +52 -0
  317. package/src/runtime/shared/sensitive-path-rules.d.mts +2 -10
  318. package/src/runtime/shared/sensitive-path-rules.mjs +19 -41
  319. package/src/runtime/shared/session-activity-signal.mjs +34 -1
  320. package/src/runtime/shared/session-sidebar-host.d.mts +1 -1
  321. package/src/runtime/shared/session-sidebar-host.mjs +7 -3
  322. package/src/runtime/shared/session-turn-prelude.mjs +18 -0
  323. package/src/runtime/shared/tool-execution-contract.mjs +2 -0
  324. package/src/runtime/shared/tool-result-summary.mjs +1 -14
  325. package/src/runtime/shared/transcript-metadata.mjs +38 -0
  326. package/src/runtime/shared/user-data-guard.mjs +30 -2
  327. package/src/runtime/shared/worktrees/activity-gate.mjs +12 -5
  328. package/src/runtime/shared/worktrees/test-support.mjs +9 -4
  329. package/src/runtime/terminal-bridge/client.mjs +10 -1
  330. package/src/runtime/visualize-bridge/client.mjs +27 -8
  331. package/src/runtime/visualize-bridge/tool-defs.mjs +22 -8
  332. package/src/runtime/visualize-bridge/tool-meta.d.mts +5 -0
  333. package/src/runtime/visualize-bridge/tool-meta.mjs +22 -0
  334. package/src/runtime/web-search/lib/fetch-pipeline.mjs +2 -2
  335. package/src/session-runtime/agent-home-delivery.mjs +486 -179
  336. package/src/session-runtime/agent-room-delivery.mjs +28 -1
  337. package/src/session-runtime/agent-task-sessions.mjs +561 -199
  338. package/src/session-runtime/boot/apis.mjs +9 -0
  339. package/src/session-runtime/boot/begin.mjs +3 -2
  340. package/src/session-runtime/boot/infrastructure.mjs +4 -0
  341. package/src/session-runtime/boot/providers.mjs +3 -0
  342. package/src/session-runtime/boot/tools.mjs +8 -1
  343. package/src/session-runtime/bot-chain.mjs +32 -0
  344. package/src/session-runtime/bot-status.mjs +7 -4
  345. package/src/session-runtime/channel-config-api.mjs +63 -17
  346. package/src/session-runtime/config-lifecycle.mjs +6 -0
  347. package/src/session-runtime/connector-merge-migration.mjs +305 -0
  348. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +132 -28
  349. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +2 -0
  350. package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +45 -0
  351. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +72 -5
  352. package/src/session-runtime/internal-tool-executor.mjs +4 -0
  353. package/src/session-runtime/lifecycle/inheritance.mjs +16 -3
  354. package/src/session-runtime/lifecycle/session-catalog.mjs +3 -1
  355. package/src/session-runtime/lifecycle/teardown/runtime-stops.mjs +50 -4
  356. package/src/session-runtime/lifecycle/teardown.mjs +23 -5
  357. package/src/session-runtime/mcp-glue/server-catalog.mjs +61 -3
  358. package/src/session-runtime/mcp-oauth-login.mjs +5 -3
  359. package/src/session-runtime/mixy-heartbeat.mjs +927 -0
  360. package/src/session-runtime/model-route/route-tuning.mjs +10 -6
  361. package/src/session-runtime/notification-bus.mjs +31 -117
  362. package/src/session-runtime/notification-completion-queue.mjs +54 -0
  363. package/src/session-runtime/notification-listeners.mjs +76 -0
  364. package/src/session-runtime/owned-automation-run.mjs +35 -13
  365. package/src/session-runtime/prewarm/channel-start.mjs +3 -7
  366. package/src/session-runtime/provider-auth/login.mjs +7 -0
  367. package/src/session-runtime/provider-models/row-hydration.mjs +13 -2
  368. package/src/session-runtime/resource-mcp-api.mjs +143 -224
  369. package/src/session-runtime/resource-mcp-connector-accounts.mjs +163 -0
  370. package/src/session-runtime/resource-mcp-oauth-api.mjs +217 -0
  371. package/src/session-runtime/resource-mcp-secrets.mjs +82 -0
  372. package/src/session-runtime/resource-mcp-servers.mjs +78 -0
  373. package/src/session-runtime/resource-mcp-store.mjs +49 -0
  374. package/src/session-runtime/routine-home-turn.mjs +33 -6
  375. package/src/session-runtime/routine-owner.mjs +40 -9
  376. package/src/session-runtime/schedule-session-run.mjs +7 -3
  377. package/src/session-runtime/services/agent-tool/close-flow.mjs +3 -0
  378. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +8 -12
  379. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +7 -2
  380. package/src/session-runtime/services/agent-tool/tag-registry.mjs +1 -0
  381. package/src/session-runtime/services/agent-tool/worker-index/awaiting-user-sync.mjs +51 -0
  382. package/src/session-runtime/services/agent-tool/worker-index/disposal.mjs +55 -0
  383. package/src/session-runtime/services/agent-tool/worker-index/released-store.mjs +123 -0
  384. package/src/session-runtime/services/agent-tool/worker-index/restart-settlement.mjs +112 -0
  385. package/src/session-runtime/services/agent-tool/worker-index/row-reads.mjs +36 -0
  386. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -0
  387. package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +17 -4
  388. package/src/session-runtime/services/agent-tool/worker-index/row-writes.mjs +58 -0
  389. package/src/session-runtime/services/agent-tool/worker-index.mjs +41 -241
  390. package/src/session-runtime/services/agent-tool/worker-rows.mjs +3 -0
  391. package/src/session-runtime/services/channel-admin.mjs +73 -31
  392. package/src/session-runtime/services/channel-session-router.mjs +4 -2
  393. package/src/session-runtime/services/channel-worker.mjs +1 -1
  394. package/src/session-runtime/services/provider-admin.mjs +128 -25
  395. package/src/session-runtime/services/schedule-access.mjs +14 -0
  396. package/src/session-runtime/services/seeds.mjs +0 -28
  397. package/src/session-runtime/services/usage-dashboard-model.mjs +1 -0
  398. package/src/session-runtime/session-compaction-sync.mjs +27 -0
  399. package/src/session-runtime/session-create/session-options.mjs +8 -4
  400. package/src/session-runtime/settings-builtin-tools-api.mjs +11 -0
  401. package/src/session-runtime/settings-compaction-api.mjs +2 -40
  402. package/src/session-runtime/settings-system-api.mjs +28 -1
  403. package/src/session-runtime/setup-tool/agent-settings-actions.mjs +65 -0
  404. package/src/session-runtime/setup-tool/executor.mjs +187 -29
  405. package/src/session-runtime/setup-tool/extended-actions.mjs +33 -2
  406. package/src/session-runtime/setup-tool/session-route-actions.mjs +152 -0
  407. package/src/session-runtime/setup-tool/settings-contract.mjs +68 -6
  408. package/src/session-runtime/setup-tool/tool-defs.mjs +92 -6
  409. package/src/session-runtime/task-session-route.mjs +99 -0
  410. package/src/session-runtime/tool-policy/agent-profile-fold.mjs +17 -13
  411. package/src/session-runtime/tool-policy/policy-stage.mjs +21 -4
  412. package/src/session-runtime/tool-policy/surface-helpers.mjs +2 -3
  413. package/src/session-runtime/tool-surface.mjs +4 -0
  414. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
  415. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +98 -21
  416. package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +4 -0
  417. package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +6 -1
  418. package/src/session-runtime/workspace/transitions.mjs +10 -2
  419. package/src/standalone/channel-transport/client-registry/client-records.mjs +65 -0
  420. package/src/standalone/channel-transport/client-registry/client-registration.mjs +118 -0
  421. package/src/standalone/channel-transport/client-registry.mjs +24 -163
  422. package/src/standalone/channel-transport.mjs +1 -1
  423. package/src/standalone/daemon-channels-loader.mjs +10 -8
  424. package/src/standalone/daemon-session-control/list-page.mjs +72 -0
  425. package/src/standalone/daemon-session-control/request.mjs +51 -0
  426. package/src/standalone/daemon-session-control/room-host.mjs +57 -0
  427. package/src/standalone/daemon-session-control/route-control.mjs +48 -0
  428. package/src/standalone/daemon-session-control/send-turn.mjs +60 -0
  429. package/src/standalone/daemon-session-control/worktree-control.mjs +40 -0
  430. package/src/standalone/daemon-session-control.mjs +161 -157
  431. package/src/standalone/daemon.mjs +33 -2
  432. package/src/standalone/session-client.mjs +7 -3
  433. package/src/standalone/session-protocol.mjs +10 -0
  434. package/src/standalone/session-service/agent-tree/agent-cancel.mjs +6 -2
  435. package/src/standalone/session-service/agent-tree/agent-child.mjs +1 -0
  436. package/src/standalone/session-service/quota-resume.mjs +3 -0
  437. package/src/standalone/session-service/retention/busy.mjs +65 -1
  438. package/src/standalone/session-service/session-calls/session-action.mjs +23 -2
  439. package/src/standalone/session-service/session-calls/session-turns.mjs +6 -0
  440. package/src/standalone/session-service/session-calls/session-views.mjs +8 -0
  441. package/src/standalone/session-service.mjs +13 -0
  442. package/src/standalone/session-tool-output.mjs +21 -10
  443. package/src/tui/app/model-options.mjs +4 -27
  444. package/src/tui/dist/index.mjs +81 -65
  445. package/src/tui/session/agent-job-feed/model-notification.mjs +3 -0
  446. package/src/tui/session/initial-state.mjs +3 -0
  447. package/src/tui/session/notice-surface.mjs +23 -1
  448. package/src/tui/session/queue-helpers.mjs +3 -42
  449. package/src/tui/session/session-api/integrations.mjs +1 -0
  450. package/src/tui/session/session-api/lifecycle.mjs +8 -0
  451. package/src/tui/session/session-api/routes-agent-definitions.mjs +14 -2
  452. package/src/tui/session/session-api/routes-write.mjs +5 -1
  453. package/src/tui/session/session-api/routes.mjs +1 -0
  454. package/src/tui/session/session-api/settings.mjs +30 -2
  455. package/src/tui/session/session-flow/auto-clear.mjs +4 -33
  456. package/src/tui/session/session-flow/drain-closed-queue.mjs +38 -0
  457. package/src/tui/session/session-flow/drain-enqueue.mjs +86 -0
  458. package/src/tui/session/session-flow/drain-retry.mjs +35 -0
  459. package/src/tui/session/session-flow/drain.mjs +118 -149
  460. package/src/tui/session/session-flow/queue/entry-shape.mjs +5 -0
  461. package/src/tui/session/session-flow/queue/take-entries.mjs +5 -2
  462. package/src/tui/session/tool-approval-delegation.mjs +64 -0
  463. package/src/tui/session/tool-approval-request.mjs +39 -0
  464. package/src/tui/session/tool-approval-settle.mjs +62 -0
  465. package/src/tui/session/tool-approval.mjs +69 -115
  466. package/src/tui/session/tui-steering-persist.mjs +8 -1
  467. package/src/tui/session/turn-stream.mjs +36 -3
  468. package/src/tui/session/turn.mjs +23 -2
  469. package/src/vendor/statusline/bin/statusline-route.mjs +1 -219
  470. package/src/vendor/statusline/src/gateway/route-meta.mjs +35 -13
  471. package/scripts/code-graph-description-contract.mjs +0 -188
  472. package/src/runtime/channels/lib/owned-runtime/ownership-refresh.mjs +0 -36
@@ -18,6 +18,9 @@ import { resetOutputLimitRecovery, resolveOutputLimit } from './no-tool-turn/out
18
18
  import { resolveRefusal } from './no-tool-turn/refusal.mjs';
19
19
  import { resumeContinuation } from './no-tool-turn/continuation.mjs';
20
20
  import { nudgeEmptyTurn } from './no-tool-turn/empty-nudge.mjs';
21
+ import { isReactionSuccess, reactionCallIds, takeReactionTarget } from '../../runtime-core/reaction-tool-defs.mjs';
22
+ import { messageIdOf } from '../message-ids.mjs';
23
+ import { isReactableMessage } from '../message-reactions.mjs';
21
24
 
22
25
  // `escalateMaxTokens()` asks the live provider for a higher one-shot max_tokens
23
26
  // (number) or reports none (null/undefined) — the loop stays provider-agnostic.
@@ -37,8 +40,11 @@ export function createNoToolTurnResolver({
37
40
  outputEscalationUsed: false,
38
41
  refusalRetryUsed: false,
39
42
  emptyNudgeStreak: 0,
43
+ // Successful reactions (react tool): the input each was left against and
44
+ // whether it answered that input.
45
+ reactions: [],
40
46
  // Structured provider continuation signals honored this turn. Diagnostic
41
- // only: the loop has no iteration cap, so this count bounds nothing. A
47
+ // only: this count bounds nothing. A
42
48
  // text-only continuation runaway is bounded by continuationsSinceToolBatch
43
49
  // below; continuations interleaved with tool batches are not limited.
44
50
  providerContinuationCount: 0,
@@ -46,6 +52,39 @@ export function createNoToolTurnResolver({
46
52
  // continuation runaway (see PROVIDER_CONTINUATION_NO_TOOL_LIMIT).
47
53
  continuationsSinceToolBatch: 0,
48
54
  };
55
+ // The stable id (message-ids.mjs, kept across compaction) of the newest
56
+ // real user input; runtime reminders and notices are not reactable.
57
+ // A group-room member turn's prompt is a runtime message (not reactable)
58
+ // that carries the newest room-log user row it shows (`roomRow`).
59
+ const roomRowOf = (message) => message?.meta?.transcript?.roomRow || '';
60
+ const latestInput = () => messages.findLast((message) => isReactableMessage(message) || roomRowOf(message));
61
+ const latestInputId = () => messageIdOf(latestInput());
62
+ // A reaction exempts the input only while no newer input arrived since it
63
+ // landed (same latest input id) and it answered that input.
64
+ const reactionAnswersLatest = () => {
65
+ const id = latestInputId();
66
+ return state.reactions.some((reaction) => reaction.answered && reaction.inputId === id);
67
+ };
68
+ // What this batch's react calls hit, from the id the react tool recorded
69
+ // for each (never rebuilt from this transcript: a room reaction runs
70
+ // against the room's own log). Every record is consumed; a success that
71
+ // recorded none exempts nothing. A home-chat reaction answers the input
72
+ // only when it hit that very message; a room reaction, whose message is in
73
+ // another log, only when it hit the row this turn's prompt showed.
74
+ const batchReactions = (calls, results) => {
75
+ const input = latestInput();
76
+ const inputId = messageIdOf(input);
77
+ const hit = [];
78
+ for (const id of reactionCallIds(calls)) {
79
+ const target = takeReactionTarget(sessionId, id);
80
+ if (!target || !results.some((message) => isReactionSuccess(message, new Set([id])))) continue;
81
+ const answered = target.room
82
+ ? roomRowOf(input) === target.messageId
83
+ : inputId !== '' && target.messageId === inputId;
84
+ hit.push({ inputId, answered });
85
+ }
86
+ return hit;
87
+ };
49
88
  const trace = (iteration, kind, payload) => {
50
89
  try {
51
90
  appendAgentTrace({ sessionId, iteration, kind, payload, agent: sessionAgent || null });
@@ -101,7 +140,9 @@ export function createNoToolTurnResolver({
101
140
  // output, …) and a text-only terminal turn is the correct shape —
102
141
  // nudging them produces a contradictory user message that traps the
103
142
  // model in a tool-call-blocked vs contract-required oscillation.
104
- if (!hasContent && !HIDDEN_AGENT_NAMES.has(sessionAgent)) {
143
+ // A turn that left a reaction on the user's message may end with no
144
+ // text: the reaction is its answer (react tool), never an empty turn.
145
+ if (!hasContent && !HIDDEN_AGENT_NAMES.has(sessionAgent) && !reactionAnswersLatest()) {
105
146
  return nudgeEmptyTurn(
106
147
  response,
107
148
  { stopReason, isIncompleteStop: stopReason && INCOMPLETE_STOP_REASONS.has(stopReason) },
@@ -132,9 +173,16 @@ export function createNoToolTurnResolver({
132
173
  state.emptyNudgeStreak = 0;
133
174
  resetOutputLimitRecovery(state);
134
175
  },
135
- noteToolBatchCompleted() {
176
+ // `results` are the batch's tool result messages: only a react call that
177
+ // succeeded marks the turn as having reacted.
178
+ noteToolBatchCompleted(calls = [], results = []) {
136
179
  state.continuationsSinceToolBatch = 0;
137
180
  segments.clear();
181
+ state.reactions.push(...batchReactions(calls, results));
182
+ },
183
+ // The loop ended on a reaction that answered the latest user input.
184
+ get reactionCompleted() {
185
+ return reactionAnswersLatest();
138
186
  },
139
187
  get providerContinuations() {
140
188
  return state.providerContinuationCount;
@@ -6,6 +6,12 @@ import { getProvider, initProviders } from '../../providers/registry.mjs';
6
6
  import { loadConfig } from '../../config.mjs';
7
7
  import { ensureProviderEnabled } from '../../runtime-core/config-helpers.mjs';
8
8
  import { updateSessionRoute } from '../manager/session-lifecycle.mjs';
9
+ import {
10
+ liveRouteFallbacks,
11
+ nextRouteFallbackIndex,
12
+ routeFallbackHopped,
13
+ routeLeaf,
14
+ } from '../manager/route-fallback-chain.mjs';
9
15
  import { pinSessionWireRoute } from '../../providers/wire-route.mjs';
10
16
  import { buildRequestSendOptions } from '../manager/ask-turn-loop-options.mjs';
11
17
  import { prepareTurnEffortConfiguration } from '../../providers/effort-configuration.mjs';
@@ -78,9 +84,13 @@ async function nextContextGrows(state, next) {
78
84
 
79
85
  export async function advanceAgentRoute(state, error) {
80
86
  const session = state.sessionRef;
81
- if (!session?.agent || !session.routeFallbacks?.length) return false;
82
- const index = session.routeFallbackIndex || 0;
83
- const next = session.routeFallbacks[index];
87
+ if (!session?.agent) return false;
88
+ // Read from the session's settings at this hop: a chain edited while it runs
89
+ // applies now, and one cleared to [] leaves the provider's own error.
90
+ const { chain } = liveRouteFallbacks(session, loadConfig({ secrets: false }));
91
+ if (!chain.length) return false;
92
+ const index = nextRouteFallbackIndex(session, chain);
93
+ const next = chain[index];
84
94
  // Only THIS request must have produced nothing (its stream outcome says so):
85
95
  // earlier requests of the turn may have run tools. Their calls and results
86
96
  // are already in the live turn, which the next model continues from, so no
@@ -97,6 +107,8 @@ export async function advanceAgentRoute(state, error) {
97
107
  const provider = getProvider(next.provider);
98
108
  if (!provider) throw new Error(`Provider "${next.provider}" not available`);
99
109
  const failed = routeFailure(session, error);
110
+ // The route a reset returns to (setup reset_route_fallback), kept from the first hop.
111
+ const primary = routeFallbackHopped(session) ? session.routeFallbackPrimary : routeLeaf(session);
100
112
  // The left model's share of the turn ends here: the running total at the
101
113
  // switch bounds it, so each model's tokens are booked at its own rates.
102
114
  state.routeUsageHops = [
@@ -131,6 +143,8 @@ export async function advanceAgentRoute(state, error) {
131
143
  { crossProvider: true, session }
132
144
  );
133
145
  if (!updated) throw new Error('agent session closed during model fallback');
146
+ updated.routeFallbacks = chain;
147
+ if (primary) updated.routeFallbackPrimary = primary;
134
148
  updated.routeFallbackIndex = index + 1;
135
149
  updated.routeFallbackFailures = [...(session.routeFallbackFailures || []), failed];
136
150
  await pinSessionWireRoute(updated, provider);
@@ -96,6 +96,9 @@ export async function beginIteration(state) {
96
96
  opts.providerState = state.providerState;
97
97
  if (state.forcedFirstTool && state.toolCallsTotal === 0) {
98
98
  opts.toolChoice = 'required';
99
+ } else if (state.turnLimitNoticed) {
100
+ // A capped turn told to reply may only answer (turn-limits.mjs).
101
+ opts.toolChoice = 'none';
99
102
  } else {
100
103
  delete opts.toolChoice;
101
104
  }
@@ -123,13 +126,16 @@ export async function beginIteration(state) {
123
126
  getIterations: () => state.iterations,
124
127
  getNextIteration: () => nextIteration,
125
128
  repeatFailLimit: REPEAT_FAIL_LIMIT,
129
+ isTurnLimitNoticed: () => state.turnLimitNoticed === true,
126
130
  });
127
131
  const resolveToolCallId = createToolCallIdResolver(messages);
128
132
  opts.onToolCall = (call) => {
129
133
  resolveToolCallId(call);
130
134
  try {
131
135
  opts.onAssistantToolCallObserved?.(call, { eagerStarted: false });
132
- } catch {}
136
+ } catch {
137
+ // the tool-call observer is advisory
138
+ }
133
139
  return eager.onToolCall(call);
134
140
  };
135
141
  return {
@@ -170,7 +176,9 @@ export async function sendProviderRequest(state, round) {
170
176
  });
171
177
  try {
172
178
  opts.onProviderSendStarted?.();
173
- } catch {}
179
+ } catch {
180
+ // the send-started observer is advisory
181
+ }
174
182
  // The request a /btw side question forks: kept while it is in flight, with
175
183
  // its reply once it proceeds, and dropped again if it fails.
176
184
  const sideQuestion = captureSideQuestionRequest({
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { normalizeToolEnvelope, makeToolEnvelope } from '../../tool-envelope.mjs';
6
6
  import { resolveToolResultAfterHook } from '../tool-helpers.mjs';
7
+ import { effectiveToolCwd } from '../../../tools/builtin/bash-tool/shell-cwd.mjs';
7
8
 
8
9
  export async function applyAfterToolHook(result, afterToolHook, { name, args, cwd, callerSessionId, toolCallId }) {
9
10
  if (typeof afterToolHook !== 'function') return result;
@@ -15,7 +16,7 @@ export async function applyAfterToolHook(result, afterToolHook, { name, args, cw
15
16
  const hookResult = await afterToolHook({
16
17
  name,
17
18
  args,
18
- cwd,
19
+ cwd: effectiveToolCwd(name, args, cwd),
19
20
  sessionId: callerSessionId,
20
21
  toolCallId,
21
22
  result: visible,
@@ -7,8 +7,15 @@ import { runAbortable, throwIfAborted } from '../../../../../shared/abort-race.m
7
7
  import { evaluateExecPolicy } from '../../../tools/bash-policy-scan.mjs';
8
8
  import { beginSessionApprovalWait } from '../../manager/runtime-liveness.mjs';
9
9
  import { agentMcpToolPermission, writeAgentMcpToolPermission } from '../../../mcp/agent-connectors.mjs';
10
- import { actingBotId, actingContactBotId, humanGateDecision, recordHumanDecision } from '../../human-gate/gate.mjs';
10
+ import {
11
+ actingBotId,
12
+ actingContactBotId,
13
+ allowAllActionsEnabled,
14
+ humanGateDecision,
15
+ recordHumanDecision,
16
+ } from '../../human-gate/gate.mjs';
11
17
  import { applyEditedArgs } from '../../human-gate/requests.mjs';
18
+ import { effectiveToolCwd } from '../../../tools/builtin/bash-tool/shell-cwd.mjs';
12
19
 
13
20
  const isArgsObject = (value) => value && typeof value === 'object' && !Array.isArray(value);
14
21
 
@@ -25,7 +32,7 @@ async function askApproval(
25
32
  resolvePreToolAskApproval({
26
33
  toolName: call.name,
27
34
  args: call.args,
28
- cwd: call.cwd,
35
+ cwd: effectiveToolCwd(call.name, call.args, call.cwd),
29
36
  sessionId: callerSessionId,
30
37
  toolCallId: executeOpts.toolCallId || null,
31
38
  askReason,
@@ -62,13 +69,16 @@ export const CONNECTOR_APPROVAL_REASON = 'This connector tool is set to ask befo
62
69
  * coding Lead, a task session, a workflow agent, a sub-agent) runs it unasked.
63
70
  * Run after the hooks, on the final call: a renamed call is judged under its
64
71
  * final name. A call the outbound gate owns (its card approved it, or a
65
- * remembered outbound grant answered it) is not asked a second time.
72
+ * remembered outbound grant answered it) is not asked a second time. With the
73
+ * "Allow all actions" developer option on, `needs_approval` is not asked
74
+ * either (a payment or permanent delete was asked by the outbound gate above).
66
75
  * @returns {Promise<{ denial: string } | null>} the denial, or null to run
67
76
  */
68
77
  export function applyConnectorApproval(call, { agentId, callerSessionId, executeOpts, toolApprovalHook }) {
69
78
  if (call.outboundApproved) return null;
70
79
  if (!actingBotId(callerSessionId)) return null;
71
80
  if (agentMcpToolPermission(agentId, call.name) !== 'needs_approval') return null;
81
+ if (allowAllActionsEnabled()) return null;
72
82
  // "Always allow" is offered only to the contact bot that is acting, for its
73
83
  // own policy entry for this one tool: never another bot or Mixy.
74
84
  const botId = actingContactBotId(callerSessionId);
@@ -131,7 +141,7 @@ export async function applyBeforeToolHook({
131
141
  {
132
142
  name,
133
143
  args,
134
- cwd,
144
+ cwd: effectiveToolCwd(name, args, cwd),
135
145
  sessionId: callerSessionId,
136
146
  toolCallId: executeOpts.toolCallId || null,
137
147
  },
@@ -3,7 +3,14 @@
3
3
  * MCP, code graph, internal tools, shell, apply_patch, built-ins and foreign
4
4
  * CLI adapters.
5
5
  */
6
- import { executeMcpTool, isMcpTool, isRegisteredMcpTool, mcpToolHasField } from '../../../mcp/client.mjs';
6
+ import {
7
+ executeMcpTool,
8
+ isBuiltinMailRead,
9
+ isMcpTool,
10
+ isRegisteredMcpTool,
11
+ mcpToolHasField,
12
+ } from '../../../mcp/client.mjs';
13
+ import { recordMailLinks } from '../../../../../browser-bridge/mail-links.mjs';
7
14
  import {
8
15
  executeBuiltinTool,
9
16
  formatUnknownBuiltinToolMessage,
@@ -56,11 +63,18 @@ function dispatchMcp(name, args, { cwd, callerSessionId, sessionRef, executeOpts
56
63
  // an unknown argument.
57
64
  const needsCwdInjection = cwd && mcpToolHasField(name, 'cwd', mcpScopeId) && (args == null || args.cwd == null);
58
65
  const finalArgs = needsCwdInjection ? { ...(args || {}), cwd } : args;
59
- return executeMcpTool(name, finalArgs, {
66
+ const call = executeMcpTool(name, finalArgs, {
60
67
  signal: executeOpts.signal || null,
61
68
  ownerKey: callerSessionId,
62
69
  scopeId: mcpScopeId,
63
70
  });
71
+ if (!isBuiltinMailRead(name, mcpScopeId)) return call;
72
+ // The runtime's own record of the links in a mail it read for this session:
73
+ // the only way Browser Use may open a link carrying a token.
74
+ return call.then((result) => {
75
+ recordMailLinks(callerSessionId, result);
76
+ return result;
77
+ });
64
78
  }
65
79
 
66
80
  function dispatchInternal(
@@ -19,6 +19,7 @@ import { applyBeforeToolHook, applyConnectorApproval, applyShellElevationGate }
19
19
  import { dispatchToolCall } from './tool-exec/dispatch.mjs';
20
20
  import { applyAfterToolHook } from './tool-exec/after-hook.mjs';
21
21
  import { runReadOnlyIoWithDeadline } from './tool-exec/read-only-deadline.mjs';
22
+ import { guardShellCall, requestedShellType } from '../../tools/builtin/bash-tool/misuse-guard.mjs';
22
23
 
23
24
  export { runReadOnlyIoWithDeadline as _runReadOnlyIoWithDeadlineForTest };
24
25
  export {
@@ -43,9 +44,23 @@ async function executeToolOwned(name, args, cwd, callerSessionId, sessionRef, ex
43
44
  const prepared = prepareToolCall(name, args, cwd, callerSessionId, sessionRef, executeOpts);
44
45
  const { toolOpts, notifyFn, completionToolOpts } = prepared;
45
46
  const toolApprovalHook = hookFrom(executeOpts, sessionRef, 'toolApprovalHook');
47
+ // Shell misuse is judged before any hook or approval sees the call, so they
48
+ // read the command that will run and the directory it runs in.
49
+ let callArgs = prepared.args;
50
+ if (name === 'shell') {
51
+ const guarded = guardShellCall(callArgs, {
52
+ shellType: requestedShellType(callArgs),
53
+ agent: sessionRef?.agent || null,
54
+ sessionId: callerSessionId,
55
+ telemetry: executeOpts.resultTelemetry,
56
+ });
57
+ if (guarded.failure) return guarded.failure;
58
+ callArgs = guarded.args;
59
+ completionToolOpts.shellGuardNotes = guarded.notes;
60
+ }
46
61
  const hooked = await applyBeforeToolHook({
47
62
  name,
48
- args: prepared.args,
63
+ args: callArgs,
49
64
  cwd: prepared.cwd,
50
65
  callerSessionId,
51
66
  executeOpts,
@@ -18,6 +18,7 @@ import { isAgentOwner } from '../../agent-owner.mjs';
18
18
  import { honoredSchemaAllowlist } from '../manager/session-tool-surface.mjs';
19
19
  import { syncSystemPrefix } from '../manager/prompt-utils.mjs';
20
20
  import { invalidateProviderContextBaseline } from './compact-policy.mjs';
21
+ import { rebindToChangedPrefix } from '../thinking-binding.mjs';
21
22
 
22
23
  /** Drop everything measured against the previous tool surface. */
23
24
  function invalidateForToolSurfaceMigration(session) {
@@ -34,6 +35,14 @@ function invalidateForToolSurfaceMigration(session) {
34
35
  */
35
36
  export function markToolSurfaceMigrated(session, intent = TOOL_SURFACE_MIGRATION_INTENT) {
36
37
  invalidateForToolSurfaceMigration(session);
38
+ // The stored thinking was bound to the previous prefix and can never be
39
+ // replayed again (../thinking-binding.mjs); an open tool round a stopped
40
+ // turn left behind is ended first. Outside a turn only: a running turn sends
41
+ // from its own working transcript, whose latest tool round must keep its
42
+ // thinking.
43
+ if (Array.isArray(session.messages) && !Array.isArray(session.liveTurnMessages)) {
44
+ session.messages = rebindToChangedPrefix(session.messages);
45
+ }
37
46
  session.pendingCacheBreakIntent = intent;
38
47
  }
39
48
 
@@ -105,6 +114,7 @@ const AGENT_BRIDGE_FIELDS = [
105
114
  'deferredToolDeltaRevision',
106
115
  'nativeDeferredDiscovery',
107
116
  'mcpServerInstructions',
117
+ 'mcpInstructionsAnnounced',
108
118
  ];
109
119
 
110
120
  /**
@@ -12,10 +12,7 @@ import {
12
12
  loadSkillResource,
13
13
  buildSkillToolEnvelope,
14
14
  filterSkillsExcludingDisabled,
15
- isSkillDisabled,
16
- skillMissingFeature,
17
- skillOutsideScope,
18
- skillOutsideScopeError,
15
+ skillLoadRefusal,
19
16
  } from '../../context/collect.mjs';
20
17
 
21
18
  // Eager-dispatch: tools with readOnlyHint:true in their declaration are safe
@@ -95,12 +92,8 @@ export function buildSkillsListResponse(cwd) {
95
92
  export function viewSkill(cwd, name, session = null) {
96
93
  const skillName = String(name || '').trim();
97
94
  if (!skillName) return 'Error: skill name is required';
98
- const missingFeature = skillMissingFeature(skillName);
99
- if (missingFeature) {
100
- return `Error: skill "${skillName}" needs the ${missingFeature} built-in feature, which is not installed or is switched off in Extensions → Plugin → Built-in`;
101
- }
102
- if (isSkillDisabled(skillName)) return `Error: skill "${skillName}" is disabled`;
103
- if (skillOutsideScope(skillName, cwd)) return skillOutsideScopeError(skillName);
95
+ const refusal = skillLoadRefusal(skillName, cwd);
96
+ if (refusal) return refusal;
104
97
  const res = loadSkillResource(skillName, cwd);
105
98
  if (!res) return `Error: skill "${skillName}" not found`;
106
99
  // Return the general tool envelope: the model-visible tool_result is the
@@ -9,6 +9,8 @@ import { processToolBatch } from '../tool-batch.mjs';
9
9
  import { CROSS_TURN_CAP, REPEAT_FAIL_LIMIT } from './loop-state.mjs';
10
10
  import { markStepEffort } from './step-auto-effort.mjs';
11
11
  import { resolveExecutionToolCalls } from './tool-call-resolution.mjs';
12
+ import { rememberSensitiveValues } from '../sensitive-values.mjs';
13
+ import { callsAllowed, settleTurnLimits, turnLimitsOf } from './turn-limits.mjs';
12
14
 
13
15
  function isSleepLikeToolCall(call) {
14
16
  const name = String(call?.name || call?.toolName || call?.function?.name || '').toLowerCase();
@@ -17,9 +19,13 @@ function isSleepLikeToolCall(call) {
17
19
 
18
20
  export async function runToolPhase(state, round, sent, onToolCall) {
19
21
  const { opts, messages, tools, sessionId, sessionRef, signal, response } = state;
20
- state.noToolTurn.noteToolCallTurn();
21
22
  const calls = response.toolCalls;
23
+ state.noToolTurn.noteToolCallTurn();
22
24
  round.resolveToolCallId.all(calls);
25
+ // A capped turn (turn-limits.mjs): calls past its cap are answered unrun,
26
+ // and a batch after it was told to reply ends it.
27
+ const allowed = callsAllowed(state);
28
+ const overrun = state.turnLimitNoticed === true;
23
29
  state.toolCallsTotal += calls.length;
24
30
  // Surface any mid-turn assistant text (preamble before a tool call) to the
25
31
  // UI. Providers that stream via onTextDelta already rendered it; providers
@@ -36,14 +42,21 @@ export async function runToolPhase(state, round, sent, onToolCall) {
36
42
  // derive the multi-tool adoption ratio without scanning message bodies.
37
43
  // Audit records what runs: a bridged call_tool is recorded as its target.
38
44
  // The wire calls stay the transcript identity (assistant turn below).
39
- const toolBatchId = recordToolBatch(sessionId, resolveExecutionToolCalls(calls, sessionRef), state.iterations);
45
+ const executionCalls = resolveExecutionToolCalls(calls, sessionRef);
46
+ const toolBatchId = recordToolBatch(sessionId, executionCalls, state.iterations);
47
+ // A sensitive fill (a verification code): every stored copy of the
48
+ // transcript keeps a marker instead of the value; the call runs as is.
49
+ rememberSensitiveValues(sessionId, executionCalls);
40
50
  await Promise.resolve(onToolCall?.(state.iterations, calls));
41
51
  const assistantTurnMsg = buildToolCallAssistantMessage(response, { calls, opts });
42
52
  commitAssistantMessage(messages, assistantTurnMsg, opts);
43
53
  try {
44
54
  opts.onToolPhaseStarted?.();
45
- } catch {}
55
+ } catch {
56
+ // the tool-phase-started observer is advisory
57
+ }
46
58
  const toolsT0 = Date.now();
59
+ const resultsFrom = messages.length;
47
60
  ({ dedupStubTotal: state.dedupStubTotal, editCount: state.editCount } = await processToolBatch({
48
61
  calls,
49
62
  messages,
@@ -67,10 +80,13 @@ export async function runToolPhase(state, round, sent, onToolCall) {
67
80
  pushToolResultMessage: state.pushToolResultMessage,
68
81
  throwIfAborted: state.throwIfAborted,
69
82
  repeatFailLimit: REPEAT_FAIL_LIMIT,
83
+ callsAllowed: allowed,
84
+ wrapUpAt: turnLimitsOf(state.opts)?.wrapUpAt,
70
85
  }));
71
86
  // A turn recovered from a stream cut that dropped an in-flight tool call
72
87
  // carries a runtime notice for the model, delivered after its results.
73
88
  if (response.recoveryNotice) messages.push(response.recoveryNotice);
89
+ settleTurnLimits(state, overrun);
74
90
  const toolsEndedAt = Date.now();
75
91
  // Starts the step judgment; the next request uses it if it has resolved by
76
92
  // then (send-phase.mjs applyStepEffort), else the one after.
@@ -81,7 +97,9 @@ export async function runToolPhase(state, round, sent, onToolCall) {
81
97
  calls: calls.length,
82
98
  elapsedMs: toolsEndedAt - toolsT0,
83
99
  });
84
- } catch {}
100
+ } catch {
101
+ // the tool-phase-completed observer is advisory
102
+ }
85
103
  traceLoopPhaseTiming({
86
104
  iteration: round.nextIteration,
87
105
  preSendMs: sent.preSendMs,
@@ -91,6 +109,6 @@ export async function runToolPhase(state, round, sent, onToolCall) {
91
109
  });
92
110
  state.lastToolBatchEndedAt = toolsEndedAt;
93
111
  state.toolBatchJustCompleted = true;
94
- state.noToolTurn.noteToolBatchCompleted();
112
+ state.noToolTurn.noteToolBatchCompleted(calls, messages.slice(resultsFrom));
95
113
  state.lastToolBatchHadSleep = calls.some(isSleepLikeToolCall);
96
114
  }
@@ -0,0 +1,60 @@
1
+ /**
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.
10
+ */
11
+
12
+ const NOTICE =
13
+ '[mixdog-runtime] This turn has reached its tool-call or time limit: no further tool call runs. Reply now with your final answer from what you already have.';
14
+ const REFUSAL =
15
+ '[turn-limit] This turn has reached its tool-call or time limit; this call was not run. Reply now without tools.';
16
+
17
+ /** `{ maxToolCalls, wrapUpAt }` of a capped turn (Infinity where unset), else null. */
18
+ export function turnLimitsOf(opts) {
19
+ const limits = opts?.turnLimits;
20
+ if (!limits || typeof limits !== 'object') return null;
21
+ return {
22
+ maxToolCalls: Number.isInteger(limits.maxToolCalls) && limits.maxToolCalls >= 0 ? limits.maxToolCalls : Infinity,
23
+ wrapUpAt: Number.isFinite(limits.wrapUpAt) ? limits.wrapUpAt : Infinity,
24
+ };
25
+ }
26
+
27
+ /** How many calls of the next batch may run, before the loop counts them. */
28
+ export function callsAllowed(state, now = Date.now()) {
29
+ const limits = turnLimitsOf(state.opts);
30
+ if (!limits) return Infinity;
31
+ if (state.turnLimitNoticed || now >= limits.wrapUpAt) return 0;
32
+ return Math.max(0, limits.maxToolCalls - state.toolCallsTotal);
33
+ }
34
+
35
+ /** The answer to a call the cap kept from running. */
36
+ export function turnLimitRefusal(call) {
37
+ return { role: 'tool', content: REFUSAL, toolCallId: call.id, toolKind: 'error' };
38
+ }
39
+
40
+ /**
41
+ * After a tool batch: `overrun` when the batch came after the turn was told
42
+ * to reply, which ends the turn (state.turnLimitEnded). Otherwise, once a cap
43
+ * is reached, the model is told once to reply.
44
+ */
45
+ export function settleTurnLimits(state, overrun, now = Date.now()) {
46
+ if (overrun) {
47
+ state.turnLimitEnded = true;
48
+ return;
49
+ }
50
+ const limits = turnLimitsOf(state.opts);
51
+ if (!limits || state.turnLimitNoticed) return;
52
+ if (state.toolCallsTotal < limits.maxToolCalls && now < limits.wrapUpAt) return;
53
+ state.turnLimitNoticed = true;
54
+ state.messages.push({ role: 'user', content: NOTICE, meta: { source: 'turn-limit' } });
55
+ }
56
+
57
+ /** The turn's end after an overrun: no reply. */
58
+ export function endedWithoutReply(response) {
59
+ return { ...response, content: '', toolCalls: [] };
60
+ }
@@ -9,6 +9,7 @@ import {
9
9
  drainPendingMessages,
10
10
  releasePendingMessages,
11
11
  } from './pending-messages.mjs';
12
+ import { promptOriginOf } from '../../../../shared/prompt-origin.mjs';
12
13
 
13
14
  const SYNTHETIC_PROMPT_SOURCES = ['goal-continuation', 'goal-closeout'];
14
15
 
@@ -35,6 +36,7 @@ const tailTurnFor = (group) => ({
35
36
  content: group.content,
36
37
  entries: group.entries,
37
38
  ...(group.hop ? { hop: group.hop } : {}),
39
+ ...(group.origin ? { origin: group.origin } : {}),
38
40
  ...(group.mode === PENDING_MODE_TASK_NOTIFICATION
39
41
  ? promptSourceFields(PENDING_MODE_TASK_NOTIFICATION, group.execution)
40
42
  : {}),
@@ -49,13 +51,19 @@ const queuedTurnInput = (tailTurn) => ({
49
51
  pendingEntries: tailTurn.entries,
50
52
  ...(tailTurn.hop ? { hop: tailTurn.hop } : {}),
51
53
  promptSource: promptSourceFields(tailTurn.source, tailTurn.execution),
54
+ ...(tailTurn.origin ? { promptOrigin: tailTurn.origin } : {}),
52
55
  });
53
56
 
54
- export function createAskPromptQueue({ sessionId, promptSource }) {
57
+ // The caller's provenance of its initial prompt, apart from its presentation
58
+ // source: an inbound channel message or an agent report (shared/prompt-origin.mjs).
59
+ const initialPromptOriginFor = (promptOrigin) => promptOriginOf(promptOrigin);
60
+
61
+ export function createAskPromptQueue({ sessionId, promptSource, promptOrigin }) {
55
62
  // Local FIFO of follow-up prompts drained from the pending-message queue
56
63
  // after each turn — keeps queued `agent type=send` messages in order.
57
64
  const tail = [];
58
65
  let initialPromptSource = initialPromptSourceFor(promptSource);
66
+ let initialPromptOrigin = initialPromptOriginFor(promptOrigin);
59
67
  // Queue entries of the input nextTurn handed out that no turn owns yet.
60
68
  // Once a turn starts, its failure path acks or releases them instead.
61
69
  let unstartedEntries = [];
@@ -67,13 +75,15 @@ export function createAskPromptQueue({ sessionId, promptSource }) {
67
75
  * and provenance — or null when nothing model-visible is left to run. */
68
76
  const nextTurn = (original) => {
69
77
  const source = initialPromptSource;
78
+ const origin = initialPromptOrigin;
70
79
  initialPromptSource = null;
80
+ initialPromptOrigin = null;
71
81
  let input;
72
82
  if (tail.length > 0) {
73
83
  // After the first turn, the next prompt comes from the drained queue.
74
84
  input = queuedTurnInput(tail.shift());
75
85
  } else if (hasModelVisiblePromptContent(original.prompt)) {
76
- input = { ...original, pendingEntries: [], promptSource: source };
86
+ input = { ...original, pendingEntries: [], promptSource: source, ...(origin ? { promptOrigin: origin } : {}) };
77
87
  } else {
78
88
  // Idle resume: TUI kicks an empty ask() after execution completions
79
89
  // mirror model-visible bodies into session pending. Drain that queue
@@ -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 { cacheWarmTurnKind, endCacheWarmTurn, failedTurnProvenance, noteCacheWarmTurn } from '../cache-warm.mjs';
22
23
 
23
24
  export { _api_call_with_interrupt } from './ask-call-interrupt.mjs';
24
25
  export {
@@ -168,7 +169,14 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
168
169
  // return value (the queued tail turns supersede the original prompt's
169
170
  // result, mirroring how a live chat returns the latest turn).
170
171
  let result;
171
- const queue = createAskPromptQueue({ sessionId, promptSource: askOpts.promptSource });
172
+ const queue = createAskPromptQueue({
173
+ sessionId,
174
+ promptSource: askOpts.promptSource,
175
+ promptOrigin: askOpts.promptOrigin,
176
+ });
177
+ // The turn the prompt-cache warmer is told about last; it forgets it when
178
+ // the turn ends (endCacheWarmTurn), whatever way the ask unwinds.
179
+ let warmTurn = null;
172
180
  // Hoisted so the outer finally (which runs once after the whole turn loop)
173
181
  // can compare against the last turn's generation.
174
182
  let askGeneration = 0;
@@ -213,6 +221,12 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
213
221
  recoverCheckpoint: !crashRecoveryChecked,
214
222
  emitStart: !sessionStartEmitted,
215
223
  });
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));
216
230
  crashRecoveryChecked = true;
217
231
  sessionStartEmitted = true;
218
232
  askGeneration = claimed.askGeneration;
@@ -234,6 +248,7 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
234
248
  askStartedAt,
235
249
  });
236
250
  result = turn.result;
251
+ endCacheWarmTurn(sessionId, warmTurn);
237
252
  // Turn complete. Drain the pending-message queue: any `agent type=send`
238
253
  // that arrived while this turn was in flight runs next, in order, as a
239
254
  // follow-up user turn. The mutex is still held, so a send racing this
@@ -264,6 +279,9 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
264
279
  return result;
265
280
  }
266
281
  } finally {
282
+ // A failed/cancelled turn ends here too: its warmer turn entry goes (a
283
+ // later ask's own turn is never removed by this one).
284
+ endCacheWarmTurn(sessionId, warmTurn);
267
285
  // A failed/cancelled turn ends the ask: the follow-up turns drained
268
286
  // behind it never ran, so their entries go back to the pending queue.
269
287
  queue.releaseUnstarted();
@@ -3,6 +3,7 @@
3
3
  // its empty-final forensic record), the usage/cost ledger row, and the
4
4
  // opaque provider continuation state.
5
5
  import { cloneProviderReplay } from '../../providers/lib/provider-replay.mjs';
6
+ import { turnReminderField } from '../loop/assistant-commit.mjs';
6
7
  import { logLlmCall } from '../../usage-log.mjs';
7
8
  import { runAbortable } from '../../../../shared/abort-race.mjs';
8
9
  import { getAgentRuntimeSync } from './agent-runtime-singleton.mjs';
@@ -31,6 +32,7 @@ export function appendAssistantTurnMessage({ session, sessionId, result, transcr
31
32
  ...(result.providerMetadata && typeof result.providerMetadata === 'object'
32
33
  ? { providerMetadata: result.providerMetadata }
33
34
  : {}),
35
+ ...turnReminderField(result),
34
36
  // Keep terminal provider evidence for non-empty turns too. A safety
35
37
  // classifier can emit narration and then refuse; omitting this
36
38
  // metadata made that shape indistinguishable from an ordinary