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
@@ -20,9 +20,8 @@ import { resolveAgentSpawnPreset } from '../spawn-preset.mjs';
20
20
  import { AGENT_OWNER } from '../../../../runtime/agent/orchestrator/agent-owner.mjs';
21
21
  import { workspaceCwdLock } from '../../../workspace/admission-gate.mjs';
22
22
  import { isContactAgentId } from '../../../../runtime/agent/orchestrator/runtime-core/workflow-ids.mjs';
23
- import { AGENT_MODE, resolveSessionMode } from '../../../../runtime/agent/orchestrator/session/agent-mode.mjs';
24
- import { MIXY_AGENT_ID } from '../../../../runtime/agent/orchestrator/session/agent-home.mjs';
25
- import { mixyDisplayName } from '../../../../runtime/agent/orchestrator/session/mixy-name.mjs';
23
+ import { assertBotChainTarget } from '../../../../runtime/shared/bot-hops.mjs';
24
+ import { actingAgentOf, delegationChainOf } from '../../../bot-chain.mjs';
26
25
 
27
26
  export function createSpawnPlanner({
28
27
  mgr = null,
@@ -50,17 +49,14 @@ export function createSpawnPlanner({
50
49
  if (isAgentDisabled(config, agent)) {
51
50
  throw new Error(`agent spawn: agent "${agent}" is turned off`);
52
51
  }
53
- // Only Mixy hands work to a contact agent; any other agent-mode Lead
54
- // delegates to sub-agents alone (its catalog lists no contact either).
52
+ // Any agent-mode Lead (Mixy or a bot) may hand work to a contact agent,
53
+ // never to one already in its delegation chain (itself included): the
54
+ // session's ancestry and the bot chain its turn was handed (bot-chain.mjs).
55
55
  if (isContactAgentId(agent, readAgentFrontmatterKind(agent, dataDir, STANDALONE_SOURCE_ROOT))) {
56
56
  const callerId = clean(context?.callerSessionId || context?.sessionId);
57
- const caller = (callerId && mgr?.getSession?.(callerId)) || null;
58
- const { mode, agentId } = resolveSessionMode({ session: caller, sessionId: callerId });
59
- if (mode === AGENT_MODE && agentId !== MIXY_AGENT_ID) {
60
- throw new Error(
61
- `agent spawn: only ${mixyDisplayName()} (the representative agent) can hand work to the contact agent "${agent}"; delegate to a sub-agent instead`
62
- );
63
- }
57
+ const getSession = (id) => mgr?.getSession?.(id) || null;
58
+ const chain = delegationChainOf(callerId, getSession);
59
+ assertBotChainTarget(agent, { chain, self: actingAgentOf(getSession(callerId), callerId) }, 'agent spawn');
64
60
  }
65
61
  const agentPermission = readAgentFrontmatterPermission(agent, dataDir, STANDALONE_SOURCE_ROOT);
66
62
  const agentPerm = normalizeAgentPermission(agentPermission) || null;
@@ -31,6 +31,9 @@ export function resolveAgentSpawnPreset(config, args = {}) {
31
31
  const candidates = !clean(args.preset) ? configuredAgentRouteCandidates(config, agentName) : [];
32
32
  const agentRoute = candidates.map((candidate) => normalizeAgentRoute(candidate)).find(Boolean) || null;
33
33
  const storedFallbacks = candidates.find((candidate) => normalizeAgentRoute(candidate))?.fallbacks;
34
+ // Where the spawned session reads its fallback chain at each hop
35
+ // (route-fallback-chain.mjs): this agent's route, else Main's.
36
+ const fallbackSource = { agent: agentName };
34
37
  if (agentRoute) {
35
38
  return {
36
39
  presetName: agentPresetName(agentName),
@@ -41,6 +44,7 @@ export function resolveAgentSpawnPreset(config, args = {}) {
41
44
  provider: agentRoute.provider,
42
45
  model: agentRoute.model,
43
46
  ...(storedFallbacks?.length ? { fallbacks: storedFallbacks.map(normalizeAgentRoute) } : {}),
47
+ fallbackSource,
44
48
  effort: agentRoute.effort,
45
49
  fast: agentRoute.fast === true,
46
50
  ...(agentRoute.autoEffort === false ? { autoEffort: false } : {}),
@@ -54,12 +58,13 @@ export function resolveAgentSpawnPreset(config, args = {}) {
54
58
 
55
59
  const mainPreset = !clean(args.preset) ? findPreset(config, config?.default) : null;
56
60
  if (normalizeAgentRoute(mainPreset)) {
57
- return { presetName: mainPreset.id || mainPreset.name, preset: mainPreset };
61
+ return { presetName: mainPreset.id || mainPreset.name, preset: { ...mainPreset, fallbackSource } };
58
62
  }
59
63
 
60
64
  const presetName = clean(args.preset);
61
65
  if (!presetName) throw new Error(`agent: agent "${agentName}" has no Main model assignment`);
62
- const preset = findPreset(config, presetName) || synthesizePreset(config, presetName);
66
+ const stored = findPreset(config, presetName);
67
+ const preset = stored ? { ...stored, fallbackSource: { preset: presetName } } : synthesizePreset(config, presetName);
63
68
  if (!preset) throw new Error(`agent: preset "${presetName}" not found`);
64
69
  if (!normalizeAgentRoute(preset)) throw new Error(`agent: preset "${presetName}" has no complete route`);
65
70
  return { presetName, preset };
@@ -121,6 +121,7 @@ export function createTagRegistry({ dataDir, cfgMod, mgr }) {
121
121
  updateLeadWorkerCwd,
122
122
  forgetLeadSession,
123
123
  removeWorkerRow,
124
+ markReleased: index.markReleased,
124
125
  refreshTagsFromIndex,
125
126
  tags,
126
127
  tagAgents: tagMaps.tagAgents,
@@ -0,0 +1,51 @@
1
+ // worker-index/awaiting-user-sync.mjs
2
+ // A worker row's approval wait (`awaitingUser`), fenced to its session's turn
3
+ // and kept in step with the runtime this process runs for that session. Owns
4
+ // the sessions whose deferred rows still wait in the mutation batch.
5
+ import { clean } from '../helpers.mjs';
6
+ import { applyAwaitingUser, awaitingUserWouldChange } from './approval-wait.mjs';
7
+
8
+ export function createAwaitingUserSync(store, mgr) {
9
+ // Sessions whose deferred rows still wait in the batch; reconciled once the
10
+ // batch lands in the store.
11
+ const unreconciled = new Set();
12
+
13
+ /** Persist (or clear) a worker's approval wait as a field update fenced to
14
+ * the session and its turn (`turnAt`, ms): a row whose turn started later is
15
+ * left alone. Writes only when a row of that session exists and would
16
+ * change, so a Lead's own approvals cost nothing; a row that is not in the
17
+ * store yet takes the wait when it lands (reconcileAwaitingUser). */
18
+ function setAwaitingUser(sessionId, awaitingUser, { turnAt = 0 } = {}) {
19
+ const fence = {
20
+ sessionId: clean(sessionId),
21
+ awaitingUser: awaitingUser === 'approval' ? 'approval' : null,
22
+ turnAt: Number(turnAt) || 0,
23
+ };
24
+ if (!fence.sessionId || !awaitingUserWouldChange(store.readAll(), fence)) return false;
25
+ store.update((byKey) => applyAwaitingUser(byKey, fence));
26
+ return true;
27
+ }
28
+
29
+ /** The runtime of a session this process runs is the authority on its
30
+ * approval wait (the runtime-liveness counter behind `awaitingUser`): every
31
+ * row it creates or refreshes takes that state, so a missed or stale
32
+ * transition heals on the next write. No runtime here: not ours to judge. */
33
+ function reconcileAwaitingUser(sessionId) {
34
+ const runtime = mgr.getSessionRuntime?.(sessionId);
35
+ if (runtime) setAwaitingUser(sessionId, runtime.awaitingUser, { turnAt: runtime.askStartedAt });
36
+ }
37
+
38
+ /** A deferred row of `sessionId` is queued; reconciled when its batch lands. */
39
+ function deferReconcile(sessionId) {
40
+ unreconciled.add(sessionId);
41
+ }
42
+
43
+ /** The batch landed: reconcile every session whose deferred row it carried. */
44
+ function reconcileDeferred() {
45
+ const ids = [...unreconciled];
46
+ unreconciled.clear();
47
+ for (const id of ids) reconcileAwaitingUser(id);
48
+ }
49
+
50
+ return { setAwaitingUser, reconcileAwaitingUser, deferReconcile, reconcileDeferred };
51
+ }
@@ -0,0 +1,55 @@
1
+ // worker-index/disposal.mjs
2
+ // Disposing a worker index: queued rows persist first, then `teardown`
3
+ // releases its registrations; a failed drain leaves them for a retry. Owns
4
+ // the disposed / in-flight dispose state.
5
+ export function createIndexDisposal({ batch, store, teardown }) {
6
+ let disposed = false;
7
+ let disposePromise = null;
8
+
9
+ async function drainThenTeardown() {
10
+ batch.flush();
11
+ await store.flush();
12
+ teardown();
13
+ }
14
+
15
+ /** Persist queued rows, then release the exit registration. Active rows are
16
+ * NOT settled: they are keyed by process pid, which every inline runtime in
17
+ * the daemon shares, so settling here would idle other runtimes' workers.
18
+ * When the sync flush cannot run (this process's async writer holds the
19
+ * lock) the exit hook and store ref stay until the async drain settles. */
20
+ function dispose() {
21
+ if (disposed) return;
22
+ disposed = true;
23
+ let flushed = false;
24
+ try {
25
+ batch.flush();
26
+ flushed = store.flushSync();
27
+ } catch {}
28
+ if (flushed) {
29
+ teardown();
30
+ return;
31
+ }
32
+ disposePromise = drainThenTeardown().catch(reopenAfterFailedDrain);
33
+ }
34
+
35
+ /** A failed drain tears nothing down; allow a later dispose to retry it. */
36
+ function reopenAfterFailedDrain() {
37
+ disposed = false;
38
+ disposePromise = null;
39
+ }
40
+
41
+ /** Awaitable dispose: drain everything, then tear down. A failed drain
42
+ * rejects and leaves the registrations in place. */
43
+ function disposeAsync() {
44
+ if (!disposePromise) {
45
+ disposed = true;
46
+ disposePromise = drainThenTeardown().catch((error) => {
47
+ reopenAfterFailedDrain();
48
+ throw error;
49
+ });
50
+ }
51
+ return disposePromise;
52
+ }
53
+
54
+ return { dispose, disposeAsync };
55
+ }
@@ -0,0 +1,123 @@
1
+ // worker-index/released-store.mjs
2
+ // Which agent sessions their owner closed (the agent tool's `close`): session
3
+ // id → when. A closed session stays open and resumable, so a session scan
4
+ // (also after a daemon restart) would list it again; the marker is what keeps
5
+ // its rows `released` until a new turn starts on it. It lives in a file of its
6
+ // own beside the worker index, so a writer that rebuilds the index document
7
+ // (an older daemon) can never drop it. Entries expire after the tombstone TTL,
8
+ // judged on every read, not only when the file changes.
9
+ import { readFileSync, statSync } from 'node:fs';
10
+
11
+ import { parseJsonText } from '../../../../runtime/shared/atomic-file.mjs';
12
+ import { createIndexWriteQueue } from '../index-write-queue.mjs';
13
+ import { TAG_TOMBSTONE_TTL_MS } from '../worker-rows.mjs';
14
+
15
+ export const RELEASED_FILE = 'agent-workers-released.json';
16
+
17
+ /** id → epoch ms of every entry of a raw document (expired ones included). */
18
+ function parseReleased(doc) {
19
+ const raw = doc?.released && typeof doc.released === 'object' ? doc.released : {};
20
+ return new Map(
21
+ Object.entries(raw)
22
+ .map(([sessionId, at]) => [sessionId, Date.parse(String(at)) || 0])
23
+ .filter(([sessionId]) => sessionId)
24
+ );
25
+ }
26
+
27
+ /** The next document: unexpired entries plus the mutator's changes. */
28
+ function rewriteReleased(cur, mutator) {
29
+ const cutoff = Date.now() - TAG_TOMBSTONE_TTL_MS;
30
+ const entries = new Map();
31
+ for (const [sessionId, at] of parseReleased(cur)) {
32
+ if (at >= cutoff) entries.set(sessionId, new Date(at).toISOString());
33
+ }
34
+ mutator(entries);
35
+ return { version: 1, released: Object.fromEntries(entries) };
36
+ }
37
+
38
+ /** `file` may be null (no data dir): reads are empty and writes are no-ops. */
39
+ export function createReleasedStore(file) {
40
+ let cache = null; // { mtimeMs, size, doc, at }
41
+ let dirty = true;
42
+ // Unpersisted writes are read from the queue's projection.
43
+ let projected = null; // { version, at }
44
+
45
+ const queue = createIndexWriteQueue({
46
+ file,
47
+ rewrite: rewriteReleased,
48
+ readDoc: () => {
49
+ dirty = true;
50
+ entries();
51
+ return cache?.doc ?? null;
52
+ },
53
+ onPersisted: () => {
54
+ dirty = true;
55
+ },
56
+ });
57
+
58
+ /** id → epoch ms, as written (not yet filtered by expiry). */
59
+ function entries() {
60
+ const doc = queue.projection();
61
+ if (doc) {
62
+ if (!projected || projected.version !== queue.version()) {
63
+ projected = { version: queue.version(), at: parseReleased(doc) };
64
+ }
65
+ return projected.at;
66
+ }
67
+ if (!file) return new Map();
68
+ // Only a missing file is an empty store; any other read error propagates
69
+ // (queue.write reports it and queues nothing; a reader sees it thrown).
70
+ let st;
71
+ try {
72
+ st = statSync(file);
73
+ } catch (error) {
74
+ if (error?.code !== 'ENOENT') throw error;
75
+ cache = null;
76
+ return new Map();
77
+ }
78
+ if (!dirty && cache && cache.mtimeMs === st.mtimeMs && cache.size === st.size) return cache.at;
79
+ let parsed = null;
80
+ let text = null;
81
+ try {
82
+ text = readFileSync(file, 'utf8');
83
+ } catch (error) {
84
+ if (error?.code !== 'ENOENT') throw error;
85
+ }
86
+ if (text !== null) {
87
+ try {
88
+ parsed = parseJsonText(text);
89
+ } catch (error) {
90
+ // Left on disk and reported; the next save moves it aside to
91
+ // `<file>.corrupt-<ts>` (atomic-file's damaged-state recovery) instead
92
+ // of overwriting it.
93
+ process.stderr.write(`[released-store] ${file} is malformed (${error.message}); markers unreadable\n`);
94
+ }
95
+ }
96
+ cache = { mtimeMs: st.mtimeMs, size: st.size, doc: parsed, at: parseReleased(parsed) };
97
+ dirty = false;
98
+ return cache.at;
99
+ }
100
+
101
+ return {
102
+ /** Whether the owner closed this session less than the TTL ago. */
103
+ isReleased: (sessionId) => entries().get(sessionId) >= Date.now() - TAG_TOMBSTONE_TTL_MS,
104
+ mark(sessionIds) {
105
+ const at = new Date().toISOString();
106
+ queue.write((entriesById) => {
107
+ for (const sessionId of sessionIds) entriesById.set(sessionId, at);
108
+ });
109
+ },
110
+ clear(sessionId) {
111
+ if (!entries().has(sessionId)) return;
112
+ queue.write((entriesById) => {
113
+ entriesById.delete(sessionId);
114
+ });
115
+ },
116
+ flush: queue.flush,
117
+ flushSync: queue.flushSync,
118
+ hasUnpersisted: queue.hasUnpersisted,
119
+ invalidate: () => {
120
+ dirty = true;
121
+ },
122
+ };
123
+ }
@@ -0,0 +1,112 @@
1
+ // worker-index/restart-settlement.mjs
2
+ // Rows whose turn a process exit or restart ended: this process's active rows
3
+ // settled as failed on exit, stale active rows and approval waits settled at
4
+ // startup and on later sweeps, and the owner's restart-failure notification
5
+ // stamped once queued. Owns the keys of the rows this process marked active.
6
+ import { positiveInt } from '../helpers.mjs';
7
+ import { workerRowKey } from '../worker-rows.mjs';
8
+ import { clearAwaitingUser } from './approval-wait.mjs';
9
+ import {
10
+ createRowLiveness,
11
+ isActiveWorkerRow,
12
+ isStaleApprovalWait,
13
+ isUnnotifiedRestartFailure,
14
+ PROCESS_RESTART_FAILURE,
15
+ runtimeDied,
16
+ } from './row-liveness.mjs';
17
+
18
+ export function createRestartSettlement({ store, cfgMod, mgr }) {
19
+ const liveness = createRowLiveness({
20
+ cfgMod,
21
+ isSessionRunning: (sessionId) => mgr.isSessionTurnInFlight?.(sessionId) === true,
22
+ });
23
+ // Rows this process marked active; settled as failed on exit (their turn
24
+ // dies with the process) so a crash of the host never leaves them
25
+ // "running" forever.
26
+ const activeWorkerKeys = new Set();
27
+
28
+ /** Track whether the row written under `key` is one this process runs. */
29
+ function trackActiveRow(key, row) {
30
+ if (isActiveWorkerRow(row)) activeWorkerKeys.add(key);
31
+ else activeWorkerKeys.delete(key);
32
+ }
33
+
34
+ function flushActiveWorkerRows() {
35
+ if (!activeWorkerKeys.size) return;
36
+ const keys = [...activeWorkerKeys];
37
+ activeWorkerKeys.clear();
38
+ const now = Date.now();
39
+ store.write((byKey) => {
40
+ for (const key of keys) {
41
+ const current = byKey.get(key);
42
+ if (!current || positiveInt(current.runtimePid) !== process.pid) continue;
43
+ if (isActiveWorkerRow(current)) {
44
+ byKey.set(key, liveness.failedRow(current, now, PROCESS_RESTART_FAILURE, true));
45
+ }
46
+ }
47
+ });
48
+ }
49
+
50
+ // Startup recovery: stale active rows settle — as failed when another
51
+ // process ran their turn (it is gone, or is the predecessor still shutting
52
+ // down: either way the turn never finished), otherwise to idle — and an
53
+ // approval wait left behind by a runtime that is gone (daemon restart) is
54
+ // cleared. `deadOnly` (later sweeps) settles only rows whose runtime is
55
+ // provably dead, never a live process's quiet turn.
56
+ function recoverStaleWorkerRows({ deadOnly = false } = {}) {
57
+ const now = Date.now();
58
+ const rows = store.readAll();
59
+ const isStale = deadOnly
60
+ ? (row) => isActiveWorkerRow(row) && runtimeDied(row)
61
+ : (row) => liveness.isStaleActive(row, now);
62
+ const staleActive = rows.filter(isStale);
63
+ const staleWaits = rows.some(isStaleApprovalWait);
64
+ if (!staleActive.length && !staleWaits) return false;
65
+ if (staleActive.length) {
66
+ store.write((byKey) => {
67
+ for (const row of staleActive) {
68
+ const key = workerRowKey(row);
69
+ const current = byKey.get(key);
70
+ if (!current || !isStale(current)) continue;
71
+ const ranElsewhere = positiveInt(current.runtimePid) && positiveInt(current.runtimePid) !== process.pid;
72
+ byKey.set(
73
+ key,
74
+ ranElsewhere ? liveness.failedRow(current, now, PROCESS_RESTART_FAILURE) : liveness.idleRow(current, now)
75
+ );
76
+ }
77
+ });
78
+ }
79
+ if (staleWaits) store.update((byKey) => clearAwaitingUser(byKey, isStaleApprovalWait));
80
+ return true;
81
+ }
82
+
83
+ /** Rows whose turn a process restart ended and whose owner has not been told
84
+ * yet — whoever settled the row (this process at startup, or the
85
+ * predecessor's exit flush). Read-only: the caller enqueues the owner's
86
+ * notification durably, THEN calls `markRestartNotified`, so a crash in
87
+ * between retries (the notification's stable id makes the retry exactly-once). */
88
+ function pendingRestartFailures() {
89
+ return store.readAll().filter(isUnnotifiedRestartFailure);
90
+ }
91
+
92
+ /** Stamp these rows as told, once their notification is durably queued. */
93
+ function markRestartNotified(rows) {
94
+ const stamp = new Date().toISOString();
95
+ store.write((byKey) => {
96
+ for (const row of rows) {
97
+ const key = workerRowKey(row);
98
+ const current = byKey.get(key);
99
+ if (!current || !isUnnotifiedRestartFailure(current)) continue;
100
+ byKey.set(key, { ...current, restartNotified: stamp });
101
+ }
102
+ });
103
+ }
104
+
105
+ return {
106
+ trackActiveRow,
107
+ flushActiveWorkerRows,
108
+ recoverStaleWorkerRows,
109
+ pendingRestartFailures,
110
+ markRestartNotified,
111
+ };
112
+ }
@@ -0,0 +1,36 @@
1
+ // worker-index/row-reads.mjs
2
+ // Scoped reads of the worker rows and tag tombstones: a row a tombstone still
3
+ // blocks never reads as work, and a legacy tombstone without an owner takes
4
+ // the one its session resolves to.
5
+ import { clean, rowMatchesContext } from '../helpers.mjs';
6
+ import { recoverTagTombstoneOwner } from '../worker-rows.mjs';
7
+ import {
8
+ findTagTombstone,
9
+ tagTombstoneKey,
10
+ tombstoneBlocksWork,
11
+ } from '../../../../runtime/shared/agent-reap-state.mjs';
12
+
13
+ export function createWorkerRowReads(store, mgr) {
14
+ function readWorkerRows(context = {}) {
15
+ const { rows, tombstones: savedTombstones } = store.readSnapshot();
16
+ if (rows.length === 0) return rows;
17
+ const tombstones = new Map(recoverTombstoneOwners(savedTombstones).map((row) => [tagTombstoneKey(row), row]));
18
+ return rows.filter(
19
+ (row) => rowMatchesContext(row, context) && !tombstoneBlocksWork(row, findTagTombstone(row, tombstones))
20
+ );
21
+ }
22
+
23
+ function readAllTagTombstones() {
24
+ return recoverTombstoneOwners(store.readTombstones());
25
+ }
26
+
27
+ function recoverTombstoneOwners(rows) {
28
+ if (!rows.some((row) => !clean(row.parentSessionId || row.ownerSessionId))) return rows;
29
+ const sessions = typeof mgr.listSessions === 'function' ? mgr.listSessions({ includeClosed: true }) : [];
30
+ // Resolve legacy ownership against all sessions, never just the caller's
31
+ // scope: filtering first would conceal an ambiguous sibling owner.
32
+ return rows.map((row) => recoverTagTombstoneOwner(row, sessions));
33
+ }
34
+
35
+ return { readWorkerRows, readAllTagTombstones };
36
+ }
@@ -65,6 +65,8 @@ export function normalizeWorkerRows(value) {
65
65
  // Additive and transient: present only while the worker's turn waits on
66
66
  // a user approval (worker-index setAwaitingUser).
67
67
  ...(row.awaitingUser === 'approval' ? { awaitingUser: 'approval' } : {}),
68
+ // Present only on an agent its owner closed (the agent tool's `close`).
69
+ ...(row.released === true ? { released: true } : {}),
68
70
  }))
69
71
  .filter((row) => !isLeadPoolAgent(row.agent) && keepWorkerRow(row));
70
72
  }
@@ -4,10 +4,11 @@
4
4
  // worker index of a process over one file shares one store (acquireWorkerRowStore),
5
5
  // so all of them serialize through the same projection and queue.
6
6
  import { readFileSync, statSync } from 'node:fs';
7
- import { resolve } from 'node:path';
7
+ import { dirname, join, resolve } from 'node:path';
8
8
 
9
9
  import { createIndexWriteQueue } from '../index-write-queue.mjs';
10
10
  import { normalizeTagTombstones, tagTombstoneKey, workerRowKey } from '../worker-rows.mjs';
11
+ import { RELEASED_FILE, createReleasedStore } from './released-store.mjs';
11
12
  import { settleAwaitingUser } from './approval-wait.mjs';
12
13
  import { keepWorkerRow, normalizeWorkerRows } from './row-shape.mjs';
13
14
 
@@ -62,6 +63,9 @@ function rewriteIndexDocument(cur, mutator) {
62
63
 
63
64
  /** `file` may be null (no data dir): reads are empty and writes are no-ops. */
64
65
  export function createWorkerRowStore(file) {
66
+ // The release markers live in their own file beside the index, so a writer
67
+ // that rebuilds the index document never drops them.
68
+ const released = createReleasedStore(file ? join(dirname(file), RELEASED_FILE) : null);
65
69
  // Mtime-keyed parse cache. A single spawn calls refreshTagsFromSessions /
66
70
  // resolveTag / nextTag, which each re-read and re-parse this file; across a
67
71
  // parallel fanout that is O(spawns^2) synchronous reads of the same bytes.
@@ -148,18 +152,27 @@ export function createWorkerRowStore(file) {
148
152
  readAll,
149
153
  readSnapshot,
150
154
  readTombstones,
155
+ isReleased: released.isReleased,
156
+ markReleased: released.mark,
157
+ clearReleased: released.clear,
151
158
  write,
152
159
  /** Conditional field update: `mutator` runs now on the projection and again
153
160
  * on the locked document when persisted, so its condition is re-checked
154
161
  * against what is on disk. The one writer of `awaitingUser`. */
155
162
  update: (mutator) => queue.write(mutator, { rerun: true }),
156
163
  /** Resolves once every write so far is on disk. */
157
- flush: queue.flush,
164
+ flush: async () => {
165
+ await Promise.all([queue.flush(), released.flush()]);
166
+ },
158
167
  /** Exit path: persist what is still queued, synchronously. */
159
- flushSync: queue.flushSync,
160
- hasUnpersisted: queue.hasUnpersisted,
168
+ flushSync: () => {
169
+ const flushed = [queue.flushSync(), released.flushSync()];
170
+ return flushed.every(Boolean);
171
+ },
172
+ hasUnpersisted: () => queue.hasUnpersisted() || released.hasUnpersisted(),
161
173
  invalidate: () => {
162
174
  dirty = true;
175
+ released.invalidate();
163
176
  },
164
177
  };
165
178
  }
@@ -0,0 +1,58 @@
1
+ // worker-index/row-writes.mjs
2
+ // Row upserts (immediate or batched), removal, and the owner's release mark:
3
+ // every upsert projects the row into the tag maps (`bindTag`), tracks whether
4
+ // this process runs it, and brings its approval wait in step.
5
+ import { clean } from '../helpers.mjs';
6
+ import { applyWorkerRowUpsert, workerRowKey } from '../worker-rows.mjs';
7
+ import { isUnnotifiedRestartFailure } from './row-liveness.mjs';
8
+ import { normalizeWorkerRows, workerRowFromSession } from './row-shape.mjs';
9
+
10
+ export function createWorkerRowWrites({ file, store, batch, mgr, bindTag, settlement, awaitingUser }) {
11
+ function upsertWorkerRow(row, { defer = false } = {}) {
12
+ const normalized = normalizeWorkerRows({ workers: [row] })[0];
13
+ if (!normalized) return false;
14
+ const key = workerRowKey(normalized);
15
+ settlement.trackActiveRow(key, normalized);
16
+ bindTag(normalized);
17
+ if (defer) {
18
+ if (!file) return false;
19
+ awaitingUser.deferReconcile(normalized.sessionId);
20
+ return batch.queue((byKey) => applyWorkerRowUpsert(byKey, normalized));
21
+ }
22
+ store.write((byKey) => {
23
+ applyWorkerRowUpsert(byKey, normalized);
24
+ });
25
+ awaitingUser.reconcileAwaitingUser(normalized.sessionId);
26
+ return true;
27
+ }
28
+
29
+ // A session its owner closed keeps `released` on every row written for it,
30
+ // until a turn starts on it.
31
+ const rowFromSession = (session, fallbackTag = '', extra = {}) => {
32
+ const row = workerRowFromSession(session, fallbackTag, extra, (sessionId) => mgr.getSessionRuntime?.(sessionId));
33
+ return row && store.isReleased(row.sessionId) ? { ...row, released: true } : row;
34
+ };
35
+
36
+ /** Persist that the owner closed these sessions (survives a restart). */
37
+ function markReleased(sessionIds) {
38
+ store.markReleased(sessionIds.map(clean).filter(Boolean));
39
+ }
40
+
41
+ // A row whose owner is still owed its restart failure stays: closing the
42
+ // worker (a process exit closes every one) must not lose that result.
43
+ function removeWorkerRow({ tag = '', sessionId = '' } = {}) {
44
+ const targetTag = clean(tag);
45
+ const targetSessionId = clean(sessionId);
46
+ batch.flush();
47
+ store.write((byKey) => {
48
+ for (const [key, row] of [...byKey.entries()]) {
49
+ if (isUnnotifiedRestartFailure(row)) continue;
50
+ if ((targetSessionId && row.sessionId === targetSessionId) || (targetTag && row.tag === targetTag)) {
51
+ byKey.delete(key);
52
+ }
53
+ }
54
+ });
55
+ }
56
+
57
+ return { upsertWorkerRow, rowFromSession, markReleased, removeWorkerRow };
58
+ }