mixdog 1.2.5 → 1.3.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 (493) hide show
  1. package/LICENSES/simple-icons-CC0-1.0.txt +30 -0
  2. package/NOTICE.md +19 -0
  3. package/package.json +4 -1
  4. package/scripts/check-model-recommendations.mjs +89 -0
  5. package/scripts/gate-local.mjs +7 -0
  6. package/scripts/lib/gate-scheduler.mjs +2 -2
  7. package/scripts/lib/isolated-test-env.mjs +16 -0
  8. package/scripts/lib/run-node-tests.mjs +5 -14
  9. package/scripts/provider-pingpong-live.mjs +9 -9
  10. package/src/agents/advisor/AGENT.md +4 -4
  11. package/src/agents/advisor/agent.json +1 -1
  12. package/src/agents/reviewer/AGENT.md +5 -4
  13. package/src/agents/reviewer/agent.json +1 -1
  14. package/src/defaults/shipped-history.json +17 -0
  15. package/src/defaults/skills/browser-use/SKILL.md +7 -1
  16. package/src/defaults/skills/computer-use/SKILL.md +3 -1
  17. package/src/defaults/skills/memory-management/SKILL.md +4 -1
  18. package/src/defaults/skills/setup/references/actions.md +11 -4
  19. package/src/defaults/skills/setup-guide/SKILL.md +175 -0
  20. package/src/defaults/skills/setup-guide/model-recommendations.json +333 -0
  21. package/src/defaults/skills/visualize/SKILL.md +3 -2
  22. package/src/lib/rules-builder.cjs +56 -7
  23. package/src/output-styles/common.md +22 -6
  24. package/src/output-styles/detailed.md +7 -5
  25. package/src/output-styles/minimal.md +7 -3
  26. package/src/output-styles/simple.md +2 -3
  27. package/src/rules/agent/AGENT.md +7 -1
  28. package/src/rules/agent-mode/AGENT_MODE.md +24 -0
  29. package/src/rules/agent-mode/MIXY.md +21 -0
  30. package/src/rules/agent-mode/ROUTINE.md +6 -0
  31. package/src/rules/lead/LEAD.md +9 -4
  32. package/src/rules/shared/10-tool-workflow.md +2 -1
  33. package/src/rules/shared/60-verification.md +4 -5
  34. package/src/rules/shared/70-delivery.md +3 -0
  35. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -1
  36. package/src/runtime/agent/orchestrator/agent-runtime/room-supervisor-completion.mjs +86 -0
  37. package/src/runtime/agent/orchestrator/config.mjs +6 -0
  38. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +77 -14
  39. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +182 -13
  40. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +5 -3
  41. package/src/runtime/agent/orchestrator/internal-agents.mjs +10 -0
  42. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +215 -0
  43. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +58 -64
  44. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +10 -1
  45. package/src/runtime/agent/orchestrator/mcp/client.mjs +122 -15
  46. package/src/runtime/agent/orchestrator/mcp/oauth-account.mjs +97 -0
  47. package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +2 -3
  48. package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +49 -0
  49. package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +59 -9
  50. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +32 -7
  51. package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +45 -6
  52. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +195 -6
  53. package/src/runtime/agent/orchestrator/mcp/server-connectors.mjs +17 -0
  54. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +9 -2
  55. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  56. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +41 -19
  57. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +3 -2
  58. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-start.mjs +4 -1
  59. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events.mjs +2 -0
  60. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn.mjs +3 -0
  61. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +2 -1
  62. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +3 -1
  63. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +11 -5
  64. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +4 -0
  65. package/src/runtime/agent/orchestrator/providers/catalog-deadline.mjs +163 -0
  66. package/src/runtime/agent/orchestrator/providers/catalog-failures.mjs +34 -0
  67. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  68. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +38 -14
  69. package/src/runtime/agent/orchestrator/providers/lib/oauth-page.mjs +100 -26
  70. package/src/runtime/agent/orchestrator/providers/lib/send-callbacks.mjs +24 -0
  71. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +13 -0
  72. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +2 -1
  73. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
  74. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +4 -0
  75. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +3 -1
  76. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +2 -0
  77. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +49 -4
  78. package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +0 -7
  79. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +7 -1
  80. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
  81. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +2 -0
  82. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
  83. package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +17 -12
  84. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +5 -1
  85. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +11 -2
  86. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +1 -0
  87. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +45 -16
  88. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -0
  89. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +1 -0
  90. package/src/runtime/agent/orchestrator/providers/openai-ws-connect.mjs +196 -0
  91. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +74 -9
  92. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +14 -1
  93. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +2 -0
  94. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth-login.mjs +194 -0
  95. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth-tokens.mjs +353 -0
  96. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +293 -0
  97. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +33 -100
  98. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +23 -14
  99. package/src/runtime/agent/orchestrator/providers/registry.mjs +41 -27
  100. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +98 -4
  101. package/src/runtime/agent/orchestrator/runtime-core/context-status-shape.mjs +4 -0
  102. package/src/runtime/agent/orchestrator/runtime-core/extension-scopes.mjs +6 -4
  103. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +15 -8
  104. package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +1 -0
  105. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +12 -6
  106. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +32 -5
  107. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +10 -4
  108. package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +4 -1
  109. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +50 -20
  110. package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +2 -0
  111. package/src/runtime/agent/orchestrator/session/agent-home.d.mts +4 -0
  112. package/src/runtime/agent/orchestrator/session/agent-home.mjs +33 -0
  113. package/src/runtime/agent/orchestrator/session/agent-mode.mjs +62 -0
  114. package/src/runtime/agent/orchestrator/session/agent-pool-progress.mjs +15 -3
  115. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.d.mts +31 -0
  116. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.mjs +241 -0
  117. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +162 -0
  118. package/src/runtime/agent/orchestrator/session/agent-room.mjs +641 -0
  119. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +7 -1
  120. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +74 -13
  121. package/src/runtime/agent/orchestrator/session/compact/forked-summary.mjs +102 -0
  122. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +8 -2
  123. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +16 -0
  124. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +2 -0
  125. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +6 -2
  126. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +165 -0
  127. package/src/runtime/agent/orchestrator/session/human-gate/held-message.mjs +95 -0
  128. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +221 -0
  129. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +195 -0
  130. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +167 -0
  131. package/src/runtime/agent/orchestrator/session/lifecycle-scan.mjs +9 -0
  132. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +3 -1
  133. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +39 -2
  134. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +10 -0
  135. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +16 -1
  136. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +82 -33
  137. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +11 -4
  138. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +5 -4
  139. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +13 -3
  140. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +7 -0
  141. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +1 -0
  142. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +6 -1
  143. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
  144. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +2 -0
  145. package/src/runtime/agent/orchestrator/session/manager/ask-turn-steering-drain.mjs +13 -5
  146. package/src/runtime/agent/orchestrator/session/manager/pending-lifecycle-epoch.mjs +0 -8
  147. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +10 -7
  148. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +62 -22
  149. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +44 -16
  150. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +22 -5
  151. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +23 -1
  152. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +81 -34
  153. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +39 -4
  154. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +27 -5
  155. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +55 -8
  156. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +16 -0
  157. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +3 -2
  158. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +6 -0
  159. package/src/runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs +58 -0
  160. package/src/runtime/agent/orchestrator/session/manager.mjs +2 -2
  161. package/src/runtime/agent/orchestrator/session/mixy-name.mjs +24 -0
  162. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +32 -5
  163. package/src/runtime/agent/orchestrator/session/request-fork.mjs +47 -0
  164. package/src/runtime/agent/orchestrator/session/routine-result.d.mts +70 -0
  165. package/src/runtime/agent/orchestrator/session/routine-result.mjs +277 -0
  166. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +16 -1
  167. package/src/runtime/agent/orchestrator/session/side-question.mjs +5 -43
  168. package/src/runtime/agent/orchestrator/session/store/fs-probe.mjs +10 -0
  169. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +73 -6
  170. package/src/runtime/agent/orchestrator/session/store/load-session.mjs +40 -0
  171. package/src/runtime/agent/orchestrator/session/store/save-fault.mjs +15 -0
  172. package/src/runtime/agent/orchestrator/session/store/session-paths.mjs +12 -0
  173. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +8 -0
  174. package/src/runtime/agent/orchestrator/session/store-agent-worker-header.mjs +36 -0
  175. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +258 -43
  176. package/src/runtime/agent/orchestrator/session/store-record-strict-scan.mjs +325 -0
  177. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +84 -21
  178. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +44 -12
  179. package/src/runtime/agent/orchestrator/session/store-summary-last-message.mjs +46 -0
  180. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +46 -30
  181. package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +0 -4
  182. package/src/runtime/agent/orchestrator/session/store-transcript-cache.mjs +144 -45
  183. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +32 -13
  184. package/src/runtime/agent/orchestrator/session/store-transcript-worker.mjs +364 -38
  185. package/src/runtime/agent/orchestrator/session/store.mjs +9 -2
  186. package/src/runtime/agent/orchestrator/session/task-wait-control.mjs +43 -12
  187. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +23 -6
  188. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +44 -1
  189. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +12 -6
  190. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +13 -7
  191. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/bashism-rewrite.mjs +1 -1
  192. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/benign-exit.mjs +7 -1
  193. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +40 -7
  194. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +37 -34
  195. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/inline-hoist.mjs +1 -1
  196. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +0 -4
  197. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +6 -0
  198. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +29 -27
  199. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +1 -1
  200. package/src/runtime/agent/orchestrator/tools/builtin/find-files-tool.mjs +28 -11
  201. package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.mjs +12 -3
  202. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +203 -101
  203. package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.mjs +90 -41
  204. package/src/runtime/agent/orchestrator/tools/builtin/lib/list-helpers.mjs +17 -1
  205. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-delivery.mjs +199 -0
  206. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +12 -0
  207. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-task-output.mjs +165 -39
  208. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +25 -13
  209. package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +113 -5
  210. package/src/runtime/agent/orchestrator/tools/builtin/read-batch.mjs +7 -6
  211. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +19 -4
  212. package/src/runtime/agent/orchestrator/tools/builtin/read-line-collector.mjs +5 -3
  213. package/src/runtime/agent/orchestrator/tools/builtin/read-single-render.mjs +25 -7
  214. package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +2 -1
  215. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-render.mjs +3 -1
  216. package/src/runtime/agent/orchestrator/tools/builtin/search-glob-tool.mjs +12 -5
  217. package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +61 -22
  218. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +32 -8
  219. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +193 -19
  220. package/src/runtime/agent/orchestrator/tools/builtin/shell-direct-exe.mjs +57 -7
  221. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +123 -32
  222. package/src/runtime/agent/orchestrator/tools/builtin/shell-lossless-compact.mjs +26 -1
  223. package/src/runtime/agent/orchestrator/tools/builtin/shell-output.mjs +13 -28
  224. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +90 -11
  225. package/src/runtime/agent/orchestrator/tools/builtin/wait-budgets.mjs +23 -0
  226. package/src/runtime/agent/orchestrator/tools/builtin.mjs +54 -10
  227. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +22 -28
  228. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +11 -7
  229. package/src/runtime/agent/orchestrator/tools/destructive-warning.mjs +126 -9
  230. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
  231. package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +15 -6
  232. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +64 -2
  233. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +10 -2
  234. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promotion-results.mjs +13 -3
  235. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +8 -0
  236. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +6 -1
  237. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +1 -1
  238. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +104 -55
  239. package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +7 -2
  240. package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +39 -23
  241. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +6 -2
  242. package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +13 -13
  243. package/src/runtime/attachments/office-extract.mjs +11 -9
  244. package/src/runtime/browser-bridge/client.mjs +24 -0
  245. package/src/runtime/channels/lib/scheduler.mjs +40 -12
  246. package/src/runtime/channels/lib/schedules-db.mjs +71 -12
  247. package/src/runtime/channels/lib/webhook/session-dispatch.mjs +36 -16
  248. package/src/runtime/channels/lib/webhook.mjs +0 -1
  249. package/src/runtime/channels/lib/webhooks-db.mjs +100 -10
  250. package/src/runtime/channels/lib/worker-main.mjs +17 -20
  251. package/src/runtime/computer-bridge/client.mjs +28 -1
  252. package/src/runtime/connectors/gmail-app-password.mjs +690 -0
  253. package/src/runtime/connectors/google-mcp.mjs +446 -0
  254. package/src/runtime/memory/lib/core-memory-access.mjs +69 -0
  255. package/src/runtime/memory/lib/core-memory-file.mjs +16 -8
  256. package/src/runtime/memory/lib/core-memory-index.mjs +15 -9
  257. package/src/runtime/memory/lib/core-memory-management.mjs +10 -4
  258. package/src/runtime/memory/lib/core-memory-store.mjs +53 -30
  259. package/src/runtime/memory/lib/core-memory-uniqueness.mjs +10 -6
  260. package/src/runtime/memory/lib/embedding-tokenizer.mjs +32 -18
  261. package/src/runtime/memory/lib/http-router/session-routes.mjs +3 -2
  262. package/src/runtime/memory/lib/memory-action-handlers/core-actions.mjs +44 -14
  263. package/src/runtime/memory/lib/memory-schema/core-and-meta.mjs +11 -0
  264. package/src/runtime/memory/lib/memory.mjs +7 -1
  265. package/src/runtime/memory/lib/query-core-recall.mjs +9 -1
  266. package/src/runtime/memory/lib/query-hybrid-search.mjs +2 -0
  267. package/src/runtime/memory/lib/runtime-fetcher.mjs +1 -1
  268. package/src/runtime/memory/lib/tool-call-handler.mjs +9 -5
  269. package/src/runtime/memory/tool-defs.mjs +5 -0
  270. package/src/runtime/office/authoring/pptx-script-normalize.mjs +11 -8
  271. package/src/runtime/office/core/tabular.mjs +1 -1
  272. package/src/runtime/office/design/library/design-template-inspect.mjs +7 -4
  273. package/src/runtime/office/portable/docx-formatting.mjs +1 -1
  274. package/src/runtime/office/portable/docx-runs.mjs +1 -1
  275. package/src/runtime/office/portable/docx-tracked-edits.mjs +2 -2
  276. package/src/runtime/office/portable/portable-docx-edits.mjs +11 -10
  277. package/src/runtime/office/portable/portable-docx-operations.mjs +21 -17
  278. package/src/runtime/office/portable/portable-docx-parts.mjs +15 -9
  279. package/src/runtime/office/portable/portable-docx-styles.mjs +4 -1
  280. package/src/runtime/office/portable/portable-docx-xml.mjs +9 -9
  281. package/src/runtime/office/portable/portable-opc.mjs +27 -13
  282. package/src/runtime/office/portable/portable-pivot.mjs +3 -3
  283. package/src/runtime/office/portable/portable-pptx-chart.mjs +3 -3
  284. package/src/runtime/office/portable/portable-pptx-charts.mjs +24 -18
  285. package/src/runtime/office/portable/portable-pptx-core.mjs +5 -5
  286. package/src/runtime/office/portable/portable-pptx-deck.mjs +8 -4
  287. package/src/runtime/office/portable/portable-pptx-package.mjs +7 -4
  288. package/src/runtime/office/portable/portable-pptx-shapes.mjs +18 -9
  289. package/src/runtime/office/portable/portable-sheet-parts.mjs +9 -3
  290. package/src/runtime/office/portable/portable-sheet-xml.mjs +5 -5
  291. package/src/runtime/office/portable/portable-xlsx-charts.mjs +4 -1
  292. package/src/runtime/office/portable/portable-xlsx-drawings.mjs +10 -6
  293. package/src/runtime/office/portable/portable-xlsx-operations.mjs +8 -5
  294. package/src/runtime/office/portable/portable-xlsx-pivot-ops.mjs +1 -1
  295. package/src/runtime/office/portable/portable-xlsx-reference-shift.mjs +11 -7
  296. package/src/runtime/office/portable/portable-xlsx-sheet-edits.mjs +6 -3
  297. package/src/runtime/office/portable/portable-xml.mjs +1 -1
  298. package/src/runtime/shared/agent-avatar-sprites.mjs +445 -0
  299. package/src/runtime/shared/agent-avatar.d.mts +64 -0
  300. package/src/runtime/shared/agent-avatar.mjs +320 -0
  301. package/src/runtime/shared/agent-route-config.d.mts +3 -0
  302. package/src/runtime/shared/agent-route-config.mjs +16 -0
  303. package/src/runtime/shared/atomic-file.mjs +36 -14
  304. package/src/runtime/shared/background-tasks.mjs +60 -20
  305. package/src/runtime/shared/child-spawn-gate.mjs +26 -16
  306. package/src/runtime/shared/config.mjs +8 -11
  307. package/src/runtime/shared/connector-accounts.mjs +15 -0
  308. package/src/runtime/shared/connector-catalog.d.mts +52 -0
  309. package/src/runtime/shared/connector-catalog.mjs +230 -0
  310. package/src/runtime/shared/connector-triggers.d.mts +3 -0
  311. package/src/runtime/shared/connector-triggers.mjs +43 -0
  312. package/src/runtime/shared/file-lock.mjs +7 -1
  313. package/src/runtime/shared/file-permissions-worker.mjs +38 -0
  314. package/src/runtime/shared/file-permissions.d.mts +19 -4
  315. package/src/runtime/shared/file-permissions.mjs +310 -4
  316. package/src/runtime/shared/git-runner-worker.mjs +6 -78
  317. package/src/runtime/shared/google-oauth-client.mjs +24 -0
  318. package/src/runtime/shared/held-request-host.d.mts +12 -0
  319. package/src/runtime/shared/held-request-host.mjs +71 -0
  320. package/src/runtime/shared/llm/http-agent.mjs +7 -3
  321. package/src/runtime/shared/llm/provider-catalog-registry.mjs +10 -0
  322. package/src/runtime/shared/llm/usage-accounting.mjs +2 -0
  323. package/src/runtime/shared/llm/usage-ledger-import.mjs +32 -3
  324. package/src/runtime/shared/llm/usage-ledger-rollup.mjs +12 -5
  325. package/src/runtime/shared/llm/usage-ledger-worker.mjs +1 -1
  326. package/src/runtime/shared/llm/usage-ledger.mjs +20 -5
  327. package/src/runtime/shared/memory-snapshot.mjs +5 -1
  328. package/src/runtime/shared/notify-trace.mjs +8 -1
  329. package/src/runtime/shared/plain-preview.d.mts +1 -0
  330. package/src/runtime/shared/plain-preview.mjs +28 -0
  331. package/src/runtime/shared/pristine-execution-contract.json +5 -1
  332. package/src/runtime/shared/process-runner-worker.mjs +7 -0
  333. package/src/runtime/shared/process-runner.mjs +38 -0
  334. package/src/runtime/shared/provider-accounts.mjs +2 -1
  335. package/src/runtime/shared/provider-api-key.mjs +0 -1
  336. package/src/runtime/shared/read-row-numbers.mjs +1 -1
  337. package/src/runtime/shared/resource-admission.mjs +60 -18
  338. package/src/runtime/shared/routine-limits.d.mts +11 -0
  339. package/src/runtime/shared/routine-limits.mjs +21 -0
  340. package/src/runtime/shared/routine-trigger.d.mts +10 -0
  341. package/src/runtime/shared/routine-trigger.mjs +44 -0
  342. package/src/runtime/shared/run-child.mjs +84 -0
  343. package/src/runtime/shared/schedule-model-ref.mjs +9 -10
  344. package/src/runtime/shared/session-activity-signal.mjs +23 -0
  345. package/src/runtime/shared/shell-output-summary.mjs +23 -0
  346. package/src/runtime/shared/singleton-owner.mjs +126 -42
  347. package/src/runtime/shared/slow-span.mjs +4 -12
  348. package/src/runtime/shared/statusline/statusline-segments.mjs +13 -1
  349. package/src/runtime/shared/tool-output-limit.mjs +4 -2
  350. package/src/runtime/shared/tool-output-marker.mjs +58 -0
  351. package/src/runtime/shared/transcript-metadata.mjs +10 -0
  352. package/src/runtime/shared/turn-changes.mjs +0 -8
  353. package/src/runtime/shared/usage-readout-totals.mjs +56 -0
  354. package/src/runtime/shared/user-data-guard.mjs +131 -34
  355. package/src/runtime/shared/worktrees/activity-gate.mjs +33 -8
  356. package/src/runtime/shared/worktrees/git-head.mjs +20 -2
  357. package/src/runtime/shared/worktrees/lock.mjs +3 -2
  358. package/src/runtime/terminal-bridge/tool-defs.mjs +1 -1
  359. package/src/runtime/tidy/runners/biome.mjs +3 -1
  360. package/src/runtime/tidy/shadow.mjs +2 -1
  361. package/src/runtime/web-search/index.mjs +5 -7
  362. package/src/runtime/web-search/lib/formatter.mjs +42 -6
  363. package/src/runtime/web-search/tool-defs.mjs +6 -1
  364. package/src/session-runtime/agent-home-delivery.mjs +459 -0
  365. package/src/session-runtime/agent-profile-changes.mjs +21 -0
  366. package/src/session-runtime/agent-room-delivery.mjs +92 -0
  367. package/src/session-runtime/agent-task-sessions.mjs +200 -0
  368. package/src/session-runtime/boot/apis.mjs +50 -18
  369. package/src/session-runtime/boot/facade.mjs +5 -8
  370. package/src/session-runtime/boot/infrastructure.mjs +28 -17
  371. package/src/session-runtime/boot/tools.mjs +3 -0
  372. package/src/session-runtime/channel-config-api.mjs +53 -5
  373. package/src/session-runtime/cwd-plugins/core-memory-context.mjs +36 -20
  374. package/src/session-runtime/cwd-plugins/plugin-status.mjs +6 -1
  375. package/src/session-runtime/held-requests.mjs +228 -0
  376. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +121 -0
  377. package/src/session-runtime/internal-tool-executor/feature-tools.mjs +5 -1
  378. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +31 -2
  379. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +31 -2
  380. package/src/session-runtime/internal-tool-executor.mjs +2 -0
  381. package/src/session-runtime/lifecycle/session-catalog.mjs +9 -0
  382. package/src/session-runtime/lifecycle/session-switch/context-switch.mjs +4 -3
  383. package/src/session-runtime/lifecycle/session-switch/resume.mjs +11 -4
  384. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +3 -3
  385. package/src/session-runtime/lifecycle/shared.mjs +25 -2
  386. package/src/session-runtime/lifecycle/teardown/session-close.mjs +2 -0
  387. package/src/session-runtime/lifecycle/teardown.mjs +5 -2
  388. package/src/session-runtime/lifecycle-api.mjs +3 -1
  389. package/src/session-runtime/mcp-glue/server-catalog.mjs +8 -0
  390. package/src/session-runtime/mcp-glue/server-input.mjs +3 -0
  391. package/src/session-runtime/mcp-oauth-login.mjs +112 -15
  392. package/src/session-runtime/model-route/set-route.mjs +14 -3
  393. package/src/session-runtime/owned-automation-run.mjs +229 -0
  394. package/src/session-runtime/provider-auth/login.mjs +10 -6
  395. package/src/session-runtime/provider-auth/setup.mjs +19 -2
  396. package/src/session-runtime/provider-catalog-cache.mjs +101 -25
  397. package/src/session-runtime/provider-models/catalog-load.mjs +14 -3
  398. package/src/session-runtime/provider-models/row-hydration.mjs +4 -0
  399. package/src/session-runtime/provider-models.mjs +76 -10
  400. package/src/session-runtime/provider-readiness.mjs +4 -0
  401. package/src/session-runtime/resource-api.mjs +5 -0
  402. package/src/session-runtime/resource-mcp-api.mjs +107 -13
  403. package/src/session-runtime/resource-skills-api.mjs +12 -0
  404. package/src/session-runtime/room-member-gate.mjs +48 -0
  405. package/src/session-runtime/routine-home-turn.mjs +156 -0
  406. package/src/session-runtime/routine-owner.mjs +48 -0
  407. package/src/session-runtime/schedule-session-run.mjs +30 -64
  408. package/src/session-runtime/services/agent-tool/close-flow.mjs +2 -1
  409. package/src/session-runtime/services/agent-tool/helpers.mjs +10 -0
  410. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +10 -2
  411. package/src/session-runtime/services/agent-tool/job-views/session-progress.mjs +13 -4
  412. package/src/session-runtime/services/agent-tool/notify.mjs +15 -4
  413. package/src/session-runtime/services/agent-tool/restart-notify.mjs +134 -0
  414. package/src/session-runtime/services/agent-tool/send-flow.mjs +24 -1
  415. package/src/session-runtime/services/agent-tool/spawn-flow/deferred-spawn.mjs +2 -1
  416. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +4 -3
  417. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +19 -4
  418. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +2 -0
  419. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +5 -1
  420. package/src/session-runtime/services/agent-tool/tag-registry/tag-lookup.mjs +8 -3
  421. package/src/session-runtime/services/agent-tool/tag-registry.mjs +3 -0
  422. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +4 -0
  423. package/src/session-runtime/services/agent-tool/worker-index.mjs +44 -10
  424. package/src/session-runtime/services/agent-tool.mjs +20 -0
  425. package/src/session-runtime/services/channel-admin.mjs +93 -24
  426. package/src/session-runtime/services/memory-access.mjs +37 -0
  427. package/src/session-runtime/services/provider-admin.mjs +166 -59
  428. package/src/session-runtime/services/schedule-access.mjs +91 -0
  429. package/src/session-runtime/services/usage-dashboard-model.mjs +2 -2
  430. package/src/session-runtime/services/usage-dashboard.mjs +27 -69
  431. package/src/session-runtime/session-create/session-options.mjs +34 -7
  432. package/src/session-runtime/session-hooks.mjs +16 -8
  433. package/src/session-runtime/setup-tool/executor.mjs +68 -6
  434. package/src/session-runtime/setup-tool/extended-actions.mjs +72 -14
  435. package/src/session-runtime/setup-tool/settings-contract.mjs +8 -6
  436. package/src/session-runtime/setup-tool/tool-defs.mjs +8 -0
  437. package/src/session-runtime/skills-api.mjs +16 -1
  438. package/src/session-runtime/tool-policy-refresh.mjs +238 -29
  439. package/src/session-runtime/turn/session-ops.mjs +10 -3
  440. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +11 -2
  441. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +3 -0
  442. package/src/session-runtime/usage-stats-api.mjs +6 -1
  443. package/src/session-runtime/webhook-session-run.mjs +49 -67
  444. package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +239 -0
  445. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +556 -22
  446. package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +11 -1
  447. package/src/session-runtime/workflow-agents-api.mjs +4 -1
  448. package/src/standalone/channel-transport/http-routes.mjs +10 -9
  449. package/src/standalone/daemon-agent-control.mjs +1 -8
  450. package/src/standalone/daemon-desktop-runtime.mjs +20 -7
  451. package/src/standalone/daemon-owner.mjs +117 -25
  452. package/src/standalone/daemon-session-control.mjs +11 -2
  453. package/src/standalone/daemon.mjs +61 -17
  454. package/src/standalone/session-call-cache.mjs +6 -0
  455. package/src/standalone/session-protocol.mjs +28 -1
  456. package/src/standalone/session-service/retention/busy.mjs +7 -2
  457. package/src/standalone/session-service/session-calls/session-turns.mjs +9 -0
  458. package/src/standalone/session-service.mjs +19 -0
  459. package/src/standalone/session-transport/routes.mjs +7 -6
  460. package/src/tui/app/model-options.mjs +39 -2
  461. package/src/tui/app/model-picker.mjs +16 -2
  462. package/src/tui/app/provider-setup-picker.mjs +1 -2
  463. package/src/tui/app/slash-commands.mjs +20 -0
  464. package/src/tui/dist/index.mjs +487 -128
  465. package/src/tui/session/context-state.mjs +6 -1
  466. package/src/tui/session/notice-surface.mjs +35 -4
  467. package/src/tui/session/notice-text.mjs +0 -1
  468. package/src/tui/session/queue-helpers.mjs +2 -1
  469. package/src/tui/session/session-api/extensions.mjs +4 -0
  470. package/src/tui/session/session-api/intake/submission.mjs +12 -13
  471. package/src/tui/session/session-api/integrations.mjs +9 -5
  472. package/src/tui/session/session-api/lifecycle/session-reset.mjs +13 -3
  473. package/src/tui/session/session-api/lifecycle.mjs +17 -1
  474. package/src/tui/session/session-api/routes.mjs +48 -3
  475. package/src/tui/session/session-flow/drain.mjs +20 -3
  476. package/src/tui/session/session-flow/queue/entry-shape.mjs +2 -1
  477. package/src/tui/session/session-flow/queue/take-entries.mjs +3 -2
  478. package/src/tui/session/session-flow/steering.mjs +104 -24
  479. package/src/tui/session/session-flow.mjs +2 -0
  480. package/src/tui/session/tool-approval.mjs +26 -5
  481. package/src/tui/session/transcript-intake.mjs +6 -2
  482. package/src/tui/session/transcript-spill.mjs +5 -1
  483. package/src/tui/session/tui-steering-persist.mjs +65 -42
  484. package/src/tui/session/turn-live-call.mjs +56 -0
  485. package/src/tui/session/turn-stream.mjs +33 -0
  486. package/src/tui/session/turn.mjs +50 -7
  487. package/src/ui/statusline.mjs +1 -6
  488. package/src/ui/streaming-markdown-heal.mjs +24 -7
  489. package/src/vendor/statusline/bin/statusline-route.mjs +0 -1
  490. package/src/vendor/statusline/src/gateway/route-meta.mjs +0 -1
  491. package/src/workflows/default/WORKFLOW.md +9 -13
  492. package/src/session-runtime/automation-agents.mjs +0 -94
  493. package/src/session-runtime/automation-workflow.mjs +0 -59
@@ -15,6 +15,8 @@ import {
15
15
  SUMMARY_OUTPUT_TOKENS,
16
16
  COMPACT_TARGET_MIN_TOKENS,
17
17
  } from '../compact.mjs';
18
+ import { summarizeOnSharedPrefix } from '../compact/forked-summary.mjs';
19
+ import { retireCompactionFork } from '../side-question-store.mjs';
18
20
 
19
21
  // Conversation summarization triggers independently of the post-compact target:
20
22
  // at 5% of the window, capped so a large window does not carry tens of
@@ -110,6 +112,39 @@ function summarizeConversation(target, ctx) {
110
112
  );
111
113
  }
112
114
 
115
+ function sumUsage(a, b) {
116
+ if (!a || !b) return a || b || null;
117
+ return Object.fromEntries(Object.keys(a).map((key) => [key, (a[key] || 0) + (Number(b[key]) || 0)]));
118
+ }
119
+
120
+ // Summary policy. On the session's own provider and model the summary forks
121
+ // the latest cached request (compact/forked-summary.mjs): the context is
122
+ // already in the prompt cache, so reading it costs a tenth to a twentieth of
123
+ // sending the text-only transcript uncached, and the summary sees the full
124
+ // execution context. A different summary route (the maintenance model)
125
+ // cannot share that cache; there the isolated text-only request runs: on
126
+ // the usage ledger it cost a median $0.16 and 24.5 s against a projected
127
+ // $0.19 and 27 s for a cache-read fork of the main model's median 500k-token
128
+ // context (the fork is cheaper than a same-model isolated request, $0.20).
129
+ // A fork that cannot run or fails falls back to the isolated request.
130
+ async function summarizeOnRoute(route, ctx) {
131
+ const shared = await summarizeOnSharedPrefix({
132
+ sessionId: ctx.sessionId,
133
+ route,
134
+ conversationInput: ctx.conversationInput,
135
+ signal: ctx.signal,
136
+ timeoutMs: ctx.compactPolicy.handoffTimeoutMs,
137
+ });
138
+ if (shared.generated) return shared.generated;
139
+ ctx.signal?.throwIfAborted();
140
+ const generated = await summarizeConversation(route, ctx);
141
+ return {
142
+ ...generated,
143
+ usage: sumUsage(generated.usage, shared.usage),
144
+ diagnostics: { ...generated.diagnostics, mode: 'isolated', sharedPrefixFallback: shared.fallback },
145
+ };
146
+ }
147
+
113
148
  // A maintenance summary model that is out of quota must not take the whole
114
149
  // turn down: the conversation's own model still has budget and can
115
150
  // summarize. Only exhaustion reroutes — every other compaction failure
@@ -118,7 +153,7 @@ function summarizeConversation(target, ctx) {
118
153
  async function summarizeWithQuotaFallback(ctx, routeInput) {
119
154
  const route = await resolveCompactionRoute(routeInput);
120
155
  try {
121
- return { route, generated: await summarizeConversation(route, ctx) };
156
+ return { route, generated: await summarizeOnRoute(route, ctx) };
122
157
  } catch (error) {
123
158
  ctx.signal?.throwIfAborted();
124
159
  const status = Number(error?.status || error?.httpStatus || error?.response?.status || 0);
@@ -132,7 +167,7 @@ async function summarizeWithQuotaFallback(ctx, routeInput) {
132
167
  throw error;
133
168
  }
134
169
  try {
135
- return { route: fallback, generated: await summarizeConversation(fallback, ctx) };
170
+ return { route: fallback, generated: await summarizeOnRoute(fallback, ctx) };
136
171
  } catch (fallbackError) {
137
172
  fallbackError.compactRoute = { provider: fallback.providerName, model: fallback.model };
138
173
  throw fallbackError;
@@ -236,6 +271,8 @@ export async function runFreshContextCompact({
236
271
  diagnostics: { ...result.diagnostics, noOp: true, reason: 'no_token_reduction' },
237
272
  };
238
273
  }
274
+ // The rewritten transcript is no longer what the saved request carried.
275
+ if (result.freshContext) retireCompactionFork(sessionId);
239
276
  return result;
240
277
  };
241
278
  const pipeline = {
@@ -16,6 +16,7 @@ import { isOffReadonlySurface } from './deferred-call-through.mjs';
16
16
  import { BRIDGE_CALL_TOOL_NAME, bridgeErrorText, isToolBridgeEnabled } from '../../runtime-core/tool-bridge.mjs';
17
17
  import { sessionAllowlistExcludes, sessionDisallows } from '../../runtime-core/deferred-tool-availability.mjs';
18
18
  import { providerToolExclusionReason } from './provider-tool-exclusions.mjs';
19
+ import { agentMcpToolPermission, projectMcpToolDisabled } from '../../mcp/agent-connectors.mjs';
19
20
 
20
21
  const WORKER_DENIED_TOOLS = new Set(['agent', 'ask_user', 'session']);
21
22
  const BRIDGE_DISCOVERY_TOOL_NAMES = new Set(['load_tool']);
@@ -122,6 +123,15 @@ export function preDispatchDenyForSession(sessionRef, call) {
122
123
  if (sessionDisallows(sessionRef, schemaName)) {
123
124
  return `Error: tool "${name}" is not available in this session.`;
124
125
  }
126
+ // An agent's connectors: a tool of a server it does not mount, or one set
127
+ // to `disabled`, stays refused though the process-wide registry knows it.
128
+ if (agentMcpToolPermission(sessionRef?.agent, schemaName) === 'disabled') {
129
+ return `Error: tool "${name}" is not available to this agent.`;
130
+ }
131
+ // The project the session runs in may switch a connector off as well.
132
+ if (projectMcpToolDisabled(sessionRef, schemaName)) {
133
+ return `Error: tool "${name}" is switched off in this project.`;
134
+ }
125
135
  if (isOffReadonlySurface(sessionRef, schemaName)) {
126
136
  return `Error: tool "${name}" is not available in this read-only session.`;
127
137
  }
@@ -7,6 +7,7 @@ import { traceCacheBreak } from '../../cache-break-trace.mjs';
7
7
  import { resolveLiveToolCwd } from './tool-exec.mjs';
8
8
  import { notifySkillsApplied, prepareExplicitSkills, recordAppliedSkills } from '../explicit-skills.mjs';
9
9
  import { addUsage, usageDeltaEvent } from './usage.mjs';
10
+ import { isVisibleStreamProgress } from '../../../../shared/stream-progress.mjs';
10
11
  import { prepareProviderRequest } from './request-boundary.mjs';
11
12
  import { applyStepEffort } from './step-auto-effort.mjs';
12
13
  import { projectProviderRequest } from './request-projection.mjs';
@@ -181,6 +182,15 @@ export async function sendProviderRequest(state, round) {
181
182
  tools: round.sendTools,
182
183
  opts,
183
184
  });
185
+ // The first visible token of this request, for its generation time (queue,
186
+ // preflight and time-to-first-token stay out of the speed figures).
187
+ let firstVisibleAt = 0;
188
+ const callerOnStreamDelta = opts.onStreamDelta;
189
+ const observeStreamDelta = (kind, ...rest) => {
190
+ if (!firstVisibleAt && isVisibleStreamProgress(kind)) firstVisibleAt = Date.now();
191
+ return callerOnStreamDelta?.(kind, ...rest);
192
+ };
193
+ opts.onStreamDelta = observeStreamDelta;
184
194
  let result;
185
195
  try {
186
196
  result = await runWithProviderRequestToolsScope(round.requestToolScope, () =>
@@ -208,15 +218,19 @@ export async function sendProviderRequest(state, round) {
208
218
  sideQuestion?.discard();
209
219
  if (!(await advanceAgentRoute(state, error))) throw error;
210
220
  result = { action: 'retry_route' };
221
+ } finally {
222
+ if (opts.onStreamDelta === observeStreamDelta) opts.onStreamDelta = callerOnStreamDelta;
211
223
  }
212
224
  if (result.action === 'proceed') sideQuestion?.settle(result.response);
213
225
  else sideQuestion?.discard();
226
+ const sendEndedAt = Date.now();
214
227
  return {
215
228
  result,
216
229
  providerMessages,
217
230
  prefixGuardCandidate,
218
231
  sendStartedAt,
219
- sendEndedAt: Date.now(),
232
+ sendEndedAt,
233
+ generationMs: firstVisibleAt ? Math.max(0, sendEndedAt - firstVisibleAt) : 0,
220
234
  preSendMs,
221
235
  toolResumeMs,
222
236
  };
@@ -429,6 +443,7 @@ export function settleSendResult(state, round, sent) {
429
443
  usage: reportedUsage,
430
444
  sendTools: round.sendTools,
431
445
  toolCalls: response.toolCalls?.length || 0,
446
+ generationMs: sent.generationMs,
432
447
  })
433
448
  )
434
449
  );
@@ -6,10 +6,16 @@ import { resolvePreToolAskApproval } from '../tool-helpers.mjs';
6
6
  import { runAbortable, throwIfAborted } from '../../../../../shared/abort-race.mjs';
7
7
  import { evaluateExecPolicy } from '../../../tools/bash-policy-scan.mjs';
8
8
  import { beginSessionApprovalWait } from '../../manager/runtime-liveness.mjs';
9
+ import { agentMcpToolPermission } from '../../../mcp/agent-connectors.mjs';
10
+ import { humanGateDecision, recordHumanDecision } from '../../human-gate/gate.mjs';
11
+ import { applyEditedArgs } from '../../human-gate/requests.mjs';
9
12
 
10
13
  const isArgsObject = (value) => value && typeof value === 'object' && !Array.isArray(value);
11
14
 
12
- async function askApproval(call, { callerSessionId, executeOpts, toolApprovalHook, decision, keepArgs = false }) {
15
+ async function askApproval(
16
+ call,
17
+ { callerSessionId, executeOpts, toolApprovalHook, decision, request = null, gate = null }
18
+ ) {
13
19
  const askReason = String(decision?.reason || 'approval requested by hook').trim();
14
20
  // While the user decides, the session is waiting on them, not working.
15
21
  const releaseApprovalWait = beginSessionApprovalWait(callerSessionId);
@@ -24,40 +30,58 @@ async function askApproval(call, { callerSessionId, executeOpts, toolApprovalHoo
24
30
  toolCallId: executeOpts.toolCallId || null,
25
31
  askReason,
26
32
  toolApprovalHook,
33
+ request,
27
34
  })
28
35
  );
29
36
  } finally {
30
37
  releaseApprovalWait();
31
38
  }
32
- if (askOutcome.denial) return { denial: askOutcome.denial };
33
39
  const approval = askOutcome.approval;
34
- if (!keepArgs && approval && typeof approval === 'object' && isArgsObject(approval.args)) call.args = approval.args;
40
+ await recordHumanDecision(gate, approval);
41
+ if (askOutcome.denial) return { denial: askOutcome.denial };
42
+ if (approval && typeof approval === 'object' && isArgsObject(approval.args)) call.args = approval.args;
43
+ // The card's edited text replaces the model's before the call runs.
44
+ if (request?.kind === 'outbound' && approval?.editedArgs) {
45
+ call.args = applyEditedArgs(call.args, approval.editedArgs, request.outbound?.editable);
46
+ }
47
+ // The user approved this very call on the richer outbound card.
48
+ if (request?.kind === 'outbound') call.outboundApproved = true;
35
49
  return null;
36
50
  }
37
51
 
52
+ export const CONNECTOR_APPROVAL_REASON = 'This connector tool is set to ask before it runs for this agent.';
53
+
38
54
  /**
39
- * Approval gate for a shell call (or a terminal `run`) whose exec policy
40
- * decision is 'ask' (elevated launch). Runs after the final call is known. The
41
- * approval is granted for the exact command being asked about: an approval
42
- * reply cannot rewrite the args.
43
- * @returns {Promise<{ denial: string } | { elevationApproved: boolean }>}
55
+ * A connector tool the running agent set to `needs_approval` asks through the
56
+ * ordinary approval hook, whatever the PreToolUse hooks decided. Run after
57
+ * them, on the final call: a renamed call is judged under its final name.
58
+ * A call the outbound card already got approved is not asked a second time.
59
+ * @returns {Promise<{ denial: string } | null>} the denial, or null to run
44
60
  */
45
- export async function applyShellElevationGate(call, { callerSessionId, executeOpts, toolApprovalHook }) {
46
- const gated = call.name === 'shell' || (call.name === 'terminal' && call.args?.action === 'run');
47
- if (!gated || !isArgsObject(call.args) || call.args.command == null) {
48
- return { elevationApproved: false };
49
- }
50
- const decision = evaluateExecPolicy(String(call.args.command));
51
- if (decision.decision !== 'ask') return { elevationApproved: false };
52
- const denied = await askApproval(call, {
61
+ export function applyConnectorApproval(call, { agentId, callerSessionId, executeOpts, toolApprovalHook }) {
62
+ if (call.outboundApproved) return null;
63
+ if (agentMcpToolPermission(agentId, call.name) !== 'needs_approval') return null;
64
+ return askApproval(call, {
53
65
  callerSessionId,
54
66
  executeOpts,
55
67
  toolApprovalHook,
56
- decision,
57
- keepArgs: true,
68
+ decision: { reason: CONNECTOR_APPROVAL_REASON },
58
69
  });
59
- if (denied) return denied;
60
- return { elevationApproved: true };
70
+ }
71
+
72
+ /**
73
+ * Elevation pass for a shell call (or a terminal `run`) whose exec policy
74
+ * decision is 'ask' (elevated launch). Runs after the final call is known.
75
+ * Mixdog does not prompt first: the OS elevation prompt (Windows UAC) is the
76
+ * user's confirmation. A deny is never lifted.
77
+ * @returns {{ elevationApproved: boolean }}
78
+ */
79
+ export function applyShellElevationGate(call) {
80
+ const gated = call.name === 'shell' || (call.name === 'terminal' && call.args?.action === 'run');
81
+ if (!gated || !isArgsObject(call.args) || call.args.command == null) {
82
+ return { elevationApproved: false };
83
+ }
84
+ return { elevationApproved: evaluateExecPolicy(String(call.args.command)).decision === 'ask' };
61
85
  }
62
86
 
63
87
  /**
@@ -72,22 +96,40 @@ export async function applyBeforeToolHook({
72
96
  executeOpts,
73
97
  beforeToolHook,
74
98
  toolApprovalHook,
99
+ mcpScopeId = null,
75
100
  }) {
76
101
  const call = { name, args, cwd };
77
102
  // ask_user is itself the user prompt; it never goes through the ordinary approval gate.
78
- if (!beforeToolHook || name === 'ask_user') return call;
103
+ if (name === 'ask_user') return call;
79
104
  try {
80
- const decision = await runAbortable(executeOpts.signal, () =>
81
- beforeToolHook(
82
- {
83
- name,
84
- args,
85
- cwd,
86
- sessionId: callerSessionId,
87
- toolCallId: executeOpts.toolCallId || null,
88
- },
89
- { signal: executeOpts.signal }
90
- )
105
+ const hookDecision = beforeToolHook
106
+ ? await runAbortable(executeOpts.signal, () =>
107
+ beforeToolHook(
108
+ {
109
+ name,
110
+ args,
111
+ cwd,
112
+ sessionId: callerSessionId,
113
+ toolCallId: executeOpts.toolCallId || null,
114
+ },
115
+ { signal: executeOpts.signal }
116
+ )
117
+ )
118
+ : null;
119
+ // Outbound sends ask the user whatever the hooks allowed (human-gate/gate.mjs).
120
+ // Judged as the call will run: only a rewriting decision changes name/args.
121
+ const hookAction = String(hookDecision?.action || hookDecision?.decision || '').toLowerCase();
122
+ const rewritten = hookAction === 'ask' || hookAction === 'modify' || hookAction === 'rewrite';
123
+ const { decision, request, gate } = await humanGateDecision(
124
+ {
125
+ name:
126
+ rewritten && typeof hookDecision?.name === 'string' && hookDecision.name.trim()
127
+ ? hookDecision.name.trim()
128
+ : name,
129
+ args: rewritten && isArgsObject(hookDecision?.args) ? hookDecision.args : args,
130
+ },
131
+ hookDecision,
132
+ { sessionId: callerSessionId, mcpScopeId }
91
133
  );
92
134
  const action = String(decision?.action || decision?.decision || '').toLowerCase();
93
135
  if (action === 'deny' || action === 'block') {
@@ -99,7 +141,14 @@ export async function applyBeforeToolHook({
99
141
  if (typeof decision?.name === 'string' && decision.name.trim()) call.name = decision.name.trim();
100
142
  }
101
143
  if (action === 'ask') {
102
- const denied = await askApproval(call, { callerSessionId, executeOpts, toolApprovalHook, decision });
144
+ const denied = await askApproval(call, {
145
+ callerSessionId,
146
+ executeOpts,
147
+ toolApprovalHook,
148
+ decision,
149
+ request,
150
+ gate,
151
+ });
103
152
  if (denied) return denied;
104
153
  }
105
154
  } catch (error) {
@@ -14,7 +14,7 @@ import { runWithToolExecutionOwner } from '../../../../shared/tool-execution-own
14
14
  import { runWithLocalSearchTelemetry } from '../../tools/builtin/local-search-telemetry.mjs';
15
15
  import { throwIfAborted } from '../../../../shared/abort-race.mjs';
16
16
  import { prepareToolCall } from './tool-exec/call-prep.mjs';
17
- import { applyBeforeToolHook, applyShellElevationGate } from './tool-exec/before-hook.mjs';
17
+ import { applyBeforeToolHook, applyConnectorApproval, applyShellElevationGate } from './tool-exec/before-hook.mjs';
18
18
  import { dispatchToolCall } from './tool-exec/dispatch.mjs';
19
19
  import { applyAfterToolHook } from './tool-exec/after-hook.mjs';
20
20
  import { runReadOnlyIoWithDeadline } from './tool-exec/read-only-deadline.mjs';
@@ -50,8 +50,16 @@ async function executeToolOwned(name, args, cwd, callerSessionId, sessionRef, ex
50
50
  executeOpts,
51
51
  beforeToolHook: hookFrom(executeOpts, sessionRef, 'beforeToolHook'),
52
52
  toolApprovalHook,
53
+ mcpScopeId: sessionRef?.mcpScopeId || null,
53
54
  });
54
55
  if (hooked.denial) return hooked.denial;
56
+ const connectorDenied = await applyConnectorApproval(hooked, {
57
+ agentId: sessionRef?.agent,
58
+ callerSessionId,
59
+ executeOpts,
60
+ toolApprovalHook,
61
+ });
62
+ if (connectorDenied) return connectorDenied.denial;
55
63
  // A decision can settle after its turn was cancelled. It must never
56
64
  // authorize a new mutation, even if the hook ignores the abort signal.
57
65
  throwIfAborted(executeOpts.signal);
@@ -74,9 +82,8 @@ async function executeToolOwned(name, args, cwd, callerSessionId, sessionRef, ex
74
82
  const unavailable = bridgeTargetUnavailable(sessionRef, finalCall.schemaName || finalCall.name);
75
83
  if (unavailable) return unavailable;
76
84
  }
77
- // Elevated launches ask the user first; denied / no approval UI refuses the call.
78
- const elevation = await applyShellElevationGate(call, { callerSessionId, executeOpts, toolApprovalHook });
79
- if (elevation.denial) return elevation.denial;
85
+ // Elevated launches skip Mixdog's own prompt; the OS elevation prompt confirms them.
86
+ const elevation = applyShellElevationGate(call);
80
87
  throwIfAborted(executeOpts.signal);
81
88
  const result = await runReadOnlyIoWithDeadline(call.name, executeOpts.signal || null, async (deadlineSignal) => {
82
89
  let callOpts = executeOpts;
@@ -27,12 +27,13 @@ function invalidateForToolSurfaceMigration(session) {
27
27
  }
28
28
 
29
29
  /**
30
- * After a surface re-apply outside a request (resume, provider switch)
31
- * migrated the exposure: the first send of the next turn carries the tag.
30
+ * After a surface re-apply outside a request (resume, provider switch, a
31
+ * workflow switch: `intent`) changed the request prefix: the first send of the
32
+ * next turn carries the tag.
32
33
  */
33
- export function markToolSurfaceMigrated(session) {
34
+ export function markToolSurfaceMigrated(session, intent = TOOL_SURFACE_MIGRATION_INTENT) {
34
35
  invalidateForToolSurfaceMigration(session);
35
- session.pendingCacheBreakIntent = TOOL_SURFACE_MIGRATION_INTENT;
36
+ session.pendingCacheBreakIntent = intent;
36
37
  }
37
38
 
38
39
  /** Provider switch: rebuild the surface for `provider`, migrating bridge ↔ native. */
@@ -2,6 +2,7 @@
2
2
  // message-array change detect, tool-kind classification, skills, hook overrides,
3
3
  // native tool-search payload parsing, bash-session routing, and approval.
4
4
  import { isMcpTool } from '../../mcp/client.mjs';
5
+ import { unattendedApprovalPort } from '../human-gate/gate.mjs';
5
6
  import { isBuiltinTool } from '../../tools/builtin.mjs';
6
7
  import { isInternalTool } from '../../internal-tools.mjs';
7
8
  import { loadSkillToolDependencies } from '../../runtime-core/skill-tool-loading.mjs';
@@ -207,15 +208,24 @@ export async function resolvePreToolAskApproval({
207
208
  toolCallId,
208
209
  askReason,
209
210
  toolApprovalHook,
211
+ request = null,
210
212
  }) {
211
213
  const name = String(toolName || 'tool');
212
214
  const reason = String(askReason || 'approval requested by hook').trim();
213
- if (typeof toolApprovalHook !== 'function') {
215
+ // Unattended runs never auto-approve and have no card: a schedule or webhook
216
+ // run always holds the request for its owner; a room member does when no
217
+ // approval UI is attached (session-runtime/held-requests.mjs).
218
+ const unattended = unattendedApprovalPort();
219
+ const run = unattended?.runOf?.(sessionId) || null;
220
+ const held = run && (run.kind !== 'room' || typeof toolApprovalHook !== 'function');
221
+ const ask = held ? unattended.hold : toolApprovalHook;
222
+ if (typeof ask !== 'function') {
214
223
  return { denial: formatMissingToolApprovalUiDenial(name, reason) };
215
224
  }
216
225
  let approval;
217
226
  try {
218
- approval = await toolApprovalHook({
227
+ approval = await ask({
228
+ ...(request || {}),
219
229
  name,
220
230
  args,
221
231
  cwd,
@@ -229,7 +239,7 @@ export async function resolvePreToolAskApproval({
229
239
  }
230
240
  if (!approvalGranted(approval)) {
231
241
  const detail = approvalReason(approval, reason || 'not approved');
232
- return { denial: `Error: tool "${name}" denied by hook: ${detail}` };
242
+ return { denial: `Error: tool "${name}" denied by hook: ${detail}`, approval };
233
243
  }
234
244
  return { approval };
235
245
  }
@@ -41,6 +41,7 @@ export function usageDeltaEvent({
41
41
  usage,
42
42
  sendTools,
43
43
  toolCalls = 0,
44
+ generationMs = 0,
44
45
  }) {
45
46
  return {
46
47
  sessionId,
@@ -56,6 +57,12 @@ export function usageDeltaEvent({
56
57
  deltaToolCalls: toolCalls,
57
58
  ...reasoningUsage(usage),
58
59
  deltaPrompt: usage.promptTokens || 0,
60
+ // First visible token → end of this response; 0 when the call was not
61
+ // timed (nothing visible streamed). Only timed output enters speed figures.
62
+ generationMs,
63
+ // A provider that cannot measure the prompt or its cache split says so;
64
+ // its zeros are unknowns, not readings (no cache hit can be derived).
65
+ promptKnown: usage.inputTokensKnown !== false && usage.cacheTokensKnown !== false,
59
66
  // Cache delta carried alongside input/output so live metrics reflect
60
67
  // the same token classes the terminal aggregate adds; additive —
61
68
  // callers that ignore these fields keep working.
@@ -186,6 +186,7 @@ export async function prepareAskTurn({ sessionId, opened, input, cwdOverride, tr
186
186
  beforeCount: historyMessages.length + 1,
187
187
  deferredToolDelta: userTurn.deferredToolDelta,
188
188
  goalReminder: userTurn.goalReminder,
189
+ workflowNotice: userTurn.workflowNotice,
189
190
  workspaceNotice: userTurn.workspaceNotice,
190
191
  setupNotice: userTurn.setupNotice,
191
192
  };
@@ -8,6 +8,7 @@ import { createAskSteeringDrain } from './ask-turn-steering-drain.mjs';
8
8
  import { updateSessionStage, markSessionStreamDelta } from './runtime-liveness.mjs';
9
9
  import { codexWireSendOpts } from './session-id.mjs';
10
10
  import { persistIterationMetrics } from './usage-metrics.mjs';
11
+ import { isAgentModeSession } from '../agent-mode.mjs';
11
12
 
12
13
  const optionalFn = (value) => (typeof value === 'function' ? value : undefined);
13
14
  const relay = (fn, ...args) => {
@@ -130,6 +131,9 @@ export function buildAgentLoopOptions({
130
131
  checkpoint.schedule(true);
131
132
  relay(askOpts?.onAssistantToolCallObserved, call, detail);
132
133
  },
134
+ // A provider output item started (a tool call, a phased message): display
135
+ // only, so it bypasses the interruption journal.
136
+ onStreamItemStart: optionalFn(askOpts?.onStreamItemStart),
133
137
  onProviderSendStarted: () => {
134
138
  interruption.markProviderSendStarted();
135
139
  checkpoint.schedule(true);
@@ -181,7 +185,8 @@ export function buildAgentLoopOptions({
181
185
  onCompactEvent: (event) => {
182
186
  checkpoint.refreshContextState();
183
187
  checkpoint.schedule(true);
184
- relay(askOpts?.onCompactEvent, event);
188
+ // Agent mode compacts silently: no compaction notice reaches the host.
189
+ if (!isAgentModeSession(session)) relay(askOpts?.onCompactEvent, event);
185
190
  },
186
191
  // Pre-send gauge sync. Defined only when a host listens so the loop
187
192
  // never computes a display number nobody reads.
@@ -19,6 +19,7 @@ import {
19
19
  snapshotPendingWorkspaceNotice,
20
20
  snapshotPendingSetupNotice,
21
21
  } from './prompt-utils.mjs';
22
+ import { snapshotPendingWorkflowNotice } from './workflow-switch-notice.mjs';
22
23
  import { codexWireSendOpts } from './session-id.mjs';
23
24
  import { _tryBridgeExplicitPrefetch } from './prefetch-bridge.mjs';
24
25
  import { filterModelVisibleSessionMessages } from './message-sanitize.mjs';
@@ -180,7 +181,11 @@ export async function buildUserTurn({
180
181
  const workspaceNotice = snapshotPendingWorkspaceNotice(session);
181
182
  // What preparing a new worktree linked, installed, ran, failed or skipped.
182
183
  const setupNotice = snapshotPendingSetupNotice(session);
184
+ // A mid-conversation workflow switch: the prompt and tools were rebuilt
185
+ // for the new workflow; this turn tells the model once.
186
+ const workflowNotice = snapshotPendingWorkflowNotice(session);
183
187
  const reminderBlock = [
188
+ workflowNotice?.content || '',
184
189
  workspaceNotice?.content || '',
185
190
  setupNotice?.content || '',
186
191
  currentTimeBlock ? `<system-reminder>\n# Current Time\n${currentTimeBlock}\n</system-reminder>` : '',
@@ -209,6 +214,7 @@ export async function buildUserTurn({
209
214
  historyMessages,
210
215
  deferredToolDelta,
211
216
  goalReminder,
217
+ workflowNotice,
212
218
  workspaceNotice,
213
219
  setupNotice,
214
220
  userTurnMessage,
@@ -9,6 +9,7 @@ import { persistedAssistantTranscriptMetadata } from '../../../../shared/transcr
9
9
  import { acknowledgePendingDeferredToolDelta } from '../../runtime-core/deferred-tool-delta.mjs';
10
10
  import { acknowledgePendingGoalReminder } from '../../runtime-core/goal-reminder.mjs';
11
11
  import { acknowledgePendingSetupNotice, acknowledgePendingWorkspaceNotice } from './prompt-utils.mjs';
12
+ import { acknowledgePendingWorkflowNotice } from './workflow-switch-notice.mjs';
12
13
  import {
13
14
  finalizePendingMessageDelivery,
14
15
  drainPendingMessages,
@@ -139,6 +140,7 @@ export async function commitAskTurn({ sessionId, opened, prepared, result, rawTr
139
140
  if (prepared.goalReminder) {
140
141
  acknowledgePendingGoalReminder(session, prepared.goalReminder.revision);
141
142
  }
143
+ if (prepared.workflowNotice) acknowledgePendingWorkflowNotice(session, prepared.workflowNotice.revision);
142
144
  if (prepared.workspaceNotice) {
143
145
  acknowledgePendingWorkspaceNotice(session, prepared.workspaceNotice.revision);
144
146
  }
@@ -3,7 +3,12 @@
3
3
  // prompts at every continuation boundary, plus — at the TERMINAL boundary only
4
4
  // — the manager's pending-message entries, grouped into one steering item per
5
5
  // mode and adopted by this turn (so their delivery/ack/release rides it).
6
- import { _groupPendingMessageEntries, drainPendingMessages, releasePendingMessages } from './pending-messages.mjs';
6
+ import {
7
+ _groupPendingMessageEntries,
8
+ drainPendingMessages,
9
+ pendingQueueHoldsOnlyNotifications,
10
+ releasePendingMessages,
11
+ } from './pending-messages.mjs';
7
12
 
8
13
  /**
9
14
  * @param {object} input
@@ -26,9 +31,12 @@ export function createAskSteeringDrain({ sessionId, turn, askOpts }) {
26
31
  /* best-effort steering drain */
27
32
  }
28
33
  }
29
- // Manager/pending-messages entries carry no mode/priority/slash
30
- // metadata, so they stay OUT of the mid-chain (post-tool-batch) drain —
31
- // that would bypass the queued-command filters. At the TERMINAL boundary
34
+ // Manager/pending-messages prompts carry no priority/slash metadata, so
35
+ // they stay OUT of the mid-chain (post-tool-batch) drain — that would
36
+ // bypass the queued-command filters. A queue holding only task
37
+ // notifications (agent/shell completions, which also end a passive wait)
38
+ // is taken at every boundary, so a completion reaches the very next model
39
+ // request instead of waiting for the model to stop. At the TERMINAL boundary
32
40
  // they are exactly pending input: an `agent type=send` queued while the
33
41
  // terminal sample was in flight must be folded into THIS turn before any
34
42
  // stop hook runs, instead of losing its slot to a synthetic continuation
@@ -36,7 +44,7 @@ export function createAskSteeringDrain({ sessionId, turn, askOpts }) {
36
44
  // nothing. Entries consumed here join the turn's pending entries: their
37
45
  // delivery/ack (and release on failure) rides this turn, and the
38
46
  // post-loop drain can no longer see them.
39
- if (drainOptions?.stage === 'terminal') {
47
+ if (drainOptions?.stage === 'terminal' || pendingQueueHoldsOnlyNotifications(sessionId)) {
40
48
  const pendingNow = drainPendingMessages(sessionId);
41
49
  if (pendingNow.length > 0) {
42
50
  // One steering entry per mode group: the merged prompt (if any) and
@@ -40,14 +40,6 @@ export function currentPendingLifecycleToken(sessionId) {
40
40
  return pendingLifecycleToken(pendingSessionLifecycle(sessionId));
41
41
  }
42
42
 
43
- // Refusal verdict of the lifecycle a token was minted from (tombstoned or
44
- // unreadable) — exactly pendingLifecycleInvalidated(sessionId) for that same
45
- // read, so a caller holding a fresh token needs no second durable read.
46
- export function lifecycleTokenClosed(token) {
47
- const { source } = parseLifecycleToken(token);
48
- return source === 'closed' || source === 'unreadable';
49
- }
50
-
51
43
  // Parses a `source:generation` epoch token.
52
44
  function parseLifecycleToken(token) {
53
45
  const text = String(token);
@@ -103,12 +103,15 @@ function completionEntryFields(entry) {
103
103
  };
104
104
  }
105
105
 
106
- export function completionWasDelivered(entry, site) {
106
+ /** True when this completion entry already reached the model (no log). */
107
+ export function completionIsDelivered(entry) {
107
108
  if (!isCompletionNotificationEntry(entry)) return false;
108
- const executionId = completionExecutionId(entry);
109
- const text = pendingMessageText(entry);
110
- if (!isDeliveredCompletion({ executionId, text })) return false;
111
- logDuplicateSkip(site, { executionId, text });
109
+ return isDeliveredCompletion({ executionId: completionExecutionId(entry), text: pendingMessageText(entry) });
110
+ }
111
+
112
+ export function completionWasDelivered(entry, site) {
113
+ if (!completionIsDelivered(entry)) return false;
114
+ logDuplicateSkip(site, { executionId: completionExecutionId(entry), text: pendingMessageText(entry) });
112
115
  return true;
113
116
  }
114
117
 
@@ -166,7 +169,7 @@ function explicitModeField(entry) {
166
169
  return entry?.mode === PENDING_MODE_TASK_NOTIFICATION ? { mode: PENDING_MODE_TASK_NOTIFICATION } : {};
167
170
  }
168
171
 
169
- export function normalizePendingMessageEntry(entry) {
172
+ function normalizePendingMessageEntry(entry) {
170
173
  if (typeof entry === 'string') {
171
174
  const text = entry.trim();
172
175
  return text ? { content: text, text } : null;
@@ -211,7 +214,7 @@ export function normalizePendingMessageEntry(entry) {
211
214
  return carryLifecycleToken(marker ? { ...out, ...identity, ...marker } : { ...out, ...identity }, entry);
212
215
  }
213
216
 
214
- export function pendingMessageText(entry) {
217
+ function pendingMessageText(entry) {
215
218
  const normalized = normalizePendingMessageEntry(entry);
216
219
  return normalized ? String(normalized.text || promptContentText(normalized.content) || '').trim() : '';
217
220
  }