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
@@ -8,29 +8,73 @@ import {
8
8
  } from '../../runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs';
9
9
  import { createCallerContextResolvers } from './caller-context.mjs';
10
10
  import { runAbortable } from '../../runtime/shared/abort-race.mjs';
11
- import { SECRET_REQUEST_TIMEOUT_MS } from '../../runtime/agent/orchestrator/session/human-gate/requests.mjs';
11
+ import {
12
+ HANDOFF_OUTCOMES as AGENT_OUTCOMES,
13
+ SECRET_REQUEST_TIMEOUT_MS,
14
+ } from '../../runtime/agent/orchestrator/session/human-gate/requests.mjs';
12
15
  import {
13
16
  agentIdForSession,
14
17
  recordHumanDecision,
15
18
  unattendedApprovalPort,
16
19
  } from '../../runtime/agent/orchestrator/session/human-gate/gate.mjs';
17
- import { browserSecretInput } from '../../runtime/browser-bridge/client.mjs';
18
- import { computerSecretInput } from '../../runtime/computer-bridge/client.mjs';
20
+ import { browserHandoffPage, browserSecretInput } from '../../runtime/browser-bridge/client.mjs';
21
+ import { computerSecretBind, computerSecretInput } from '../../runtime/computer-bridge/client.mjs';
22
+
23
+ /** The settle reason of a takeover that expired (desktop takeover-gate.ts
24
+ * TAKEOVER_EXPIRED_REASON). */
25
+ const TAKEOVER_EXPIRED_REASON = 'expired';
26
+
27
+ /**
28
+ * The agent's result of a secret request, from the card's answer and the
29
+ * desktop's fill: one of requests.mjs HANDOFF_OUTCOMES. `fill` runs only for a provided
30
+ * value and answers 'entered' / 'no_field' / 'page_changed' / 'blocked' /
31
+ * 'expired'.
32
+ */
33
+ export async function secretOutcome(result, fill) {
34
+ if (result?.timedOut === true || result?.reason === TAKEOVER_EXPIRED_REASON) return 'expired';
35
+ if (result?.approved !== true || result.response !== 'provide-secret') return 'user_skipped';
36
+ const filled = await fill();
37
+ return AGENT_OUTCOMES.includes(filled) && filled !== 'user_skipped' ? filled : 'blocked';
38
+ }
39
+
40
+ /** A page address as the agent may see it: origin and path, no query or
41
+ * fragment (a sign-in redirect can carry a code or token there). */
42
+ function factUrl(value) {
43
+ try {
44
+ const url = new URL(String(value || ''));
45
+ return url.protocol === 'http:' || url.protocol === 'https:' ? `${url.origin}${url.pathname}` : '';
46
+ } catch {
47
+ return '';
48
+ }
49
+ }
50
+
51
+ /** The agent's result of a takeover: what the user did and where the page is
52
+ * now, never "signed in" (the agent checks the page). */
53
+ export function takeoverOutcome(result, pageUrl = '') {
54
+ if (result?.timedOut === true || result?.reason === TAKEOVER_EXPIRED_REASON) return 'expired';
55
+ if (result?.approved !== true) return 'user_skipped';
56
+ const url = factUrl(pageUrl);
57
+ const did = result.reason === 'saved_login' ? 'User chose a saved login; it was filled in' : 'User pressed Done';
58
+ return url ? `${did}; page now at ${url}` : did;
59
+ }
19
60
 
20
61
  /**
21
- * A secret or takeover handoff. Secret: the card collects the value on the
22
- * desktop, which holds it for `requestId` and fills it into the target field
23
- * (isolated-world fill for the browser, one-shot typed input for computer
24
- * use); here only the verdict travels, and the result is exactly 'provided',
25
- * 'skipped' or 'timeout'. Takeover: the run waits until the user says done
26
- * (computer and browser actions stay blocked meanwhile, desktop
27
- * main/takeover-gate.ts); the result is 'done' or 'denied'.
62
+ * A secret or takeover handoff. Secret: at request time the desktop binds the
63
+ * request to its page ({pageId, origin}) or, for computer use, it is bound to
64
+ * {window_id, ref}; the card collects the value on the desktop, which holds it
65
+ * for `requestId` and fills only that page, only while its origin is
66
+ * unchanged. Here only the outcome travels (secretOutcome). Takeover: the run
67
+ * waits until the user says done (that session's actions stay blocked
68
+ * meanwhile, desktop main/takeover-gate.ts); the result is takeoverOutcome's.
28
69
  */
29
70
  export async function runAskUserHandoff(handoff, { hook, sessionId, toolCallId, signal, inputs = {} }) {
30
71
  const secretInput = {
31
72
  browser: inputs.browser || browserSecretInput,
32
73
  computer: inputs.computer || computerSecretInput,
33
74
  };
75
+ const page = inputs.page || browserHandoffPage;
76
+ const bindComputer = inputs.computerBind || computerSecretBind;
77
+ const identity = handoff.identity || {};
34
78
  const gate = {
35
79
  agentId: agentIdForSession(sessionId),
36
80
  kind: handoff.kind,
@@ -47,32 +91,84 @@ export async function runAskUserHandoff(handoff, { hook, sessionId, toolCallId,
47
91
  return UNATTENDED_TAKEOVER;
48
92
  }
49
93
  if (typeof hook !== 'function') {
50
- // Nobody can take over here: blocked, never "proceed".
94
+ // Nobody can take over or type here: blocked, never "proceed".
51
95
  if (handoff.kind === 'takeover') return NO_TAKEOVER;
52
- await recordHumanDecision(gate, { response: 'skipped' });
53
- return 'skipped';
96
+ await recordHumanDecision(gate, { response: 'blocked' });
97
+ return 'blocked';
54
98
  }
55
99
  if (handoff.kind === 'secret') {
56
100
  const requestId = `secret_${randomUUID()}`;
101
+ // Bound now: the page (and its origin) the value may go to, and what its
102
+ // field is. A browser page that cannot be bound, or has no such field,
103
+ // never asks the user for a value it could not place.
104
+ let bound = {};
105
+ if (handoff.surface === 'browser') {
106
+ const facts = await page(
107
+ sessionId,
108
+ 'secret_bind',
109
+ { request_id: requestId, ...(identity.tab ? { tab: identity.tab } : {}), field: handoff.field },
110
+ { signal }
111
+ );
112
+ let outcome = '';
113
+ if (!facts?.origin) outcome = 'blocked';
114
+ else if (!facts.form) outcome = 'no_field';
115
+ if (outcome) {
116
+ await recordHumanDecision(gate, { response: outcome });
117
+ return outcome;
118
+ }
119
+ bound = { origin: facts.origin, form: facts.form, ...(facts.length ? { length: facts.length } : {}) };
120
+ } else {
121
+ // Computer Use: bound to the exact window and field ref the agent named
122
+ // (from snapshot/find). Without both there is no field to confirm, and
123
+ // none is made up (never the focused field or the front window).
124
+ const outcome =
125
+ identity.windowId && identity.ref
126
+ ? await bindComputer(sessionId, requestId, { windowId: identity.windowId, ref: identity.ref, signal })
127
+ : 'no_field';
128
+ if (outcome !== 'bound') {
129
+ const refused = outcome === 'no_field' ? 'no_field' : 'blocked';
130
+ await recordHumanDecision(gate, { response: refused });
131
+ return refused;
132
+ }
133
+ }
57
134
  const result = await runAbortable(signal, () =>
58
135
  hook({
59
136
  name: 'ask_user',
60
137
  kind: 'secret',
61
- secret: { requestId, surface: handoff.surface, target: handoff.target, field: handoff.field },
138
+ secret: { requestId, surface: handoff.surface, target: handoff.target, field: handoff.field, ...bound },
62
139
  reason: '',
63
140
  timeoutMs: SECRET_REQUEST_TIMEOUT_MS,
64
141
  sessionId,
65
142
  toolCallId,
66
143
  })
67
144
  );
68
- let verdict = 'skipped';
69
- if (result?.timedOut === true) verdict = 'timeout';
70
- else if (result?.approved === true && result.response === 'provide-secret') {
71
- verdict = await secretInput[handoff.surface](sessionId, requestId, { signal });
72
- }
73
- await recordHumanDecision(gate, { response: verdict, device: result?.device });
74
- return verdict;
145
+ const computerTarget = {
146
+ ...(identity.ref ? { ref: identity.ref } : {}),
147
+ ...(identity.windowId ? { windowId: identity.windowId } : {}),
148
+ };
149
+ const outcome = await secretOutcome(result, () =>
150
+ secretInput[handoff.surface](sessionId, requestId, {
151
+ signal,
152
+ ...(handoff.surface === 'computer' ? computerTarget : {}),
153
+ })
154
+ );
155
+ await recordHumanDecision(gate, { response: outcome, device: result?.device });
156
+ return outcome;
75
157
  }
158
+ // A sign-in page's saved logins (masked) let the card fill one for the
159
+ // user: the desktop offers them only for a page on that origin, bound now
160
+ // under `fillId` so the fill reaches that page alone (host-agent.ts fillBoundLogin).
161
+ const fillId = handoff.login ? `secret_${randomUUID()}` : '';
162
+ const accounts = handoff.login
163
+ ? (
164
+ await page(
165
+ sessionId,
166
+ 'login_accounts',
167
+ { url: handoff.login.url, request_id: fillId, ...(identity.tab ? { tab: identity.tab } : {}) },
168
+ { signal }
169
+ )
170
+ )?.accounts
171
+ : null;
76
172
  const result = await runAbortable(signal, () =>
77
173
  hook({
78
174
  name: 'ask_user',
@@ -81,9 +177,10 @@ export async function runAskUserHandoff(handoff, { hook, sessionId, toolCallId,
81
177
  surface: handoff.surface,
82
178
  instructions: handoff.instructions,
83
179
  target: handoff.target,
84
- ...handoff.identity,
180
+ ...identity,
85
181
  // A login takeover: the desktop opens its sign-in window on this page.
86
182
  ...(handoff.login ? { purpose: 'login', url: handoff.login.url } : {}),
183
+ ...(Array.isArray(accounts) && accounts.length ? { accounts, fillId } : {}),
87
184
  },
88
185
  args: { surface: handoff.surface, instructions: handoff.instructions },
89
186
  reason: '',
@@ -92,8 +189,15 @@ export async function runAskUserHandoff(handoff, { hook, sessionId, toolCallId,
92
189
  toolCallId,
93
190
  })
94
191
  );
95
- const answer = result?.approved === true ? 'done' : 'denied';
96
- await recordHumanDecision(gate, { response: answer, device: result?.device });
192
+ const pageUrl =
193
+ result?.approved === true && handoff.surface === 'browser'
194
+ ? (await page(sessionId, 'page_url', identity.tab ? { tab: identity.tab } : {}, { signal }))?.url
195
+ : '';
196
+ const answer = takeoverOutcome(result, pageUrl);
197
+ await recordHumanDecision(gate, {
198
+ response: answer.startsWith('User ') ? 'done' : answer,
199
+ device: result?.device,
200
+ });
97
201
  return answer;
98
202
  }
99
203
 
@@ -125,7 +229,7 @@ const DISMISSED =
125
229
  'User closed the questions without answering. Proceed with your best judgment; ask again only if something new depends on it.';
126
230
 
127
231
  /** Wire answers -> array aligned with questions; invalid entries become null. */
128
- export function normalizeAskUserAnswers(answers, questions) {
232
+ function normalizeAskUserAnswers(answers, questions) {
129
233
  const list = Array.isArray(answers) ? answers.slice(0, 4) : [];
130
234
  return questions.map((q, i) => {
131
235
  const entry = list[i];
@@ -143,7 +247,7 @@ export function normalizeAskUserAnswers(answers, questions) {
143
247
 
144
248
  // Strings are JSON-quoted so a quote inside one cannot end it early; typed
145
249
  // text stays apart from the picked labels (`"picked" + "typed" (typed)`).
146
- export function formatAskUserAnswers(questions, answers) {
250
+ function formatAskUserAnswers(questions, answers) {
147
251
  const quote = (text) => JSON.stringify(text);
148
252
  const parts = questions.map((q, i) => {
149
253
  const a = answers[i];
@@ -157,14 +261,14 @@ export function formatAskUserAnswers(questions, answers) {
157
261
 
158
262
  /** A composer message sent instead of (or with) picking: kept when it is a
159
263
  * non-empty string within the typed-answer limit, else dropped. */
160
- export function normalizeAskUserReply(reply) {
264
+ function normalizeAskUserReply(reply) {
161
265
  if (typeof reply !== 'string' || reply.length > ASK_USER_MAX_TEXT) return '';
162
266
  return reply.trim();
163
267
  }
164
268
 
165
269
  // `User replied in chat: "<reply>". ` leads, then the usual picks sentence
166
270
  // when any question was answered, else just the closing sentence.
167
- export function formatAskUserResult(questions, answers, reply) {
271
+ function formatAskUserResult(questions, answers, reply) {
168
272
  if (!reply) return formatAskUserAnswers(questions, answers);
169
273
  const lead = `User replied in chat: ${JSON.stringify(reply)}. `;
170
274
  if (answers.some(Boolean)) return `${lead}${formatAskUserAnswers(questions, answers)}`;
@@ -45,6 +45,8 @@ export function createBridgeToolHandlers({ rt }) {
45
45
  cwd: callerCwd,
46
46
  requestApproval: callerCtx?.toolApprovalHook,
47
47
  toolCallId: callerCtx?.toolCallId || null,
48
+ // The agent turn, as for the browser: it scopes a saved-login fill's protection.
49
+ turnId: callerCtx?.turnId || rt.session?.usageMetricsTurnId,
48
50
  signal: signalFor(callerCtx),
49
51
  });
50
52
  };
@@ -0,0 +1,45 @@
1
+ // react: a bot's emoji reaction on the user's message (message-reactions.mjs).
2
+ // Only a bot's own chat with the user may react: its home chat, where the
3
+ // message is in the chat's own transcript, or its seat in a group room, where
4
+ // the message is the user's row in the room's log. Never a coding session, a
5
+ // delegated worker, or a run nobody attends.
6
+ import { isAgentOwner } from '../../runtime/agent/orchestrator/agent-owner.mjs';
7
+ import { noteReactionTarget, validateReactionArgs } from '../../runtime/agent/orchestrator/runtime-core/reaction-tool-defs.mjs';
8
+ import { agentIdOfHomeSession } from '../../runtime/agent/orchestrator/session/agent-home.mjs';
9
+ import { roomMemberOfSession, roomSessionId } from '../../runtime/agent/orchestrator/session/agent-room.mjs';
10
+ import { reactInSession } from '../../runtime/agent/orchestrator/session/message-reactions.mjs';
11
+ import { agentHomeLiveSurface } from '../agent-home-delivery.mjs';
12
+ import { reactInRoom } from '../agent-room-delivery.mjs';
13
+ import { scheduleCallerScope } from '../services/schedule-access.mjs';
14
+
15
+ const DONE =
16
+ 'The user sees it under their message. Say nothing about it; reply only if the message also needs an answer.';
17
+
18
+ export function createReactionToolHandlers({ rt, live = agentHomeLiveSurface, roomReact = reactInRoom }) {
19
+ return {
20
+ react: async (args, { callerCtx }) => {
21
+ const sessionId = callerCtx?.callerSessionId || rt.session?.id || '';
22
+ const session = rt.session?.id === sessionId ? rt.session : null;
23
+ const member = roomMemberOfSession(sessionId);
24
+ const agentId = member?.agentId || agentIdOfHomeSession(sessionId);
25
+ if (!session || !agentId || isAgentOwner(session)) {
26
+ throw new Error("react: reactions belong to a bot's own chat with the user");
27
+ }
28
+ if (scheduleCallerScope(session).unattended === true) {
29
+ throw new Error('react: nobody is in this run to see a reaction');
30
+ }
31
+ const { emoji, messagesBack } = validateReactionArgs(args);
32
+ const change = { agentId, emoji, messagesBack };
33
+ let result;
34
+ if (member) result = await roomReact(roomSessionId(member.roomId), change);
35
+ else {
36
+ result = reactInSession(session, change);
37
+ // The chat's own turn saves the transcript; its open views show it now.
38
+ if (result.changed) live()?.react?.(sessionId, result.messageId, result.reactions);
39
+ }
40
+ if (!emoji) return result.changed ? 'Removed your reaction.' : 'You had no reaction on that message.';
41
+ noteReactionTarget(sessionId, callerCtx?.toolCallId, { messageId: result.messageId, room: Boolean(member) });
42
+ return `Reacted ${emoji}. ${DONE}`;
43
+ },
44
+ };
45
+ }
@@ -6,11 +6,17 @@ import { listProjects } from '../../runtime/shared/projects.mjs';
6
6
  import { applySessionSidebarChange } from '../../runtime/shared/session-sidebar-host.mjs';
7
7
  import { clean } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
8
8
  import { AGENT_MODE, resolveSessionMode } from '../../runtime/agent/orchestrator/session/agent-mode.mjs';
9
- import { agentIdOfHomeSession } from '../../runtime/agent/orchestrator/session/agent-home.mjs';
9
+ import {
10
+ MIXY_AGENT_ID,
11
+ agentHomeSessionId,
12
+ agentIdOfHomeSession,
13
+ } from '../../runtime/agent/orchestrator/session/agent-home.mjs';
10
14
  import { turnHopOf } from '../../runtime/shared/bot-hops.mjs';
15
+ import { isHeartbeatSession } from '../../runtime/shared/routine-trigger.mjs';
11
16
  import { scheduleCallerScope } from '../services/schedule-access.mjs';
12
17
  import { worktreeCallerScope } from '../services/worktree-access.mjs';
13
18
  import { resolveHandoffAttachments, sessionRoots } from '../handoff-attachments.mjs';
19
+ import { delegationChainOf } from '../bot-chain.mjs';
14
20
 
15
21
  const USER_TITLE_ERROR = 'session: the user already titled this session; the title was not changed';
16
22
 
@@ -53,6 +59,14 @@ export function createWorkspaceToolHandlers({
53
59
  return rt.session.id;
54
60
  };
55
61
 
62
+ // Mixy's hidden heartbeat session (mixy-heartbeat.mjs), by the in-process
63
+ // mark on the calling session, never by tool input or stored fields.
64
+ const callerIsHeartbeat = (callerCtx) => isHeartbeatSession(callerCtx?.callerSessionId || rt.session?.id);
65
+ const HEARTBEAT_NO_HANDOFF =
66
+ 'a heartbeat turn cannot hand work off: its result would reach nobody. Propose it to the user instead (session action suggest).';
67
+ // What a heartbeat turn does with sessions: read them and the bots, and record proposals.
68
+ const HEARTBEAT_SESSION_ACTIONS = new Set(['read', 'status', 'list', 'bots', 'suggest']);
69
+
56
70
  const SIDEBAR_ACTIONS = new Set(['favorite', 'archive']);
57
71
 
58
72
  // Sidebar flags only (the desktop's favorite/archive metadata).
@@ -71,12 +85,26 @@ export function createWorkspaceToolHandlers({
71
85
  const session = async (args, { callerCtx, callerCwd }) => {
72
86
  const action = clean(args?.action || (args?.path ? 'set-project' : 'status')).toLowerCase();
73
87
  const target = clean(args?.session_id);
88
+ if (callerIsHeartbeat(callerCtx) && !HEARTBEAT_SESSION_ACTIONS.has(action)) {
89
+ throw new Error(
90
+ `session: ${
91
+ action === 'create' || action === 'send'
92
+ ? HEARTBEAT_NO_HANDOFF
93
+ : `a heartbeat turn only reads sessions and bots and records proposals; action=${action} is refused`
94
+ }`
95
+ );
96
+ }
74
97
  if (
75
98
  ['list', 'bots', 'read', 'send', 'cancel', 'delete', 'create'].includes(action) ||
76
99
  (action === 'status' && target)
77
100
  ) {
78
101
  const callerSessionId = assertLead(action, callerCtx);
102
+ // `callerAgentId`: the bot whose conversation gets results reported
103
+ // back; `callerBotId`: the bot the caller acts as, attended or not,
104
+ // for what only a bot's own sessions allow (an effort change).
79
105
  const callerAgentId = attendedAgentOf(rt.session);
106
+ const acting = resolveSessionMode({ session: rt.session });
107
+ const callerBotId = acting.mode === AGENT_MODE ? acting.agentId : '';
80
108
  if (action === 'create' && !callerAgentId) {
81
109
  throw new Error(
82
110
  'session: create belongs to a bot conversation; delegated workers, agent task sessions and schedule/webhook runs cannot create agent task sessions'
@@ -105,8 +133,24 @@ export function createWorkspaceToolHandlers({
105
133
  {
106
134
  callerSessionId,
107
135
  callerAgentId,
136
+ callerBotId,
108
137
  callerCwd,
109
- ...(action === 'send' || action === 'create' ? { callerHop: turnHopOf(rt.session) } : {}),
138
+ ...(action === 'send' || action === 'create'
139
+ ? {
140
+ callerHop: turnHopOf(rt.session),
141
+ // The bot chain this turn acts in (bot-chain.mjs), checked
142
+ // against the target and carried to the turn it starts —
143
+ // only when the sender is a bot conversation (a bot's chat,
144
+ // room seat or run); any other session hands work to any
145
+ // bot as before, whatever started its turn (the hop limit
146
+ // still bounds a loop).
147
+ callerBotChain: callerBotId
148
+ ? delegationChainOf(callerSessionId, (id) =>
149
+ id === rt.session?.id ? rt.session : getSessionManager()?.getSession?.(id) || null
150
+ )
151
+ : [],
152
+ }
153
+ : {}),
110
154
  // Whether a create may provision a worktree (worktree-access.mjs).
111
155
  ...(action === 'create' ? { callerScope: worktreeCallerScope(rt.session) } : {}),
112
156
  // Which bots and sessions it may see (bot-status.mjs).
@@ -116,6 +160,16 @@ export function createWorkspaceToolHandlers({
116
160
  )
117
161
  );
118
162
  }
163
+ // Mixy's proposals to the user (mixy-heartbeat.mjs): kept so none repeats.
164
+ if (action === 'suggest') {
165
+ const sessionId = assertLead(action, callerCtx);
166
+ if (sessionId !== agentHomeSessionId(MIXY_AGENT_ID) && !callerIsHeartbeat(callerCtx)) {
167
+ throw new Error("session: suggest belongs to Mixy's chat");
168
+ }
169
+ const { recordSuggestion } = await import('../mixy-heartbeat.mjs');
170
+ const receipt = recordSuggestion({ key: args?.key, text: args?.title, dismissed: args?.value === false });
171
+ return JSON.stringify({ action, ...receipt }, null, 2);
172
+ }
119
173
  if (target && target !== assertLead(action, callerCtx)) {
120
174
  throw new Error(`session: action=${action} only applies to the current session`);
121
175
  }
@@ -184,6 +238,9 @@ export function createWorkspaceToolHandlers({
184
238
  project: current?.workspace?.projectPath || current?.desktopSession?.projectPath || currentCwd,
185
239
  provider: current?.provider || null,
186
240
  model: current?.model || null,
241
+ effort: current?.effort || null,
242
+ autoEffort: current?.autoEffort !== false,
243
+ fast: current?.fast === true,
187
244
  },
188
245
  null,
189
246
  2
@@ -229,12 +286,22 @@ export function createWorkspaceToolHandlers({
229
286
 
230
287
  return {
231
288
  session,
232
- goal: async (args, { name, callerCtx }) =>
233
- await goalRuntime.executeTool(name, args || {}, {
289
+ goal: async (args, { name, callerCtx }) => {
290
+ // A Goal keeps working in its session across turns: a heartbeat's would
291
+ // run on unseen in the hidden session.
292
+ if (['create', 'resume'].includes(clean(args?.action).toLowerCase()) && callerIsHeartbeat(callerCtx)) {
293
+ throw new Error(`goal: ${HEARTBEAT_NO_HANDOFF}`);
294
+ }
295
+ return await goalRuntime.executeTool(name, args || {}, {
234
296
  callerSessionId: callerCtx?.callerSessionId || rt.session?.id || rt.reservedSessionId || null,
235
- }),
297
+ });
298
+ },
236
299
  agent: async (args, { callerCtx, callerCwd }) => {
237
300
  const callerSessionId = callerCtx?.callerSessionId || rt.session?.id || null;
301
+ // The agent tool's action is `type`, spawn when omitted (agent-tool/execute.mjs).
302
+ if (['spawn', 'send'].includes((clean(args?.type) || 'spawn').toLowerCase()) && callerIsHeartbeat(callerCtx)) {
303
+ throw new Error(`agent: ${HEARTBEAT_NO_HANDOFF}`);
304
+ }
238
305
  // `attachmentParts` is never the model's to set: only the resolved
239
306
  // `attachments` reach the agent's turn.
240
307
  const { attachments, attachmentParts: _ignored, ...rest } = args || {};
@@ -10,6 +10,7 @@ import { createAskUserToolHandlers } from './internal-tool-executor/ask-user-too
10
10
  import { createBridgeToolHandlers } from './internal-tool-executor/bridge-tools.mjs';
11
11
  import { createFeatureToolHandlers } from './internal-tool-executor/feature-tools.mjs';
12
12
  import { createKnowledgeToolHandlers } from './internal-tool-executor/knowledge-tools.mjs';
13
+ import { createReactionToolHandlers } from './internal-tool-executor/reaction-tool.mjs';
13
14
  import { createWorkspaceToolHandlers } from './internal-tool-executor/workspace-tools.mjs';
14
15
 
15
16
  export function createInternalToolExecutor({
@@ -40,6 +41,7 @@ export function createInternalToolExecutor({
40
41
  const handlers = {
41
42
  ...createBridgeToolHandlers({ rt }),
42
43
  ...createAskUserToolHandlers({ rt }),
44
+ ...createReactionToolHandlers({ rt }),
43
45
  ...createFeatureToolHandlers({ rt, setupTool, officeToolsEnabled, mediaToolEnabled, tidyToolEnabled }),
44
46
  ...createKnowledgeToolHandlers({
45
47
  rt,
@@ -80,6 +82,8 @@ export function createInternalToolExecutor({
80
82
 
81
83
  return async (name, args, callerCtx = {}) => {
82
84
  const callerCwd = clean(callerCtx?.callerCwd) || rt.currentCwd;
85
+ // The Reactions setting gates the tool whoever calls it, not only the model.
86
+ if (name === 'react' && rt.config?.reactions === false) throw new Error('reactions are turned off in settings');
83
87
  if (callerCtx?.invocationSource === 'model-tool') guardModelCall(name);
84
88
  if (Object.hasOwn(handlers, name)) return handlers[name](args, { name, callerCtx, callerCwd });
85
89
  if (channels.isChannelTool(name)) {
@@ -15,6 +15,10 @@ import {
15
15
  inheritanceRouteTarget,
16
16
  } from '../inheritance-fit.mjs';
17
17
  import { clean } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
18
+ import {
19
+ rebindToChangedPrefix,
20
+ requestPrefixIdentity,
21
+ } from '../../runtime/agent/orchestrator/session/thinking-binding.mjs';
18
22
  import { buildColdHeir, committedSource, persistColdHeir } from './cold-heir.mjs';
19
23
 
20
24
  export function createInheritance(deps, { compactConversation, saveSession }) {
@@ -155,8 +159,17 @@ export function createInheritance(deps, { compactConversation, saveSession }) {
155
159
  async function carryConversation(source, target, { compact = false } = {}) {
156
160
  // System blocks belong to the session that BUILT them: the target's own
157
161
  // prompt was composed for the current model, tool surface, and workflow.
158
- // Only the conversation itself travels.
159
- let carried = inheritableMessages(source.messages);
162
+ // Only the conversation itself travels — without its thinking replay when
163
+ // the heir's prompt or tools differ from the source's: that replay is
164
+ // bound to the source's prefix, so the provider would drop it (and every
165
+ // thinking block after it) or refuse it, while the heir's context estimate
166
+ // kept counting it (thinking-binding.mjs); an open tool round the source
167
+ // stopped in is ended for the heir. An identical prefix keeps it all.
168
+ const carrySource =
169
+ requestPrefixIdentity(source) === requestPrefixIdentity(target)
170
+ ? source
171
+ : { ...source, messages: rebindToChangedPrefix(source.messages) };
172
+ let carried = inheritableMessages(carrySource.messages);
160
173
  if (!hasUserConversationMessage(carried)) {
161
174
  throw new Error('inheritFrom: the source session has no conversation to carry');
162
175
  }
@@ -169,7 +182,7 @@ export function createInheritance(deps, { compactConversation, saveSession }) {
169
182
  // compact:true summarizes even a conversation that fits; otherwise only
170
183
  // one that cannot fit is compacted.
171
184
  if (compact || (fit.known && !fit.fits)) {
172
- const next = await fitConversationForHeir(source, target, carried, fit, { required: compact });
185
+ const next = await fitConversationForHeir(carrySource, target, carried, fit, { required: compact });
173
186
  compacted = next !== carried;
174
187
  carried = next;
175
188
  }
@@ -77,7 +77,9 @@ export function leadSessionRow(s, working = false) {
77
77
  if (sourceType === 'exec') return null;
78
78
  if (!agentSession && !isLeadShaped(s, owner, sourceType)) return null;
79
79
  const { preview, messageCount, realTurnCount, incomingTail } = previewOf(s);
80
- if (!preview && messageCount === 0) return null;
80
+ // A titled one is not scratch: its prompt was rewound away (Esc-Esc message
81
+ // selector), so it stays listed rather than vanishing with its open tab.
82
+ if (!preview && messageCount === 0 && !cleanSessionPreview(s.title || '', 100)) return null;
81
83
  const lastMessageRoutine = normalizeRoutineProvenance(s.lastMessageRoutine);
82
84
  return {
83
85
  id: s.id,
@@ -5,6 +5,17 @@
5
5
  * close, all-settled for a full one.
6
6
  */
7
7
  import { releaseComputerSession } from '../../../runtime/computer-bridge/client.mjs';
8
+ import {
9
+ pendingSummaryIndexIds,
10
+ settleSummaryIndexWrites,
11
+ } from '../../../runtime/agent/orchestrator/session/store-summary-index.mjs';
12
+ import { settleGatewayUsageWrites } from '../../../runtime/agent/orchestrator/providers/statusline-route-meta.mjs';
13
+ import { closeSessionSearch } from '../../../runtime/session-search/session-search-ingest.mjs';
14
+
15
+ // How long a close waits for a stop that owns files (memory, goals, and the
16
+ // close's own persistence): briefly for a detached close, longer for a full one.
17
+ const DETACHED_FILE_STOP_MS = 1500;
18
+ const FULL_FILE_STOP_MS = 5500;
8
19
 
9
20
  export function startRuntimeStops(deps, reason, { detach, isProcessExit }) {
10
21
  const { mcpClient, getMcpScopeId, closePatchRuntimeIfLoaded, getMemoryModPromise, setMemoryModPromise } = deps;
@@ -66,8 +77,8 @@ export async function settleDetached(withTeardownDeadline, stops) {
66
77
  const bounded = [
67
78
  [stops.channelStop, 300],
68
79
  [stops.shellJobsStop, 300],
69
- [stops.memoryStop, 1500],
70
- [stops.goalStop, 1500],
80
+ [stops.memoryStop, DETACHED_FILE_STOP_MS],
81
+ [stops.goalStop, DETACHED_FILE_STOP_MS],
71
82
  ];
72
83
  for (const [stop, deadlineMs] of bounded) {
73
84
  try {
@@ -79,14 +90,49 @@ export async function settleDetached(withTeardownDeadline, stops) {
79
90
  }
80
91
  }
81
92
 
93
+ /**
94
+ * The close's deferred data-dir writes, under the file-stop deadline: this
95
+ * session's summary-index ops (every queued op when no session is closing),
96
+ * the buffered gateway usage events with their compaction, and this session's
97
+ * session-search writes (cancelled, PostgreSQL install included). Past the
98
+ * deadline the close stops waiting and reports what is still pending; those
99
+ * writes stay queued and land later.
100
+ */
101
+ export async function settlePersistence(withTeardownDeadline, { detach, closingSessionId }) {
102
+ const ids = closingSessionId ? [closingSessionId] : null;
103
+ const giveUp = new AbortController();
104
+ const pending = new Set(['summary index', 'gateway usage', 'session search']);
105
+ const track = (name, work) =>
106
+ Promise.resolve(work).then((settled) => {
107
+ if (settled !== false) pending.delete(name);
108
+ });
109
+ const settled = await withTeardownDeadline(
110
+ Promise.all([
111
+ track('summary index', settleSummaryIndexWrites({ ids, signal: giveUp.signal })),
112
+ track('gateway usage', settleGatewayUsageWrites()),
113
+ track('session search', closingSessionId ? closeSessionSearch(closingSessionId) : null),
114
+ ]).then(() => true),
115
+ detach ? DETACHED_FILE_STOP_MS : FULL_FILE_STOP_MS,
116
+ false
117
+ );
118
+ if (settled) return;
119
+ giveUp.abort();
120
+ const detail = [...pending].map((name) =>
121
+ name === 'summary index' && ids ? `summary index (sessions: ${pendingSummaryIndexIds(ids).join(', ')})` : name
122
+ );
123
+ process.emitWarning(`close persistence incomplete; still pending: ${detail.join('; ')}`, {
124
+ code: 'TEARDOWN_PERSISTENCE_PENDING',
125
+ });
126
+ }
127
+
82
128
  export async function settleAll(withTeardownDeadline, stops) {
83
129
  await Promise.allSettled([
84
130
  withTeardownDeadline(stops.channelStop, 5500, false),
85
131
  withTeardownDeadline(stops.mcpStop, 1500, false),
86
132
  withTeardownDeadline(stops.openaiWsStop, 1500, false),
87
133
  withTeardownDeadline(stops.patchStop, 1500, false),
88
- withTeardownDeadline(stops.memoryStop, 5500, false),
89
- withTeardownDeadline(stops.goalStop, 5500, false),
134
+ withTeardownDeadline(stops.memoryStop, FULL_FILE_STOP_MS, false),
135
+ withTeardownDeadline(stops.goalStop, FULL_FILE_STOP_MS, false),
90
136
  withTeardownDeadline(stops.shellJobsStop, 1500, false),
91
137
  withTeardownDeadline(stops.nativeToolStop, 1500, false),
92
138
  withTeardownDeadline(stops.computerStop, 1500, false),
@@ -11,7 +11,13 @@ import { flushAllIndexWrites } from '../services/agent-tool/index-write-queue.mj
11
11
  import { PROCESS_RESTART_FAILURE } from '../services/agent-tool/worker-index/row-liveness.mjs';
12
12
  import { unregisterLiveSession } from '../../runtime/shared/staged-update.mjs';
13
13
  import { abortRuntime, closeCanonicalSession, closeOwnSession, dispatchSessionEnd } from './teardown/session-close.mjs';
14
- import { settleAll, settleDetached, startRuntimeStops, startWorkStops } from './teardown/runtime-stops.mjs';
14
+ import {
15
+ settleAll,
16
+ settleDetached,
17
+ settlePersistence,
18
+ startRuntimeStops,
19
+ startWorkStops,
20
+ } from './teardown/runtime-stops.mjs';
15
21
 
16
22
  const CLOSE_ALL_DEADLINE_MS = 5000;
17
23
  const PROCESS_EXIT_REASON = /exit|quit|shutdown|sighup|sigint|sigterm/;
@@ -243,9 +249,22 @@ export function createTeardown(deps, { ingestSessionIntoMemory, closeSurfaceSess
243
249
  else await settleAll(withTeardownDeadline, stops);
244
250
  // Reported closed only once the barrier is on disk (closeSessionAsync),
245
251
  // and its write control is released only after that.
246
- const ok = await ownClose;
247
- // The session went away (idle eviction included): free its save baselines.
248
- if (scope.closingSessionId) forgetSessionSaveBaseline(scope.closingSessionId, { releaseWriteControl: true });
252
+ let ok = false;
253
+ try {
254
+ ok = await ownClose;
255
+ // The session went away (idle eviction included): free its save baselines.
256
+ if (scope.closingSessionId) forgetSessionSaveBaseline(scope.closingSessionId, { releaseWriteControl: true });
257
+ // The close's saves and turns queued buffered data-dir writes (summary
258
+ // index ops, gateway usage events, session-search writes) that run on
259
+ // later ticks: land or cancel them under the teardown deadline.
260
+ await settlePersistence(withTeardownDeadline, scope);
261
+ } finally {
262
+ finishClose(scope);
263
+ }
264
+ return ok;
265
+ }
266
+
267
+ function finishClose(scope) {
249
268
  // Self-update stages in the background and swaps on the next clean launch
250
269
  // (see staged-update.mjs) — nothing installs at shutdown. On a real
251
270
  // process exit we just drop this session's live-refcount pid file so a
@@ -257,7 +276,6 @@ export function createTeardown(deps, { ingestSessionIntoMemory, closeSurfaceSess
257
276
  /* advisory refcount only */
258
277
  }
259
278
  }
260
- return ok;
261
279
  }
262
280
 
263
281
  return {