mixdog 1.3.5 → 1.4.1

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 (515) hide show
  1. package/NOTICE.md +2 -2
  2. package/README.md +2 -2
  3. package/package.json +2 -11
  4. package/scripts/check-voice-runtime-inputs.mjs +25 -0
  5. package/scripts/daemon-stub.mjs +1 -1
  6. package/scripts/gate-local.mjs +16 -5
  7. package/scripts/latest-guard.mjs +21 -9
  8. package/scripts/lib/isolated-test-env.mjs +10 -5
  9. package/scripts/lib/run-node-tests.mjs +37 -37
  10. package/scripts/lib/scoped-temp.d.mts +16 -0
  11. package/scripts/lib/scoped-temp.mjs +123 -0
  12. package/scripts/lib/stale-temp.mjs +44 -0
  13. package/scripts/native-release-status.mjs +2 -2
  14. package/scripts/post-release-check.mjs +1 -6
  15. package/scripts/pptx-kit-index.mjs +17 -4
  16. package/scripts/recall-history-cases.json +1 -1
  17. package/scripts/suite-health.mjs +20 -2
  18. package/scripts/test-data-dir.mjs +10 -7
  19. package/scripts/test.mjs +7 -11
  20. package/src/agents/reviewer/AGENT.md +7 -11
  21. package/src/agents/reviewer/agent.json +1 -1
  22. package/src/app.mjs +4 -0
  23. package/src/defaults/shipped-history.json +22 -1
  24. package/src/defaults/skills/browser-use/SKILL.md +62 -12
  25. package/src/defaults/skills/code-tidy/SKILL.md +142 -270
  26. package/src/defaults/skills/code-tidy/references/agent-cleanup.md +4 -4
  27. package/src/defaults/skills/code-tidy/references/cleanup-report.md +31 -76
  28. package/src/defaults/skills/code-tidy/references/dead-code.md +6 -12
  29. package/src/defaults/skills/computer-use/SKILL.md +35 -9
  30. package/src/defaults/skills/setup/SKILL.md +22 -0
  31. package/src/defaults/skills/setup/references/actions.md +39 -5
  32. package/src/defaults/skills/setup/references/surfaces.md +11 -2
  33. package/src/defaults/skills/visualize/SKILL.md +2 -1
  34. package/src/lib/rules-builder.cjs +39 -20
  35. package/src/output-styles/common.md +2 -2
  36. package/src/output-styles/simple.md +1 -1
  37. package/src/rules/agent/AGENT.md +11 -10
  38. package/src/rules/lead/LEAD.md +23 -20
  39. package/src/rules/shared/05-tool-calls.md +5 -6
  40. package/src/rules/shared/10-tool-workflow.md +4 -0
  41. package/src/rules/shared/45-destructive.md +0 -2
  42. package/src/rules/shared/50-execution.md +2 -0
  43. package/src/rules/shared/60-verification.md +7 -5
  44. package/src/rules/shared/70-delivery.md +7 -9
  45. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -6
  46. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -3
  47. package/src/runtime/agent/orchestrator/agent-runtime/title-completion.mjs +3 -1
  48. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +3 -1
  49. package/src/runtime/agent/orchestrator/cache-break-trace.mjs +2 -0
  50. package/src/runtime/agent/orchestrator/config.mjs +17 -1
  51. package/src/runtime/agent/orchestrator/context/collect.mjs +3 -0
  52. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +73 -68
  53. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +57 -2
  54. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +3 -3
  55. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +176 -72
  56. package/src/runtime/agent/orchestrator/mcp/client.mjs +19 -0
  57. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +5 -2
  58. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +192 -133
  59. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +42 -6
  60. package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +1 -1
  61. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +7 -3
  62. package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +8 -2
  63. package/src/runtime/agent/orchestrator/providers/anthropic-cache-warm.mjs +165 -0
  64. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +6 -2
  65. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -2
  66. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  67. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +4 -1
  68. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +14 -14
  69. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +3 -1
  70. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +159 -69
  71. package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +25 -3
  72. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +70 -33
  73. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -2
  74. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +135 -87
  75. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +123 -102
  76. package/src/runtime/agent/orchestrator/providers/cache-warm-admission.mjs +90 -0
  77. package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +1 -10
  78. package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +25 -9
  79. package/src/runtime/agent/orchestrator/providers/cursor.mjs +25 -13
  80. package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +1 -1
  81. package/src/runtime/agent/orchestrator/providers/devin.mjs +3 -0
  82. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +210 -20
  83. package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -11
  84. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +45 -6
  85. package/src/runtime/agent/orchestrator/providers/lib/first-output-window.mjs +58 -0
  86. package/src/runtime/agent/orchestrator/providers/lib/listen-on.mjs +14 -0
  87. package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +17 -3
  88. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +52 -10
  89. package/src/runtime/agent/orchestrator/providers/mimo-oauth-login.mjs +1 -10
  90. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +8 -1
  91. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
  92. package/src/runtime/agent/orchestrator/providers/npm-cli-version.mjs +11 -3
  93. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +2 -1
  94. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
  95. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +9 -2
  96. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +7 -0
  97. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +14 -3
  98. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +4 -2
  99. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
  100. package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +5 -3
  101. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +16 -3
  102. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
  103. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +5 -2
  104. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +13 -3
  105. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +13 -2
  106. package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +81 -42
  107. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +8 -0
  108. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +136 -5
  109. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -9
  110. package/src/runtime/agent/orchestrator/providers/openai-ws-send/send-context.mjs +22 -3
  111. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +43 -5
  112. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/handshake-failure.mjs +3 -1
  113. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/stream-failure.mjs +6 -3
  114. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +20 -12
  115. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +7 -1
  116. package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +48 -22
  117. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +65 -1
  118. package/src/runtime/agent/orchestrator/providers/quota-reading.mjs +38 -12
  119. package/src/runtime/agent/orchestrator/providers/registry.mjs +38 -0
  120. package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +39 -7
  121. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +6 -0
  122. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +49 -16
  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 +7 -6
  127. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +9 -3
  128. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +19 -4
  129. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +125 -30
  130. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +3 -1
  131. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +5 -2
  132. package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +1 -3
  133. package/src/runtime/agent/orchestrator/session/cache-warm.mjs +798 -0
  134. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
  135. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -8
  136. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +23 -0
  137. package/src/runtime/agent/orchestrator/session/compact/observation-supersession.mjs +103 -23
  138. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +280 -45
  139. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +475 -58
  140. package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +42 -17
  141. package/src/runtime/agent/orchestrator/session/context-utils.mjs +13 -1
  142. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +163 -22
  143. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +132 -17
  144. package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +38 -7
  145. package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +3 -0
  146. package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +90 -22
  147. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +4 -1
  148. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +11 -5
  149. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +0 -2
  150. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -4
  151. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +1 -1
  152. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +51 -25
  153. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +37 -3
  154. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +3 -1
  155. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +1 -1
  156. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -1
  157. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +13 -5
  158. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +31 -3
  159. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +20 -3
  160. package/src/runtime/agent/orchestrator/session/loop/tool-exec/after-hook.mjs +2 -1
  161. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +5 -4
  162. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +16 -2
  163. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +16 -1
  164. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +49 -21
  165. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -10
  166. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +12 -3
  167. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -2
  168. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +19 -1
  169. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +133 -90
  170. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +3 -3
  171. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +3 -1
  172. package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +31 -4
  173. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +30 -18
  174. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +24 -14
  175. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +11 -2
  176. package/src/runtime/agent/orchestrator/session/manager/provider-replay.mjs +13 -0
  177. package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +130 -0
  178. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +27 -9
  179. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +81 -8
  180. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +2 -0
  181. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +27 -8
  182. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +12 -5
  183. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +1 -1
  184. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +9 -4
  185. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +4 -0
  186. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +102 -15
  187. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +21 -5
  188. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +9 -0
  189. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +24 -9
  190. package/src/runtime/agent/orchestrator/session/prompt-cache-idle-gaps.mjs +113 -0
  191. package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +150 -0
  192. package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.mjs +154 -0
  193. package/src/runtime/agent/orchestrator/session/routine-result.mjs +36 -9
  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-delete.mjs +7 -0
  203. package/src/runtime/agent/orchestrator/session/store/session-persistence-events.mjs +29 -0
  204. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +8 -6
  205. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +16 -2
  206. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +15 -0
  207. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +81 -22
  208. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +92 -3
  209. package/src/runtime/agent/orchestrator/session/stored-attachment-history.mjs +32 -0
  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/transcript-restore/restore.mjs +79 -3
  214. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-status.mjs +9 -7
  215. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-text.mjs +2 -2
  216. package/src/runtime/agent/orchestrator/session/turn-failure.mjs +5 -1
  217. package/src/runtime/agent/orchestrator/stall-policy.mjs +23 -0
  218. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +5 -2
  219. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +12 -1
  220. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +2 -2
  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/spawn-env.mjs +4 -2
  225. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +31 -6
  226. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +110 -26
  227. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +7 -1
  228. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +31 -5
  229. package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +22 -8
  230. package/src/runtime/agent/orchestrator/tools/builtin/lib/scoped-search-flight.mjs +9 -5
  231. package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +7 -1
  232. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +6 -3
  233. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +1 -1
  234. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +23 -12
  235. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +6 -1
  236. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +1 -8
  237. package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +7 -1
  238. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/federation.mjs +3 -1
  239. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -1
  240. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
  241. package/src/runtime/agent/orchestrator/tools/lib/isolated-test-env.mjs +4 -3
  242. package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +26 -4
  243. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +36 -13
  244. package/src/runtime/browser-bridge/action-schema.mjs +13 -2
  245. package/src/runtime/browser-bridge/client.mjs +73 -8
  246. package/src/runtime/browser-bridge/input-fields.mjs +8 -9
  247. package/src/runtime/browser-bridge/mail-links.mjs +107 -0
  248. package/src/runtime/browser-bridge/tool-defs.mjs +1 -1
  249. package/src/runtime/channels/lib/config.mjs +6 -61
  250. package/src/runtime/channels/lib/crash-log.mjs +1 -1
  251. package/src/runtime/channels/lib/event-pipeline.mjs +4 -19
  252. package/src/runtime/channels/lib/event-queue.mjs +3 -18
  253. package/src/runtime/channels/lib/owned-runtime/automation-services.mjs +2 -2
  254. package/src/runtime/channels/lib/owned-runtime/automation-start.mjs +3 -4
  255. package/src/runtime/channels/lib/owned-runtime/config-reload.mjs +4 -59
  256. package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +33 -115
  257. package/src/runtime/channels/lib/owned-runtime/owned-stop.mjs +5 -22
  258. package/src/runtime/channels/lib/owned-runtime/state.mjs +2 -8
  259. package/src/runtime/channels/lib/owned-runtime.mjs +6 -23
  260. package/src/runtime/channels/lib/parent-bridge.mjs +2 -2
  261. package/src/runtime/channels/lib/runtime-paths.mjs +4 -43
  262. package/src/runtime/channels/lib/scheduler.mjs +33 -147
  263. package/src/runtime/channels/lib/schedules-db.mjs +34 -11
  264. package/src/runtime/channels/lib/status-snapshot.mjs +1 -1
  265. package/src/runtime/channels/lib/tool-dispatch.mjs +1 -2
  266. package/src/runtime/channels/lib/webhook/signature.mjs +4 -1
  267. package/src/runtime/channels/lib/webhooks-db.mjs +5 -9
  268. package/src/runtime/channels/lib/whisper-port.mjs +10 -1
  269. package/src/runtime/channels/lib/worker-bootstrap.mjs +0 -1
  270. package/src/runtime/channels/lib/worker-ipc.mjs +0 -1
  271. package/src/runtime/channels/lib/worker-main.mjs +10 -34
  272. package/src/runtime/computer-bridge/action-schema.mjs +42 -1
  273. package/src/runtime/computer-bridge/actions.mjs +9 -1
  274. package/src/runtime/computer-bridge/client.mjs +180 -8
  275. package/src/runtime/computer-bridge/core-actions.mjs +2 -2
  276. package/src/runtime/computer-bridge/error-recovery.mjs +28 -2
  277. package/src/runtime/computer-bridge/linux-action-capabilities.mjs +24 -5
  278. package/src/runtime/computer-bridge/linux-readiness.d.mts +7 -2
  279. package/src/runtime/computer-bridge/linux-readiness.mjs +65 -23
  280. package/src/runtime/computer-bridge/pending-continuation.mjs +1 -1
  281. package/src/runtime/connectors/connector-accounts-mcp.mjs +230 -0
  282. package/src/runtime/connectors/gmail-app-password.mjs +172 -39
  283. package/src/runtime/connectors/google-mcp.mjs +10 -12
  284. package/src/runtime/local-provider/server-process.mjs +2 -1
  285. package/src/runtime/media/adapters/gemini-video.mjs +4 -1
  286. package/src/runtime/media/adapters/xai-media.mjs +2 -1
  287. package/src/runtime/memory/lib/embedding-worker.mjs +6 -3
  288. package/src/runtime/memory/lib/memory-action-handlers/cycle-backfill-action.mjs +1 -7
  289. package/src/runtime/memory/lib/memory-chunk-quality.mjs +1 -6
  290. package/src/runtime/memory/lib/pg/adapter.mjs +77 -70
  291. package/src/runtime/memory/lib/pg/supervisor.mjs +24 -12
  292. package/src/runtime/memory/lib/runtime-fetcher.mjs +23 -5
  293. package/src/runtime/office/authoring/pdf-author-action.mjs +1 -6
  294. package/src/runtime/office/design/design-art-direction.mjs +29 -27
  295. package/src/runtime/office/pdf/pdf-writer.mjs +6 -1
  296. package/src/runtime/office/portable/portable-snapshot-docx.mjs +7 -1
  297. package/src/runtime/office/portable/portable-snapshot-shared.mjs +6 -1
  298. package/src/runtime/office/quality/assurance-rendered.mjs +9 -2
  299. package/src/runtime/office/quality/assurance-structure-docx.mjs +3 -1
  300. package/src/runtime/office/quality/design-review.mjs +1 -5
  301. package/src/runtime/office/quality/document-brief.mjs +1 -5
  302. package/src/runtime/office/quality/forced-break-pages.mjs +9 -6
  303. package/src/runtime/office/quality/quality-pipeline.mjs +1 -1
  304. package/src/runtime/session-search/session-search-db.mjs +73 -9
  305. package/src/runtime/session-search/session-search-ingest.mjs +110 -20
  306. package/src/runtime/shared/abort-race.mjs +89 -0
  307. package/src/runtime/shared/agent-avatar.d.mts +0 -1
  308. package/src/runtime/shared/agent-kind.mjs +4 -3
  309. package/src/runtime/shared/bot-owned-work.mjs +56 -0
  310. package/src/runtime/shared/bounded-download.mjs +8 -3
  311. package/src/runtime/shared/config.mjs +2 -13
  312. package/src/runtime/shared/connector-accounts.mjs +20 -10
  313. package/src/runtime/shared/connector-catalog.d.mts +29 -0
  314. package/src/runtime/shared/connector-catalog.mjs +150 -0
  315. package/src/runtime/shared/connector-triggers.mjs +22 -19
  316. package/src/runtime/shared/defaults-separation.mjs +2 -17
  317. package/src/runtime/shared/delegated-approval-host.mjs +103 -11
  318. package/src/runtime/shared/err-text.mjs +24 -15
  319. package/src/runtime/shared/fetch-safe-listen.d.mts +16 -0
  320. package/src/runtime/shared/fetch-safe-listen.mjs +50 -0
  321. package/src/runtime/shared/file-permissions.d.mts +0 -8
  322. package/src/runtime/shared/file-permissions.mjs +0 -11
  323. package/src/runtime/shared/git-direct.test-support.mjs +19 -0
  324. package/src/runtime/shared/handoff-artifacts.d.mts +2 -1
  325. package/src/runtime/shared/handoff-artifacts.mjs +18 -8
  326. package/src/runtime/shared/heartbeat-interval.d.mts +4 -0
  327. package/src/runtime/shared/heartbeat-interval.mjs +19 -0
  328. package/src/runtime/shared/idle-auto-clear-gate.mjs +126 -0
  329. package/src/runtime/shared/llm/cost.mjs +5 -1
  330. package/src/runtime/shared/llm/usage-rollup.mjs +1 -1
  331. package/src/runtime/shared/loopback-listener.mjs +25 -2
  332. package/src/runtime/shared/mixy-presence.mjs +22 -0
  333. package/src/runtime/shared/model-route-chain.d.mts +7 -2
  334. package/src/runtime/shared/model-route-chain.mjs +22 -4
  335. package/src/runtime/shared/open-url.mjs +10 -0
  336. package/src/runtime/shared/orchestration.d.mts +2 -0
  337. package/src/runtime/shared/pg-schema-db.mjs +27 -16
  338. package/src/runtime/shared/profile-config.cjs +1 -1
  339. package/src/runtime/shared/prompt-origin.d.mts +4 -0
  340. package/src/runtime/shared/prompt-origin.mjs +21 -0
  341. package/src/runtime/shared/provider-accounts.mjs +7 -1
  342. package/src/runtime/shared/provider-display-name.mjs +29 -0
  343. package/src/runtime/shared/provider-timeout-phase.mjs +2 -0
  344. package/src/runtime/shared/routine-trigger.d.mts +2 -0
  345. package/src/runtime/shared/routine-trigger.mjs +10 -0
  346. package/src/runtime/shared/sensitive-path-rules.d.mts +2 -10
  347. package/src/runtime/shared/sensitive-path-rules.mjs +19 -41
  348. package/src/runtime/shared/session-activity-signal.mjs +48 -1
  349. package/src/runtime/shared/session-scratch.mjs +37 -0
  350. package/src/runtime/shared/session-sidebar-host.d.mts +1 -1
  351. package/src/runtime/shared/session-sidebar-host.mjs +7 -3
  352. package/src/runtime/shared/session-turn-prelude.mjs +18 -0
  353. package/src/runtime/shared/tool-card-model/agent-surface.mjs +1 -3
  354. package/src/runtime/shared/tool-card-model/card-status.mjs +15 -2
  355. package/src/runtime/shared/tool-result-summary.mjs +2 -15
  356. package/src/runtime/shared/tool-surface.mjs +3 -0
  357. package/src/runtime/shared/tool-work-units.mjs +2 -0
  358. package/src/runtime/shared/turn-snapshot-store.mjs +13 -6
  359. package/src/runtime/shared/worktrees/contract.mjs +4 -1
  360. package/src/runtime/shared/worktrees/git.mjs +15 -0
  361. package/src/runtime/shared/worktrees/manager.mjs +87 -38
  362. package/src/runtime/shared/worktrees/test-support.mjs +14 -13
  363. package/src/runtime/terminal-bridge/client.mjs +10 -1
  364. package/src/runtime/visualize-bridge/client.mjs +29 -8
  365. package/src/runtime/visualize-bridge/tool-defs.mjs +22 -8
  366. package/src/runtime/visualize-bridge/tool-meta.d.mts +5 -0
  367. package/src/runtime/visualize-bridge/tool-meta.mjs +22 -0
  368. package/src/runtime/web-search/lib/fetch-pipeline.mjs +2 -2
  369. package/src/session-runtime/agent-home-delivery.mjs +489 -185
  370. package/src/session-runtime/agent-task-sessions.mjs +467 -229
  371. package/src/session-runtime/agent-tool-routing.mjs +8 -41
  372. package/src/session-runtime/boot/apis.mjs +9 -0
  373. package/src/session-runtime/boot/infrastructure.mjs +4 -0
  374. package/src/session-runtime/boot/providers.mjs +3 -0
  375. package/src/session-runtime/boot/tools.mjs +11 -2
  376. package/src/session-runtime/bot-status.mjs +12 -6
  377. package/src/session-runtime/bot-target.mjs +38 -0
  378. package/src/session-runtime/channel-config-api.mjs +6 -1
  379. package/src/session-runtime/config-lifecycle.mjs +6 -0
  380. package/src/session-runtime/connector-merge-migration.mjs +305 -0
  381. package/src/session-runtime/conversation-import/codex-titles.mjs +88 -0
  382. package/src/session-runtime/conversation-import/discovery.mjs +271 -25
  383. package/src/session-runtime/conversation-import/service.mjs +26 -5
  384. package/src/session-runtime/handoff-attachments.mjs +82 -31
  385. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +144 -41
  386. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -0
  387. package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +4 -1
  388. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +8 -2
  389. package/src/session-runtime/lifecycle/cold-heir.mjs +11 -6
  390. package/src/session-runtime/lifecycle/inheritance.mjs +16 -3
  391. package/src/session-runtime/lifecycle/session-catalog.mjs +12 -2
  392. package/src/session-runtime/lifecycle/teardown/runtime-stops.mjs +50 -4
  393. package/src/session-runtime/lifecycle/teardown.mjs +23 -5
  394. package/src/session-runtime/mcp-glue/server-catalog.mjs +55 -3
  395. package/src/session-runtime/mcp-oauth-login.mjs +5 -3
  396. package/src/session-runtime/mixy-heartbeat.mjs +153 -33
  397. package/src/session-runtime/model-route/set-route.mjs +11 -2
  398. package/src/session-runtime/notification-bus.mjs +31 -117
  399. package/src/session-runtime/notification-completion-queue.mjs +54 -0
  400. package/src/session-runtime/notification-listeners.mjs +76 -0
  401. package/src/session-runtime/owned-automation-run.mjs +2 -1
  402. package/src/session-runtime/prewarm/channel-start.mjs +3 -7
  403. package/src/session-runtime/provider-catalog-cache.mjs +82 -36
  404. package/src/session-runtime/provider-models/catalog-load.mjs +23 -6
  405. package/src/session-runtime/provider-models.mjs +4 -2
  406. package/src/session-runtime/resource-mcp-api.mjs +143 -224
  407. package/src/session-runtime/resource-mcp-connector-accounts.mjs +163 -0
  408. package/src/session-runtime/resource-mcp-oauth-api.mjs +217 -0
  409. package/src/session-runtime/resource-mcp-secrets.mjs +82 -0
  410. package/src/session-runtime/resource-mcp-servers.mjs +78 -0
  411. package/src/session-runtime/resource-mcp-store.mjs +49 -0
  412. package/src/session-runtime/resource-recall-api.mjs +6 -1
  413. package/src/session-runtime/routine-owner.mjs +40 -9
  414. package/src/session-runtime/runtime-feature-gates.mjs +5 -1
  415. package/src/session-runtime/services/agent-tool/close-flow.mjs +3 -0
  416. package/src/session-runtime/services/agent-tool/execute.mjs +14 -9
  417. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +20 -4
  418. package/src/session-runtime/services/agent-tool/model-select.mjs +48 -7
  419. package/src/session-runtime/services/agent-tool/render.mjs +71 -17
  420. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +13 -4
  421. package/src/session-runtime/services/agent-tool/tag-registry.mjs +1 -0
  422. package/src/session-runtime/services/agent-tool/tool-def.mjs +9 -9
  423. package/src/session-runtime/services/agent-tool/worker-index/awaiting-user-sync.mjs +51 -0
  424. package/src/session-runtime/services/agent-tool/worker-index/disposal.mjs +55 -0
  425. package/src/session-runtime/services/agent-tool/worker-index/released-store.mjs +123 -0
  426. package/src/session-runtime/services/agent-tool/worker-index/restart-settlement.mjs +112 -0
  427. package/src/session-runtime/services/agent-tool/worker-index/row-reads.mjs +36 -0
  428. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -0
  429. package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +17 -4
  430. package/src/session-runtime/services/agent-tool/worker-index/row-writes.mjs +58 -0
  431. package/src/session-runtime/services/agent-tool/worker-index.mjs +41 -241
  432. package/src/session-runtime/services/agent-tool/worker-rows.mjs +3 -0
  433. package/src/session-runtime/services/channel-admin.mjs +7 -13
  434. package/src/session-runtime/services/channel-session-router.mjs +4 -2
  435. package/src/session-runtime/services/provider-admin.mjs +2 -1
  436. package/src/session-runtime/services/seeds.mjs +0 -28
  437. package/src/session-runtime/session-compaction-sync.mjs +27 -0
  438. package/src/session-runtime/session-create/session-options.mjs +1 -0
  439. package/src/session-runtime/settings-builtin-tools-api.mjs +11 -0
  440. package/src/session-runtime/settings-compaction-api.mjs +2 -40
  441. package/src/session-runtime/settings-system-api.mjs +13 -1
  442. package/src/session-runtime/setup-tool/agent-settings-actions.mjs +65 -0
  443. package/src/session-runtime/setup-tool/executor.mjs +145 -22
  444. package/src/session-runtime/setup-tool/extended-actions.mjs +8 -3
  445. package/src/session-runtime/setup-tool/session-route-actions.mjs +152 -0
  446. package/src/session-runtime/setup-tool/settings-contract.mjs +4 -3
  447. package/src/session-runtime/setup-tool/tool-defs.mjs +64 -5
  448. package/src/session-runtime/tool-policy/agent-profile-fold.mjs +17 -13
  449. package/src/session-runtime/tool-policy/policy-stage.mjs +17 -2
  450. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
  451. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +5 -1
  452. package/src/session-runtime/workflow-agents-api/agent-route.mjs +15 -4
  453. package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -3
  454. package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +4 -0
  455. package/src/session-runtime/workspace/transitions.mjs +25 -0
  456. package/src/session-runtime/workspace/worktree-tool.mjs +26 -9
  457. package/src/standalone/channel-transport/client-registry/client-records.mjs +65 -0
  458. package/src/standalone/channel-transport/client-registry/client-registration.mjs +118 -0
  459. package/src/standalone/channel-transport/client-registry.mjs +24 -163
  460. package/src/standalone/channel-transport.mjs +1 -1
  461. package/src/standalone/daemon-channels-loader.mjs +10 -8
  462. package/src/standalone/daemon-session-control/list-page.mjs +72 -0
  463. package/src/standalone/daemon-session-control/request.mjs +51 -0
  464. package/src/standalone/daemon-session-control/room-host.mjs +57 -0
  465. package/src/standalone/daemon-session-control/route-control.mjs +48 -0
  466. package/src/standalone/daemon-session-control/send-turn.mjs +60 -0
  467. package/src/standalone/daemon-session-control/worktree-control.mjs +40 -0
  468. package/src/standalone/daemon-session-control.mjs +142 -190
  469. package/src/standalone/daemon.mjs +26 -3
  470. package/src/standalone/session-client.mjs +7 -3
  471. package/src/standalone/session-protocol.mjs +5 -0
  472. package/src/standalone/session-service/agent-tree/agent-child.mjs +1 -0
  473. package/src/standalone/session-service/agent-tree/agent-turns.mjs +16 -12
  474. package/src/standalone/session-service/quota-resume.mjs +3 -0
  475. package/src/standalone/session-service/retention/busy.mjs +65 -1
  476. package/src/standalone/session-service/session-calls/session-action.mjs +30 -2
  477. package/src/standalone/session-service/session-calls/session-views.mjs +8 -0
  478. package/src/standalone/session-tool-output.mjs +15 -7
  479. package/src/standalone/session-transport/client-registry/client-grace.mjs +12 -9
  480. package/src/tui/app/app-view/floating-panel.jsx +7 -4
  481. package/src/tui/app/model-options.mjs +3 -28
  482. package/src/tui/app/model-picker/route-selection/fast-parameters.mjs +1 -3
  483. package/src/tui/app/model-picker/route-selection.mjs +5 -6
  484. package/src/tui/app/provider-setup/oauth-actions.mjs +38 -20
  485. package/src/tui/app/provider-setup/provider-items.mjs +8 -2
  486. package/src/tui/app/provider-setup-picker.mjs +9 -5
  487. package/src/tui/components/QuestionPanel.jsx +13 -4
  488. package/src/tui/dist/index.mjs +133 -92
  489. package/src/tui/session/agent-job-feed/model-notification.mjs +3 -0
  490. package/src/tui/session/initial-state.mjs +3 -0
  491. package/src/tui/session/notice-surface.mjs +7 -1
  492. package/src/tui/session/queue-helpers.mjs +3 -42
  493. package/src/tui/session/session-api/routes-agent-definitions.mjs +14 -2
  494. package/src/tui/session/session-api/routes-write.mjs +5 -1
  495. package/src/tui/session/session-api/routes.mjs +1 -0
  496. package/src/tui/session/session-api/settings.mjs +17 -1
  497. package/src/tui/session/session-flow/auto-clear.mjs +4 -33
  498. package/src/tui/session/session-flow/drain-closed-queue.mjs +38 -0
  499. package/src/tui/session/session-flow/drain-enqueue.mjs +86 -0
  500. package/src/tui/session/session-flow/drain-retry.mjs +35 -0
  501. package/src/tui/session/session-flow/drain.mjs +118 -149
  502. package/src/tui/session/session-flow/queue/entry-shape.mjs +5 -0
  503. package/src/tui/session/session-flow/queue/take-entries.mjs +16 -2
  504. package/src/tui/session/tool-approval-delegation.mjs +64 -0
  505. package/src/tui/session/tool-approval-request.mjs +39 -0
  506. package/src/tui/session/tool-approval-settle.mjs +62 -0
  507. package/src/tui/session/tool-approval.mjs +69 -115
  508. package/src/tui/session/tui-steering-persist.mjs +8 -1
  509. package/src/tui/session/turn-stream.mjs +19 -2
  510. package/src/tui/session/turn.mjs +1 -0
  511. package/src/ui/statusline-agents.mjs +1 -3
  512. package/src/vendor/statusline/bin/statusline-route.mjs +1 -219
  513. package/src/vendor/statusline/src/gateway/route-meta.mjs +35 -13
  514. package/scripts/code-graph-description-contract.mjs +0 -188
  515. package/src/runtime/channels/lib/owned-runtime/ownership-refresh.mjs +0 -36
@@ -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 {
@@ -2,7 +2,14 @@
2
2
  // servers plus the servers registered plugins declare (resolved live, with
3
3
  // explicit owner metadata), per-server config reads, and the live/failed rollup.
4
4
  import { clean } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
5
- import { connectorOfServer, gmailAppPasswordAccount } from '../../runtime/shared/connector-catalog.mjs';
5
+ import {
6
+ connectorAccountEnabled,
7
+ connectorAccountServer,
8
+ connectorAccountsOf,
9
+ connectorOfServer,
10
+ gmailAppPasswordAccount,
11
+ } from '../../runtime/shared/connector-catalog.mjs';
12
+ import { connectorAccountHealth } from '../../runtime/connectors/connector-accounts-mcp.mjs';
6
13
  import { googleOAuthClient } from '../../runtime/shared/google-oauth-client.mjs';
7
14
  import { envFlag } from '../../runtime/shared/env.mjs';
8
15
  import { resolveMcpServerCatalog } from './plugin-servers.mjs';
@@ -112,6 +119,25 @@ export function createMcpServerCatalog({
112
119
  };
113
120
  }
114
121
 
122
+ /** One account of a multi-account connector server, as its row lists it:
123
+ * `{ name, email, enabled?, appPassword?, oauth?, ownClient?, error?, authRequired? }`
124
+ * (`enabled: false` only for an account that is switched off). */
125
+ function accountRow(accountCfg, enabled) {
126
+ const address = gmailAppPasswordAccount(accountCfg);
127
+ const oauth = address ? null : mcpClient.getMcpServerOAuthState?.(accountCfg);
128
+ const state = connectorAccountHealth(accountCfg.name);
129
+ const authRequired = Boolean(oauth && oauth.signedIn !== true) || state?.authRequired === true;
130
+ return {
131
+ name: accountCfg.name,
132
+ ...(enabled ? {} : { enabled: false }),
133
+ email: address || oauth?.account || state?.email || '',
134
+ ...(address ? { appPassword: { account: address } } : {}),
135
+ ...(oauth ? { oauth } : {}),
136
+ ...(typeof accountCfg.oauthClientId === 'string' && accountCfg.oauthClientId.trim() ? { ownClient: true } : {}),
137
+ ...(authRequired ? { authRequired: true } : state?.error ? { error: state.error } : {}),
138
+ };
139
+ }
140
+
115
141
  function statusRows(catalog) {
116
142
  const { servers: configured, sources, entries } = catalog;
117
143
  const connected = new Map((mcpClient.getMcpServerStatus?.(getMcpScopeId()) || []).map((row) => [row.name, row]));
@@ -126,7 +152,19 @@ export function createMcpServerCatalog({
126
152
  failures.get(name) || (dead ? { msg: live.error || live.lastError || 'MCP server is not alive' } : undefined);
127
153
  const entry = entries[name] || {};
128
154
  const loginPending = !(live && !dead) && mcpClient.isMcpLoginPending?.(name, getMcpScopeId()) === true;
129
- const status = mcpServerStatus(cfg, live && !dead, fail, loginPending);
155
+ const accounts = entry.owner
156
+ ? []
157
+ : connectorAccountsOf(cfg).map((account) =>
158
+ accountRow(connectorAccountServer(cfg, account), connectorAccountEnabled(account))
159
+ );
160
+ // A connector server none of whose accounts is signed in needs a sign-in, not a repair.
161
+ const accountsSignIn = accounts.length > 0 && accounts.every((account) => account.authRequired === true);
162
+ const status = mcpServerStatus(
163
+ cfg,
164
+ live && !dead,
165
+ fail && accountsSignIn ? { ...fail, authRequired: true } : fail,
166
+ loginPending
167
+ );
130
168
  // Needing an OAuth sign-in is a state, not an error.
131
169
  const authRequired = status === 'sign-in required';
132
170
  const row = {
@@ -156,6 +194,14 @@ export function createMcpServerCatalog({
156
194
  if (appPasswordAccount) row.appPassword = { account: appPasswordAccount };
157
195
  // A catalog connector running on the user's own Google client.
158
196
  if (connector && typeof cfg?.oauthClientId === 'string' && cfg.oauthClientId.trim()) row.ownClient = true;
197
+ if (accounts.length) {
198
+ // A multi-account connector: its accounts, each with its own state.
199
+ row.accounts = accounts;
200
+ if (accounts.some((account) => account.ownClient)) row.ownClient = true;
201
+ // One account reads like the one-account server it replaces.
202
+ if (accounts.length === 1 && accounts[0].oauth) row.oauth = accounts[0].oauth;
203
+ if (accounts.length === 1 && accounts[0].appPassword) row.appPassword = accounts[0].appPassword;
204
+ }
159
205
  if (entry.owner) {
160
206
  row.owner = entry.owner;
161
207
  row.pluginEnabled = entry.pluginEnabled;
@@ -175,7 +221,13 @@ export function createMcpServerCatalog({
175
221
 
176
222
  function mcpStatus() {
177
223
  if (envFlag('MIXDOG_DISABLE_MCP')) {
178
- return { servers: [], configuredCount: 0, connectedCount: 0, failedCount: 0, googleClient: Boolean(googleOAuthClient()) };
224
+ return {
225
+ servers: [],
226
+ configuredCount: 0,
227
+ connectedCount: 0,
228
+ failedCount: 0,
229
+ googleClient: Boolean(googleOAuthClient()),
230
+ };
179
231
  }
180
232
  const catalog = resolveCatalog();
181
233
  const servers = statusRows(catalog);
@@ -8,7 +8,7 @@ import { openInBrowser } from '../runtime/shared/open-url.mjs';
8
8
  // Settled flows stay readable long enough for a poller to observe the result.
9
9
  const SETTLED_FLOW_TTL_MS = 10 * 60_000;
10
10
  const NOT_COMPLETED = 'MCP OAuth sign-in was not completed';
11
- export const MCP_OAUTH_LOGIN_TIMED_OUT = 'MCP OAuth sign-in timed out';
11
+ const MCP_OAUTH_LOGIN_TIMED_OUT = 'MCP OAuth sign-in timed out';
12
12
  // A sign-in nobody finishes ends here, ahead of the OAuth layer's own 5-minute
13
13
  // abort, so its failure is reported as the timeout it is.
14
14
  const LOGIN_DEADLINE_MS = 5 * 60_000 - 10_000;
@@ -125,7 +125,9 @@ export function createMcpOAuthLogins({
125
125
  Promise.all([...flows.values()].filter((flow) => flow.owner === owner && flow.name === name).map(cancelFlow));
126
126
 
127
127
  async function begin(name, options = {}) {
128
- const { openBrowser: hostBrowser, mode = 'connect' } = options || {};
128
+ // `beside`: run any mode through `switchAccount` (a sign-in beside the
129
+ // stored one, for an account that is not a server of its own).
130
+ const { openBrowser: hostBrowser, mode = 'connect', beside = false } = options || {};
129
131
  if (hostBrowser !== undefined && typeof hostBrowser !== 'boolean') {
130
132
  throw new TypeError('openBrowser must be a boolean');
131
133
  }
@@ -178,7 +180,7 @@ export function createMcpOAuthLogins({
178
180
  flow.done = (async () => {
179
181
  try {
180
182
  const outcome =
181
- mode === 'switch'
183
+ mode === 'switch' || beside === true
182
184
  ? await switchAccount(name, launch, {
183
185
  set abort(fn) {
184
186
  flow.abort = fn;
@@ -1,6 +1,7 @@
1
1
  /**
2
- * Mixy's heartbeat: every 30 minutes, one check of what happened since the
3
- * last one, in three stages that each stop as early as they can.
2
+ * Mixy's heartbeat: at the user's chosen interval (heartbeatIntervalMinutes,
3
+ * 30 min / 1 h / 2 h / 4 h, default 1 h, read live at each tick), one check of
4
+ * what happened since the last one, in three stages that each stop as early as they can.
4
5
  * 1. Collect (no model): a bounded digest of what happened in the interval:
5
6
  * sessions the user talks in with new activity (Lead sessions, bot chats,
6
7
  * group rooms; never Mixy's own chat, the heartbeat's hidden sessions,
@@ -13,11 +14,13 @@
13
14
  * still fails (once when it switched itself off); a success closes it.
14
15
  * 2. Gate: a digest equal to the last check's, or an empty one, ends the
15
16
  * check. Otherwise the Maintainer model reviews each session and task
16
- * line in its own call over that session's messages since its last
17
- * review (tool calls left out), given that review's note (a few at a
18
- * time; a session with no new item keeps its note without a call; a line
19
- * it finds nothing in is dropped, its note is appended, a failed read or
20
- * review keeps the line as collected), then answers once on the reviewed
17
+ * line in its own call over the latest 12 of that session's messages
18
+ * since its last review (tool calls left out), given that review's note
19
+ * (a few at a time; the cursor then moves to the newest item and the line
20
+ * says how many older messages went unreviewed, or that its cursor was
21
+ * not found; a session with no new item keeps its note without a call; a
22
+ * line it finds nothing in is dropped unless it says that, its note is
23
+ * appended, a failed read or review keeps the line as collected), then answers once on the reviewed
21
24
  * digest with Mixy's last delivered message of the past day
22
25
  * (heartbeat-completion.mjs). Without one
23
26
  * (it is off by default), after a
@@ -40,11 +43,17 @@
40
43
  * for, or a wake whose reply was delivered); a failed collection (any
41
44
  * source), Maintainer call or wake leaves them for the next check, and a
42
45
  * routine-only wake after a failed Maintainer call commits just its routine
43
- * warnings. A due check waits while the user is active (a turn they sent in
44
- * the last 10 minutes, as this process saw it or the state file kept it;
45
- * routine and other runtime-made turns are no user activity); missed checks run as one; two never run at once; the first one after
46
- * a start is looked at after a minute. The setting `heartbeat: false` (setup
47
- * set_heartbeat_enabled) stops it.
46
+ * warnings. A due check waits while the user is active: the user is idle only
47
+ * after 30 minutes with no operation, an operation being a turn they sent or
48
+ * an input (keyboard, mouse buttons, wheel, mouse movement) in the desktop
49
+ * app's own windows, which the app reports (noteUserInput), as this process
50
+ * saw it or the state file kept it. Input elsewhere on the machine, and
51
+ * routine and other runtime-made turns, are no user activity; without the
52
+ * desktop app only sent turns count. Missed checks run as one; two never run
53
+ * at once; the first one after a start is looked at after a minute. The setting `heartbeat: false` (setup
54
+ * set_heartbeat_enabled) stops it, and so does a representative bot that does
55
+ * not exist (mixy-presence.mjs, as the desktop shows her): no check runs
56
+ * and the watermark is cleared, so the first check once Mixy exists arms anew.
48
57
  *
49
58
  * Proposals Mixy makes (session action suggest) live in the same state file
50
59
  * under a stable key (action:target): the same key is refreshed, a declined
@@ -63,6 +72,10 @@ import { roomIdOfSession, roomMemberOfSession } from '../runtime/agent/orchestra
63
72
  import { isRootLeadSession } from '../runtime/agent/orchestrator/session/store-summary-visibility.mjs';
64
73
  import { writeJsonAtomicSync } from '../runtime/shared/atomic-file.mjs';
65
74
  import { resolvePluginData } from '../runtime/shared/plugin-paths.mjs';
75
+ import {
76
+ DEFAULT_HEARTBEAT_INTERVAL_MINUTES,
77
+ normalizeHeartbeatIntervalMinutes,
78
+ } from '../runtime/shared/heartbeat-interval.mjs';
66
79
  import { ROUTINE_LIMITS } from '../runtime/shared/routine-limits.mjs';
67
80
  import {
68
81
  HEARTBEAT_QUIET,
@@ -74,10 +87,11 @@ import { activityOf, isWorking, quotedText, scanCatalog, sessionBotOf } from './
74
87
 
75
88
  const HOUR = 60 * 60_000;
76
89
  export const HEARTBEAT_LIMITS = Object.freeze({
77
- intervalMs: 30 * 60_000,
90
+ // The default interval; the setting decides (see `intervalMs` in createMixyHeartbeat).
91
+ intervalMs: DEFAULT_HEARTBEAT_INTERVAL_MINUTES * 60_000,
78
92
  firstTickMs: 60_000,
79
93
  tickMs: 5 * 60_000,
80
- idleMs: 10 * 60_000,
94
+ idleMs: 30 * 60_000,
81
95
  repeatMs: 6 * HOUR,
82
96
  suggestionTtlMs: 72 * HOUR,
83
97
  maxPending: 5,
@@ -90,14 +104,21 @@ const MAX_UNRELEASED = 20;
90
104
  const MAX_LINES = { routine: 10, task: 8, session: 8 };
91
105
  const TEXT_LIMIT = 120;
92
106
  const DIGEST_LIMIT = 4000;
93
- /** A review reads a session's latest TAIL_ITEMS transcript items and keeps
94
- * their last TAIL_MESSAGES user and assistant messages, each cut to
95
- * TAIL_TEXT_LIMIT; this many reviews run at once. */
107
+ /** A review reads a session's latest TAIL_ITEMS transcript items and takes
108
+ * the latest TAIL_MESSAGES user and assistant messages after its cursor, each
109
+ * cut to TAIL_TEXT_LIMIT, in one model call; this many reviews run at once. */
96
110
  const TAIL_ITEMS = 40;
97
111
  const TAIL_MESSAGES = 12;
98
112
  const TAIL_TEXT_LIMIT = 400;
99
113
  const REVIEWS_AT_ONCE = 4;
100
114
  const NOTE_LIMIT = 300;
115
+ /** What a digest line says of the messages a review left out (readSessionTail). */
116
+ function skippedNote({ skipped = 0, exact = true } = {}) {
117
+ if (skipped < 0) return 'older messages since the last check could not be found, so some were not reviewed';
118
+ if (skipped === 0) return exact ? '' : 'older messages since the last check may not have been reviewed';
119
+ const count = `${exact ? '' : 'at least '}${skipped}`;
120
+ return `${count} older message${skipped === 1 && exact ? '' : 's'} since the last check ${skipped === 1 && exact ? 'was' : 'were'} not reviewed`;
121
+ }
101
122
  /** Each reviewed session's cursor (its newest item) and note, kept this long
102
123
  * and at most this many. */
103
124
  const REVIEW_TTL_MS = 7 * 24 * HOUR;
@@ -130,6 +151,61 @@ export function noteUserTurn(at = Date.now(), { path = defaultStatePath() } = {}
130
151
  });
131
152
  }
132
153
 
154
+ let lastAppInputAt = 0;
155
+ /** Per state file: the app input time last written, when it was written and
156
+ * the trailing write waiting for the end of the window (if any). */
157
+ const appInputWrites = new Map();
158
+ const APP_INPUT_WRITE_MS = 60_000;
159
+ function writeAppInput(path, entry, current) {
160
+ if (entry.timer) clearTimeout(entry.timer);
161
+ entry.timer = null;
162
+ const value = lastAppInputAt;
163
+ updateState(path, (next) => {
164
+ next.lastAppInputAt = Math.max(next.lastAppInputAt, value);
165
+ });
166
+ entry.at = value;
167
+ entry.wroteAt = current;
168
+ }
169
+ /** The latest time the user operated the desktop app (keyboard, mouse buttons,
170
+ * wheel or mouse movement over its windows), as the app reports it (session
171
+ * action noteUserInput). Memory always takes the max (a time in the future
172
+ * counts as now). The state file (`lastAppInputAt`) is written only when the
173
+ * value advanced and at most once a minute, whoever calls. A note inside the
174
+ * window schedules one trailing write (an unref'd timer) of the newest value
175
+ * at the window's end, so after input stops the file is at most a minute
176
+ * behind: a crash loses at most the last minute of input, and a restart may
177
+ * see it up to a minute older than it was. The first note of a process
178
+ * writes at once. */
179
+ export function noteUserInput(
180
+ at = Date.now(),
181
+ { path = defaultStatePath(), now = Date.now, schedule = setTimeout } = {}
182
+ ) {
183
+ const current = now();
184
+ const time = Math.min(Number(at) || 0, current);
185
+ lastAppInputAt = Math.max(lastAppInputAt, time);
186
+ let entry = appInputWrites.get(path);
187
+ if (!entry) {
188
+ entry = { at: 0, wroteAt: 0, timer: null };
189
+ appInputWrites.set(path, entry);
190
+ }
191
+ if (lastAppInputAt <= entry.at) return;
192
+ const waited = current - entry.wroteAt;
193
+ if (entry.wroteAt && waited < APP_INPUT_WRITE_MS) {
194
+ if (entry.timer) return;
195
+ const held = entry;
196
+ held.timer = schedule(
197
+ () => {
198
+ held.timer = null;
199
+ if (lastAppInputAt > held.at) writeAppInput(path, held, now());
200
+ },
201
+ Math.min(APP_INPUT_WRITE_MS, APP_INPUT_WRITE_MS - waited)
202
+ );
203
+ held.timer?.unref?.();
204
+ return;
205
+ }
206
+ writeAppInput(path, entry, current);
207
+ }
208
+
133
209
  const defaultStatePath = () => join(resolvePluginData(), 'heartbeat.json');
134
210
 
135
211
  function readState(path) {
@@ -144,6 +220,8 @@ function readState(path) {
144
220
  lastAttemptAt: Number(value.lastAttemptAt) || 0,
145
221
  // The user's latest accepted turn (noteUserTurn), for the idle check.
146
222
  lastUserInputAt: Number(value.lastUserInputAt) || 0,
223
+ // The user's latest operation inside the desktop app (noteUserInput).
224
+ lastAppInputAt: Number(value.lastAppInputAt) || 0,
147
225
  // Approvals waiting at the last handled check.
148
226
  approvals: Number.isInteger(value.approvals) && value.approvals > 0 ? value.approvals : 0,
149
227
  lastHash: typeof value.lastHash === 'string' ? value.lastHash : '',
@@ -418,24 +496,33 @@ export function isQuietReply(reply) {
418
496
  }
419
497
 
420
498
  /** Session `id`'s latest conversation for a review, oldest first:
421
- * `{ rows, head }`, a line per user or assistant message (tool calls left
422
- * out; at most TAIL_MESSAGES, the newest), only those after item `after`
423
- * when the window holds it, and `head` the id of its newest item ('' when
424
- * the items carry none). Rejects when the session cannot be read. */
499
+ * `{ rows, head, skipped, exact }`. One read of the latest TAIL_ITEMS items
500
+ * (never more, however many tool calls they hold); `rows` is a line per
501
+ * user or assistant message (tool calls left out), the newest TAIL_MESSAGES
502
+ * after item `after` in that page. `skipped` is the number of older messages
503
+ * in the page left unreviewed: exact when the cursor is in the page, else a
504
+ * lower bound (`exact` false) when older items lie beyond the page; a
505
+ * supplied cursor absent from a page with nothing older is `-1` (it was
506
+ * compacted away: the gap is unknown). `head` is the id of the newest item
507
+ * ('' when the items carry none). Rejects when the session cannot be read. */
425
508
  export async function readSessionTail(service, id, { after = '' } = {}) {
426
509
  if (typeof service?.readSession !== 'function') throw new Error('the session service is not running');
427
510
  const { range } = await service.readSession({ sessionId: id, transcriptRange: { offset: 0, limit: TAIL_ITEMS } });
428
511
  const items = range?.items || [];
512
+ const hasOlder = range?.hasOlder === true;
429
513
  const ids = items.map((item) => (item?.id == null ? '' : String(item.id)));
430
514
  const head = ids.findLast(Boolean) || '';
431
515
  const seen = after ? ids.lastIndexOf(after) : -1;
432
- const rows = [];
516
+ const all = [];
433
517
  for (const item of items.slice(seen + 1)) {
434
518
  if (item?.kind !== 'user' && item?.kind !== 'assistant') continue;
435
519
  const text = quotedText(item.text, TAIL_TEXT_LIMIT);
436
- if (text) rows.push(`${item.kind}: "${text}"`);
520
+ if (text) all.push(`${item.kind}: "${text}"`);
437
521
  }
438
- return { rows: rows.slice(-TAIL_MESSAGES), head };
522
+ const rows = all.slice(-TAIL_MESSAGES);
523
+ const skipped = all.length - rows.length;
524
+ if (after && seen < 0 && !hasOlder) return { rows, head, skipped: -1, exact: false };
525
+ return { rows, head, skipped, exact: after ? seen >= 0 : !hasOlder };
439
526
  }
440
527
 
441
528
  /** Run `work` on every row, at most `limit` at once. */
@@ -549,7 +636,16 @@ export function createMixyHeartbeat({
549
636
  path = defaultStatePath(),
550
637
  isEnabled = async () =>
551
638
  (await import('../runtime/agent/orchestrator/config.mjs')).loadConfig({ secrets: false }).heartbeat !== false,
639
+ // Whether the representative bot exists (mixy-presence.mjs, the rule the
640
+ // desktop and her routines follow): without it no check runs at all.
641
+ mixyExists = async () => (await import('./routine-owner.mjs')).readMixyExists(),
642
+ // Milliseconds between checks, read at every tick so a changed setting applies at once.
643
+ intervalMs = async () =>
644
+ normalizeHeartbeatIntervalMinutes(
645
+ (await import('../runtime/agent/orchestrator/config.mjs')).loadConfig({ secrets: false }).heartbeatIntervalMinutes
646
+ ) * 60_000,
552
647
  userTurnAt = () => lastUserTurnAt,
648
+ appInputAt = () => lastAppInputAt,
553
649
  getSessionService = () => null,
554
650
  log = defaultLog,
555
651
  collect = (input) => collectHeartbeatFacts({ ...input, getSessionService }),
@@ -561,7 +657,7 @@ export function createMixyHeartbeat({
561
657
  (await import('../runtime/agent/orchestrator/agent-runtime/heartbeat-completion.mjs')).reviewHeartbeatSession(
562
658
  input
563
659
  ),
564
- readTail = (id) => readSessionTail(getSessionService(), id),
660
+ readTail = (id, options) => readSessionTail(getSessionService(), id, options),
565
661
  release = closeHiddenSession,
566
662
  // `hooks`: how the hidden session is closed, and where a failed close goes.
567
663
  wake = (input, hooks) => wakeMixy(input, { getSessionService, log, ...hooks }),
@@ -642,9 +738,15 @@ export function createMixyHeartbeat({
642
738
  const lines = [...digest.lines];
643
739
  const dropped = new Set();
644
740
  const cursors = {};
645
- const apply = (index, note) => {
646
- if (!note) dropped.add(index);
647
- else lines[index] = `${lines[index]}; reviewed: "${quotedText(note, NOTE_LIMIT)}"`;
741
+ // A line whose older messages went unreviewed is never dropped as
742
+ // "nothing": it says so, as the overflow lines count what they leave out.
743
+ const apply = (index, note, skipped = '') => {
744
+ if (!note && !skipped) {
745
+ dropped.add(index);
746
+ return;
747
+ }
748
+ const noted = note ? `${lines[index]}; reviewed: "${quotedText(note, NOTE_LIMIT)}"` : lines[index];
749
+ lines[index] = skipped ? `${noted}; ${skipped}` : noted;
648
750
  };
649
751
  await eachLimited(digest.subjects, REVIEWS_AT_ONCE, async ({ index, id }) => {
650
752
  const prior = state.reviews[id];
@@ -656,6 +758,8 @@ export function createMixyHeartbeat({
656
758
  apply(index, prior.note);
657
759
  return;
658
760
  }
761
+ // The latest messages since the cursor, in one call; the cursor then
762
+ // moves to the newest item, and what lay before them is said so.
659
763
  result = await review({ line: digest.lines[index], transcript: tail.rows, previous: prior?.note || '' });
660
764
  } catch (error) {
661
765
  log(`review of ${id} failed: ${errorText(error)}`);
@@ -663,7 +767,7 @@ export function createMixyHeartbeat({
663
767
  }
664
768
  if (result?.action === 'nothing' || result?.action === 'note') {
665
769
  const note = result.action === 'note' ? result.text : '';
666
- apply(index, note);
770
+ apply(index, note, skippedNote(tail));
667
771
  if (tail.head) cursors[id] = { head: tail.head, note };
668
772
  } else if (result?.action !== 'unmanaged') log(`review of ${id}: unusable reply`);
669
773
  });
@@ -759,6 +863,16 @@ export function createMixyHeartbeat({
759
863
  try {
760
864
  await retryReleases();
761
865
  if (!(await isEnabled())) return 'off';
866
+ // No Maintainer call, hidden turn or queued report for a Mixy the user
867
+ // never created (issue #243); the first check after she is made arms anew.
868
+ if (!(await mixyExists())) {
869
+ if (readState(path).lastBeatAt) {
870
+ updateState(path, (next) => {
871
+ next.lastBeatAt = 0;
872
+ });
873
+ }
874
+ return 'no-mixy';
875
+ }
762
876
  const at = now();
763
877
  const state = readState(path);
764
878
  if (!state.lastBeatAt) {
@@ -769,10 +883,16 @@ export function createMixyHeartbeat({
769
883
  }
770
884
  // Checks are due by the last one begun; a check left unhandled keeps
771
885
  // its watermark, so the next one collects its changes again.
772
- if (at - Math.max(state.lastBeatAt, state.lastAttemptAt) < limits.intervalMs) return 'waiting';
773
- // The user's latest turn: one this process saw, or the state file's
774
- // record of it (it survives a crash or restart).
775
- const userAt = Math.max(Number(userTurnAt()) || 0, state.lastUserInputAt);
886
+ if (at - Math.max(state.lastBeatAt, state.lastAttemptAt) < (await intervalMs())) return 'waiting';
887
+ // The user's latest operation: a turn they sent or an input in the
888
+ // desktop app, as this process saw it or the state file kept it (it
889
+ // survives a crash or restart).
890
+ const userAt = Math.max(
891
+ Number(userTurnAt()) || 0,
892
+ Number(appInputAt()) || 0,
893
+ state.lastUserInputAt,
894
+ state.lastAppInputAt
895
+ );
776
896
  if (at - userAt < limits.idleMs) return 'deferred';
777
897
  updateState(path, (next) => {
778
898
  next.lastAttemptAt = at;
@@ -217,11 +217,20 @@ function switchSessionInPlace(deps, session) {
217
217
 
218
218
  /** The Lead's fallback models after this selection, checked like an agent's
219
219
  * chain (leaf routes, distinct providers): an explicit list replaces the
220
- * stored one ([] clears it), a selection without one keeps it. */
220
+ * stored one ([] clears it), a selection without one keeps it as stored — a
221
+ * fallback on the new main model's provider stays saved and is inactive
222
+ * (executionFallbacks). */
221
223
  async function leadFallbackChain(deps, next, primary) {
222
224
  const storedPreset = findPreset(deps.getConfig(), workflowPresetId('lead'));
223
- const requested = hasOwn(next || {}, 'fallbacks') ? next.fallbacks : storedPreset?.fallbacks;
225
+ const explicit = hasOwn(next || {}, 'fallbacks');
226
+ const requested = explicit ? next.fallbacks : storedPreset?.fallbacks;
224
227
  if (!Array.isArray(requested) || !requested.length) return [];
228
+ if (!explicit) {
229
+ return (
230
+ normalizeWorkflowRoute({ provider: primary.provider, model: primary.model, fallbacks: requested })?.fallbacks ||
231
+ []
232
+ );
233
+ }
225
234
  const chain = routeChain({ provider: primary.provider, model: primary.model, fallbacks: requested });
226
235
  validateRouteChainProviders(chain);
227
236
  const storedChain = routeChain(storedPreset || {});