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
@@ -7,11 +7,16 @@ import { builtinFeatureActive } from '../../runtime/agent/orchestrator/runtime-c
7
7
  import { ORCHESTRATION_MODES } from '../../runtime/shared/orchestration.mjs';
8
8
  import { AUTO_EFFORT_BASES } from '../../runtime/agent/orchestrator/providers/auto-effort.mjs';
9
9
  import { INSTRUCTION_FILES_MODES } from '../../runtime/shared/instruction-files-mode.mjs';
10
+ import {
11
+ normalizeHeartbeatIntervalMinutes,
12
+ requireHeartbeatIntervalMinutes,
13
+ } from '../../runtime/shared/heartbeat-interval.mjs';
10
14
  import { MEDIA_KINDS } from '../../runtime/media/tool-defs.mjs';
11
15
  import { SETUP_DESKTOP_DOMAINS, SETUP_HANDOFFS } from './settings-contract.mjs';
12
16
  import { executeExtendedSetupAction, publicAutomation, publicHookRule, validateMcpInput } from './extended-actions.mjs';
13
17
  import {
14
18
  assertAgentEditorAccess,
19
+ assertHeartbeatSetupAction,
15
20
  assertInteractiveCaller,
16
21
  assertOwnAgentSettings,
17
22
  scheduleCallerScope,
@@ -29,8 +34,32 @@ import { schemaValueError } from '../../runtime/shared/schema-value-error.mjs';
29
34
  import { clean } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
30
35
  import { CONNECTOR_IDS, connectorById } from '../../runtime/shared/connector-catalog.mjs';
31
36
  import profileConfig from '../../runtime/shared/profile-config.cjs';
37
+ import { SESSION_ROUTE_HANDLERS, routePositionsStatus } from './session-route-actions.mjs';
38
+ import { AGENT_SETTINGS_HANDLERS } from './agent-settings-actions.mjs';
39
+ import {
40
+ routeFallbackPosition,
41
+ sessionChainSource,
42
+ sessionRoutePrimary,
43
+ } from '../../runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs';
44
+ import { sameRouteModel } from '../../runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs';
45
+
46
+ const AGENT_DEFINITION_ACTIONS = new Set([
47
+ 'create_definition',
48
+ 'save_definition',
49
+ 'delete_definition',
50
+ 'restore_definition',
51
+ ]);
52
+ // Actions on one agent's own settings: another agent's thread changes only its own.
53
+ const OWN_AGENT_ACTIONS = new Set([
54
+ 'set_agent_route',
55
+ 'set_agent_permission',
56
+ 'set_agent_connector',
57
+ 'set_agent_connector_tool',
58
+ ]);
32
59
 
33
- const AGENT_DEFINITION_ACTIONS = new Set(['create_definition', 'save_definition', 'delete_definition']);
60
+ // How a fallback chain edit reaches sessions that already run (route-fallback-chain.mjs).
61
+ const CHAIN_APPLIES =
62
+ 'a changed fallback chain reaches running sessions on that route from their next fallback hop ([] stops further hops at once; the turn ends with the provider error)';
34
63
 
35
64
  const PROFILE_VALUE_IDS = Object.freeze({
36
65
  language: profileConfig.PROFILE_LANGUAGES.map((entry) => entry.id),
@@ -38,12 +67,32 @@ const PROFILE_VALUE_IDS = Object.freeze({
38
67
  });
39
68
 
40
69
  const ACTION_APPLIES_TO = {
41
- set_agent_route:
42
- "the bot's chats from their next turn (a running turn finishes on the old model); an agent switched on or off reaches open conversations from their next turn as an agent list reminder",
70
+ set_route: `new sessions (an established conversation keeps its model); ${CHAIN_APPLIES}: Lead conversations and agents that inherit Main`,
71
+ set_agent_route: `the bot's chats from their next turn (a running turn finishes on the old model); ${CHAIN_APPLIES}: its chats and sub-agents; an agent switched on or off reaches open conversations from their next turn as an agent list reminder`,
72
+ reset_route_fallback:
73
+ 'each listed session from its next turn (a running turn finishes on its fallback model): it returns to its primary and its chain starts over',
74
+ continue_in_model: 'that session now; its stopped turn is not resent',
75
+ schedule_quota_resume: "that session's stopped turn, resent at the provider's reset time",
76
+ cancel_quota_resume: 'that session: its pending resume is dropped; the turn stays stopped',
77
+ set_cache_warming_enabled: 'the next prompt-cache warm of every session; no restart required',
78
+ set_agent_permission: "that agent's next action of the kind, in every session",
79
+ set_agent_connector: "that agent's next connector tool call, in every session",
80
+ set_agent_connector_tool: "that agent's next call of the tool, in every session",
81
+ set_project_connector: 'the next connector tool call of sessions in that Project',
82
+ restore_definition: 'now: the bot and its chat return; its routines stay off',
83
+ set_bot_order: 'the Bots page and the Sessions bot section on every device',
43
84
  set_orchestration_mode:
44
85
  "new sessions and an empty current one (the default mode); a conversation keeps its own mode until it is switched with the composer's mode selector",
45
86
  set_recap_enabled: 'background Memory cycles; no restart required',
46
87
  set_auto_update: 'subsequent automatic update checks',
88
+ set_ai_session_titles: 'titles generated from now on; existing titles are unchanged',
89
+ set_quota_auto_resume: 'subsequent usage-limit stops; turning it off cancels pending resumes',
90
+ set_model_preferences: 'the /model picker and model management lists',
91
+ set_heartbeat_enabled: "Mixy's next heartbeat check; no restart required",
92
+ set_heartbeat_interval:
93
+ "Mixy's heartbeat from the next tick: the next check is due the new interval after the last one; no restart required",
94
+ set_reactions_enabled:
95
+ "bots' chats; while off a reaction is refused at once and new chats are not offered the tool; reactions already left stay visible",
47
96
  set_worktree_settings: 'new tasks and worktrees created afterwards; existing worktrees are unchanged',
48
97
  set_system_shell: 'subsequent shell calls',
49
98
  save_automation: 'the automation worker; scheduled runs follow the saved timing',
@@ -54,7 +103,7 @@ const ACTION_APPLIES_TO = {
54
103
  add_hook_rule: 'subsequent tool calls; no restart required',
55
104
  set_hook_rule_enabled: 'subsequent tool calls; no restart required',
56
105
  delete_hook_rule: 'subsequent tool calls; no restart required',
57
- set_media_default: 'media generations that omit lane/model, and the Studio selection',
106
+ set_media_default: 'media generations that name no lane/model; lane "" returns the kind to Auto',
58
107
  };
59
108
 
60
109
  const OPEN_TARGET_HINTS = Object.freeze({
@@ -65,11 +114,11 @@ const OPEN_TARGET_HINTS = Object.freeze({
65
114
  workflow: 'Desktop: Workflows · TUI: /workflow',
66
115
  agents: 'Desktop: Workflows → Agents · TUI: /agents',
67
116
  outputstyle: 'Desktop: Settings → Output style · TUI: /OutputStyle',
68
- theme: 'Desktop: Settings → General → Theme · TUI: /theme',
117
+ theme: 'Desktop: Settings → Theme · TUI: /theme',
69
118
  profile: 'Desktop: Settings → General → Profile · TUI: /profile',
70
119
  autoclear: 'Desktop: Settings → Context · TUI: /autoclear',
71
120
  memory:
72
- 'Desktop: Extensions → Plugin → Built-in (Memory); core memories under Projects → project → Memories · TUI: /memory',
121
+ 'Desktop: Extensions tab → Built-in (Memory); core memories under Projects → project → Memories · TUI: /memory',
73
122
  mcp: 'Desktop: Extensions → Connectors tab · TUI: /mcp',
74
123
  skills: 'Desktop: Skills (activity rail) · TUI: /skills',
75
124
  plugins:
@@ -79,7 +128,7 @@ const OPEN_TARGET_HINTS = Object.freeze({
79
128
  doctor: 'Desktop: Settings → System → Doctor, or /doctor in the composer · TUI: /doctor',
80
129
  context: 'Desktop: /context in the composer · TUI: /context',
81
130
  developer: 'Desktop: Settings → Developer options · TUI: /setting → Developer',
82
- voice: 'Desktop: Extensions → Plugin → Built-in (Voice) · TUI: /setting → Voice',
131
+ voice: 'Desktop: Extensions tab → Built-in (Voice) · TUI: /setting → Voice',
83
132
  connection: 'Desktop only: Settings → Connection',
84
133
  });
85
134
 
@@ -117,6 +166,31 @@ function routeInput(source) {
117
166
  return next;
118
167
  }
119
168
 
169
+ /** A fallback chain edit's receipt: whether the calling conversation now
170
+ * follows the saved chain, and the position it reads it from. `owner` is
171
+ * whose route was edited: `{ kind: 'lead' }` (set_route) or `{ kind: 'bot',
172
+ * agent }` (set_agent_route); only a caller whose chain comes from that
173
+ * route's settings follows it. */
174
+ function chainReceipt(route, { caller, getConfig }, owner) {
175
+ if (!Object.hasOwn(route, 'fallbacks') || !caller?.agent) return {};
176
+ const config = getConfig();
177
+ const position = routeFallbackPosition(caller, config);
178
+ // Settings-sourced and still on that route's primary: a conversation left on
179
+ // a previous model follows no chain, whatever the saved one is.
180
+ const source = sessionChainSource(caller, config);
181
+ const followsEdited =
182
+ position.chainFrom === 'settings' &&
183
+ sameRouteModel(source, sessionRoutePrimary(caller)) &&
184
+ position.kind === owner.kind &&
185
+ (owner.kind !== 'bot' || position.agent === owner.agent);
186
+ const saved = route.fallbacks.map((entry) => `${entry.provider}/${entry.model}`);
187
+ const followed = position.fallbacks.map((entry) => `${entry.provider}/${entry.model}`);
188
+ return {
189
+ appliedToCurrentSession: followsEdited && saved.join('\n') === followed.join('\n'),
190
+ currentSession: position,
191
+ };
192
+ }
193
+
120
194
  function apiKeySource(row) {
121
195
  if (row.env) return `env:${row.envName}`;
122
196
  return row.stored ? 'keychain' : 'none';
@@ -225,6 +299,18 @@ function mcpRows(status) {
225
299
  error: server.error || null,
226
300
  ...(server.warnings?.length ? { warnings: server.warnings } : {}),
227
301
  scope: Array.isArray(server.scope) ? server.scope : null,
302
+ ...(server.connector ? { connector: server.connector } : {}),
303
+ // A multi-account connector's accounts: remove_mcp_server / sign-in take an account's name.
304
+ ...(Array.isArray(server.accounts)
305
+ ? {
306
+ accounts: server.accounts.map((account) => ({
307
+ name: account.name,
308
+ email: account.email || '',
309
+ ...(account.authRequired ? { authRequired: true } : {}),
310
+ ...(account.error ? { error: account.error } : {}),
311
+ })),
312
+ }
313
+ : {}),
228
314
  })),
229
315
  ...(status?.warnings?.length ? { warnings: status.warnings } : {}),
230
316
  ...(status?.declarationErrors?.length ? { declarationErrors: status.declarationErrors } : {}),
@@ -277,16 +363,26 @@ async function featureStatus(rt, getConfig) {
277
363
  browser: { active: builtinFeatureActive(config, 'browser') },
278
364
  computer: { active: builtinFeatureActive(config, 'computer') },
279
365
  terminal: { active: builtinFeatureActive(config, 'terminal') },
366
+ heartbeat: {
367
+ enabled: config?.heartbeat !== false,
368
+ intervalMinutes: normalizeHeartbeatIntervalMinutes(config?.heartbeatIntervalMinutes),
369
+ },
370
+ reactions: { enabled: config?.reactions !== false },
280
371
  voice: rt.getVoiceStatus ? voiceFeatureStatus(await rt.getVoiceStatus()) : null,
281
372
  };
282
373
  }
283
374
 
284
375
  async function mediaDefaults(rt) {
285
- const rows = await Promise.all(MEDIA_KINDS.map((kind) => rt.getMediaDefault(kind)));
376
+ const [pins, ...rows] = await Promise.all([
377
+ rt.getMediaPins(),
378
+ ...MEDIA_KINDS.map((kind) => rt.getMediaDefault(kind)),
379
+ ]);
380
+ const fieldsOf = (row, fields) => (row ? Object.fromEntries(fields.map((field) => [field, row[field]])) : null);
286
381
  return {
287
- defaults: Object.fromEntries(
288
- MEDIA_KINDS.map((kind, i) => [kind, rows[i] ? { lane: rows[i].lane, model: rows[i].model } : null])
289
- ),
382
+ // The default generator (Settings → Built-in → Media generation); null = Auto.
383
+ pinned: Object.fromEntries(MEDIA_KINDS.map((kind) => [kind, fieldsOf(pins[kind], ['lane', 'model', 'account'])])),
384
+ // The last generator used; applies only to a kind that is on Auto.
385
+ remembered: Object.fromEntries(MEDIA_KINDS.map((kind, i) => [kind, fieldsOf(rows[i], ['lane', 'model'])])),
290
386
  };
291
387
  }
292
388
 
@@ -324,11 +420,24 @@ const SETUP_STATUS_READERS = {
324
420
  features: await featureStatus(rt, getConfig),
325
421
  onboarding: rt.getOnboardingStatus?.() || null,
326
422
  }),
327
- model: (rt) => ({ route: mainRoute(rt), effortOptions: rt.effortOptions || [] }),
328
- agents: (rt) => ({
423
+ model: (rt, { getConfig, caller }) => ({
424
+ route: {
425
+ ...mainRoute(rt),
426
+ autoEffort: rt.autoEffort !== false,
427
+ // The Lead preset (workflow-lead) holds the Lead route's fallback chain, as the Workflows page reads it.
428
+ fallbacks: (rt.listPresets?.() || []).find((preset) => preset?.id === 'workflow-lead')?.fallbacks || [],
429
+ },
430
+ effortOptions: rt.effortOptions || [],
431
+ // Where this conversation and the other Lead conversations are on their chain.
432
+ ...routePositionsStatus({ caller, config: getConfig(), keep: (row) => row.kind === 'lead' }),
433
+ }),
434
+ agents: (rt, { getConfig, caller }) => ({
329
435
  agents: rt.listAgents?.() || [],
330
436
  orchestrationMode: rt.getOrchestrationMode(),
331
437
  orchestrationModes: ORCHESTRATION_MODES,
438
+ ...(rt.getBotOrder ? { botOrder: rt.getBotOrder().order } : {}),
439
+ // Bot chats and sub-agents on their fallback chains.
440
+ sessions: routePositionsStatus({ caller, config: getConfig(), keep: (row) => row.kind !== 'lead' }).sessions,
332
441
  }),
333
442
  workflow: (rt) => ({ workflows: rt.listWorkflows?.() || [] }),
334
443
  websearch: (rt) => ({
@@ -347,19 +456,27 @@ const SETUP_STATUS_READERS = {
347
456
  };
348
457
  },
349
458
  autoclear: (rt) => rt.getAutoClear?.() || {},
350
- compaction: (rt) => rt.getCompactionSettings?.() || {},
459
+ compaction: (rt) => ({
460
+ ...(rt.getCompactionSettings?.() || {}),
461
+ ...(rt.getSessionTitleSettings?.() || {}),
462
+ ...(rt.getCacheWarmingSettings ? { promptCacheWarming: rt.getCacheWarmingSettings().enabled } : {}),
463
+ }),
351
464
  memory: (rt) => ({ ...(rt.getToolModuleSettings?.()?.memory || {}), recap: rt.getRecapSettings?.() || null }),
352
465
  'local-provider': (rt) => rt.getToolModuleSettings().localProvider,
353
466
  features: (rt, { getConfig }) => featureStatus(rt, getConfig),
354
467
  shell: (rt) => rt.getSystemShell?.() || {},
355
468
  providers: async (rt, { detail }) => {
356
469
  const rows = publicProviderRows(await rt.getProviderSetup?.({}));
470
+ const quotaResume = rt.getQuotaAutoResume ? { quotaAutoResume: rt.getQuotaAutoResume() } : {};
357
471
  // Client mode: the desktop owns accounts, so the server roster is never read.
358
472
  if (rows.managedByClient) {
359
- if (detail) return rows;
360
- return { managedByClient: true, unavailable: rows.unavailable, ...configuredProviderRows(rows) };
473
+ if (detail) return { ...rows, ...quotaResume };
474
+ return { managedByClient: true, unavailable: rows.unavailable, ...configuredProviderRows(rows), ...quotaResume };
361
475
  }
362
- if (!detail) return configuredProviderRows(rows, (id) => rt.getProviderAccounts(id).accounts.length);
476
+ if (!detail) {
477
+ return { ...configuredProviderRows(rows, (id) => rt.getProviderAccounts(id).accounts.length), ...quotaResume };
478
+ }
479
+ Object.assign(rows, quotaResume);
363
480
  rows.oauth = rows.oauth.map((row) =>
364
481
  row.authenticated ? { ...row, accounts: publicAccounts(rt.getProviderAccounts(row.id)) } : row
365
482
  );
@@ -473,22 +590,28 @@ const SETUP_INSTALLABLE_FEATURES = [
473
590
  // Action handlers: (rt, args, { requestDesktop, readStatus, openSurface }).
474
591
  // `rt` is null for `status` and `open`, which never touch the facade.
475
592
  const SETUP_ACTION_HANDLERS = {
476
- status: async (_rt, args, { readStatus, requestDesktop }) => {
593
+ status: async (_rt, args, { readStatus, requestDesktop, caller }) => {
477
594
  const domain = clean(args.domain) || 'summary';
478
595
  requireEnum(domain, SETUP_STATUS_DOMAINS, 'domain');
479
- return { domain, ...(await readStatus(domain, requestDesktop, args.detail === true)) };
596
+ return { domain, ...(await readStatus(domain, requestDesktop, args.detail === true, caller)) };
480
597
  },
481
598
  open: (_rt, args, { openSurface }) => openSurface(args.target),
482
- set_route: async (rt, args) => {
599
+ set_route: async (rt, args, context) => {
483
600
  const route = routeInput(args.route);
484
601
  const next = await rt.setRoute(route, { keepMainModel: false });
485
- return { route: next, appliesTo: 'next session (a conversation keeps its frozen route)' };
602
+ return { route: next, ...chainReceipt(args.route, context, { kind: 'lead' }) };
486
603
  },
487
- set_agent_route: async (rt, args) => {
604
+ set_agent_route: async (rt, args, context) => {
488
605
  const agent = requireText(args.agent, 'agent');
489
606
  const route = routeInput(args.route);
490
- return { agent, route: await rt.setAgentRoute(agent, route) };
607
+ return {
608
+ agent,
609
+ route: await rt.setAgentRoute(agent, route),
610
+ ...chainReceipt(args.route, context, { kind: 'bot', agent }),
611
+ };
491
612
  },
613
+ ...SESSION_ROUTE_HANDLERS,
614
+ ...AGENT_SETTINGS_HANDLERS,
492
615
  set_web_search_route: async (rt, args) => ({ route: await rt.setWebSearchRoute(routeInput(args.route)) }),
493
616
  set_workflow: (rt, args) => rt.setWorkflow(requireText(args.workflow, 'workflow')),
494
617
  set_output_style: async (rt, args) => {
@@ -597,6 +720,20 @@ const SETUP_ACTION_HANDLERS = {
597
720
  rt.deleteLocalProviderModel(requireText(args.confirmationToken, 'confirmationToken')),
598
721
  set_system_shell: (rt, args) => rt.setSystemShell({ command: clean(args.command) }),
599
722
  set_auto_update: (rt, args) => rt.setAutoUpdate(requireBoolean(args.enabled)),
723
+ set_ai_session_titles: (rt, args) => rt.setAiSessionTitles(requireBoolean(args.enabled)),
724
+ set_quota_auto_resume: async (rt, args) => ({
725
+ quotaAutoResume: await rt.setQuotaAutoResume(requireBoolean(args.enabled)),
726
+ }),
727
+ set_model_preferences: async (rt, args) => {
728
+ const change = { model: requireText(args.model, 'model') };
729
+ for (const key of ['hidden', 'favorite'])
730
+ if (Object.hasOwn(args, key)) change[key] = requireBoolean(args[key], key);
731
+ if (Object.keys(change).length === 1) throw new Error('hidden or favorite is required');
732
+ return rt.setModelPreferences(requireText(args.provider, 'provider'), [change]);
733
+ },
734
+ set_heartbeat_enabled: (rt, args) => rt.setHeartbeatEnabled(requireBoolean(args.enabled)),
735
+ set_heartbeat_interval: (rt, args) => rt.setHeartbeatInterval(requireHeartbeatIntervalMinutes(args.minutes)),
736
+ set_reactions_enabled: (rt, args) => rt.setReactionsEnabled(requireBoolean(args.enabled)),
600
737
  set_worktree_settings: (rt, args) => rt.setWorktreeSettings(args.worktree),
601
738
  forget_provider_auth: async (rt, args) => {
602
739
  const name = requireText(args.name, 'name');
@@ -666,7 +803,7 @@ const SETUP_ACTION_HANDLERS = {
666
803
  // as Extensions → 커넥터. Nothing is connected here.
667
804
  connect_connector: async (rt, args) => {
668
805
  const entry = connectorById(requireText(args.connector, 'connector'));
669
- if (!entry || entry.ownKey) throw new Error(`connector must be one of ${CONNECTOR_IDS.join(', ')}`);
806
+ if (!entry) throw new Error(`connector must be one of ${CONNECTOR_IDS.join(', ')}`);
670
807
  const server = (rt.mcpStatus?.()?.servers || []).find((row) => row.connector === entry.id) || null;
671
808
  const connected = server?.connected === true;
672
809
  return {
@@ -703,6 +840,8 @@ const READ_ONLY_SETUP_ACTIONS = new Set([
703
840
  'search_local_models',
704
841
  'inspect_hf_model',
705
842
  'local_model_details',
843
+ 'read_agent_settings',
844
+ 'read_project_connectors',
706
845
  ]);
707
846
  // Actions that start or steer work rather than saving a setting.
708
847
  const BACKGROUND_SETUP_ACTIONS = new Set([
@@ -711,6 +850,10 @@ const BACKGROUND_SETUP_ACTIONS = new Set([
711
850
  'cancel_local_installation',
712
851
  'reconnect_mcp',
713
852
  'run_automation',
853
+ 'reset_route_fallback',
854
+ 'continue_in_model',
855
+ 'schedule_quota_resume',
856
+ 'cancel_quota_resume',
714
857
  ]);
715
858
 
716
859
  function setupMutationReceipt(action, result) {
@@ -735,6 +878,7 @@ export function createSetupToolExecutor({
735
878
  getSessionId,
736
879
  getCallerSession,
737
880
  flushSettings,
881
+ executeSessionControl = null,
738
882
  }) {
739
883
  const desktop = createSetupUiRequests({ notifySessionUi, getSessionId });
740
884
  const api = () => {
@@ -743,11 +887,11 @@ export function createSetupToolExecutor({
743
887
  return facade;
744
888
  };
745
889
 
746
- async function readStatus(domain, requestDesktop, detail) {
890
+ async function readStatus(domain, requestDesktop, detail, caller = null) {
747
891
  const rt = api();
748
892
  if (SETUP_DESKTOP_DOMAINS.includes(domain)) return requestDesktop({ action: 'status', domain });
749
893
  if (!Object.hasOwn(SETUP_STATUS_READERS, domain)) throw new Error(`setup: unknown status domain "${domain}"`);
750
- return SETUP_STATUS_READERS[domain](rt, { domain, detail, getConfig: () => getConfig?.() || {} });
894
+ return SETUP_STATUS_READERS[domain](rt, { domain, detail, caller, getConfig: () => getConfig?.() || {} });
751
895
  }
752
896
 
753
897
  function openSurface(target) {
@@ -776,21 +920,35 @@ export function createSetupToolExecutor({
776
920
  // it never creates agents, definitions or automations, nor changes any
777
921
  // other setting. Hosts without session provenance (no getCallerSession)
778
922
  // skip this gate; automation actions still refuse an unknown caller.
923
+ let scope = null;
779
924
  if (getCallerSession && !READ_ONLY_SETUP_ACTIONS.has(action)) {
780
- const scope = scheduleCallerScope(caller);
925
+ scope = scheduleCallerScope(caller);
781
926
  assertInteractiveCaller(scope, action);
782
927
  // Agent definitions (contacts) change only from Mixy; another agent's
783
928
  // home changes only its own agent settings.
784
929
  if (AGENT_DEFINITION_ACTIONS.has(action) && args.definitionKind === 'agent') {
785
930
  assertAgentEditorAccess(scope, caller, action);
786
931
  }
787
- if (action === 'set_agent_route') assertOwnAgentSettings(scope, args.agent, action);
932
+ if (OWN_AGENT_ACTIONS.has(action)) assertOwnAgentSettings(scope, args.agent, action);
788
933
  }
934
+ // Mixy's heartbeat session takes only its two routine actions, reads included.
935
+ assertHeartbeatSetupAction(sessionId, action, args);
789
936
  const rt = action === 'status' || action === 'open' ? null : api();
790
937
  if (!Object.hasOwn(SETUP_ACTION_HANDLERS, action)) {
791
938
  return executeExtendedSetupAction(rt, args, requestDesktop, { sessionId, caller });
792
939
  }
793
- return SETUP_ACTION_HANDLERS[action](rt, args, { requestDesktop, readStatus, openSurface });
940
+ return SETUP_ACTION_HANDLERS[action](rt, args, {
941
+ requestDesktop,
942
+ readStatus,
943
+ openSurface,
944
+ sessionId,
945
+ caller,
946
+ // The session-scoped actions authorize each target against it (session-route-actions.mjs).
947
+ scope,
948
+ getSession: getCallerSession,
949
+ getConfig: () => getConfig?.() || {},
950
+ executeSessionControl,
951
+ });
794
952
  }
795
953
 
796
954
  return {
@@ -33,9 +33,16 @@ function compactModelRow(row) {
33
33
  efforts: (row.effortOptions || []).map((option) => option.value),
34
34
  ...(row.fastCapable === true ? { fast: true } : {}),
35
35
  ...(row.supportsVision === true ? { vision: true } : {}),
36
+ ...modelPreferenceFields(row),
36
37
  };
37
38
  }
38
39
 
40
+ /** The model-management choices, only when set (savedHidden is an explicit /model visibility choice). */
41
+ const modelPreferenceFields = (row) => ({
42
+ ...(typeof row.savedHidden === 'boolean' ? { hidden: row.savedHidden } : {}),
43
+ ...(row.favorite === true ? { favorite: true } : {}),
44
+ });
45
+
39
46
  const isPresent = (value) =>
40
47
  value != null && value !== '' && value !== false && !(typeof value === 'object' && !Object.keys(value).length);
41
48
 
@@ -64,7 +71,10 @@ function modelDetail(row) {
64
71
  defaultModelParameters: row.defaultModelParameters,
65
72
  supportsMaxMode: row.supportsMaxMode,
66
73
  };
67
- return Object.fromEntries(Object.entries(detail).filter(([, value]) => isPresent(value)));
74
+ return {
75
+ ...Object.fromEntries(Object.entries(detail).filter(([, value]) => isPresent(value))),
76
+ ...modelPreferenceFields(row),
77
+ };
68
78
  }
69
79
 
70
80
  function findModel(rows, id, provider) {
@@ -283,6 +293,7 @@ export const EXTENDED_SETUP_HANDLERS = Object.freeze({
283
293
  if (kind === 'webhook' && ['time', 'at', 'timezone', 'days'].some((key) => own(input, key))) {
284
294
  throw new Error('Schedule fields (timing) are not accepted for webhooks');
285
295
  }
296
+ if (kind === 'webhook' && own(input, 'newName')) throw new Error('newName (rename) is schedule-only');
286
297
  if (kind === 'schedule' && own(input, 'parser')) throw new Error('parser is a webhook-only field');
287
298
  let previous = {};
288
299
  if (input.overwrite) {
@@ -363,6 +374,12 @@ export const EXTENDED_SETUP_HANDLERS = Object.freeze({
363
374
  .sections.flatMap((section) => section.options)
364
375
  .find((row) => row.id === args.name);
365
376
  if (!option) throw new Error(`Unknown developer option "${args.name}"; read status developer`);
377
+ // An agent must never remove its own approval cards: only the person turns this on.
378
+ if (args.enabled && option.id === 'allowAllActions') {
379
+ throw new Error(
380
+ `"${option.label}" cannot be turned on by an agent; only the person can turn it on in Settings → Developer options.`
381
+ );
382
+ }
366
383
  if (args.enabled && option.warning && !args.riskAccepted) {
367
384
  throw new Error(
368
385
  `Enabling ${option.label} requires riskAccepted:true after the user explicitly accepts: ${option.warning}`
@@ -401,6 +418,11 @@ export const EXTENDED_SETUP_HANDLERS = Object.freeze({
401
418
  },
402
419
  async set_media_default(rt, args) {
403
420
  if (!MEDIA_KINDS.includes(args.kind)) throw new Error(`kind must be one of ${MEDIA_KINDS.join(', ')}`);
421
+ // A blank lane returns the kind to Auto, as the Settings control does.
422
+ if (!String(args.lane).trim()) {
423
+ if (own(args, 'model') || own(args, 'accountId')) throw new Error('model and accountId need a lane');
424
+ return { kind: args.kind, media: await rt.setMediaPin({ kind: args.kind, lane: '' }) };
425
+ }
404
426
  const lane = (await rt.listMediaLanes()).find((row) => row.id === args.lane);
405
427
  if (!lane) throw new Error(`Unknown media lane "${args.lane}"; list lanes with the media tool`);
406
428
  if (!lane.authenticated) throw new Error(`${lane.label} is not signed in; open providers first`);
@@ -409,7 +431,16 @@ export const EXTENDED_SETUP_HANDLERS = Object.freeze({
409
431
  if (own(args, 'model') && !spec.models.some((model) => model.id === args.model)) {
410
432
  throw new Error(`model "${args.model}" is not available on ${lane.id}/${args.kind}`);
411
433
  }
412
- return { media: await rt.setMediaDefault(pick(args, ['kind', 'lane', 'model'])) };
434
+ if (own(args, 'accountId')) {
435
+ if (lane.authType !== 'oauth') throw new Error(`${lane.id} signs in with an API key; it has no accounts`);
436
+ if (!rt.getProviderAccounts(lane.authProvider).accounts.some((row) => row.id === args.accountId)) {
437
+ throw new Error(
438
+ `Unknown ${lane.authProvider} account "${args.accountId}"; list accounts with status providers`
439
+ );
440
+ }
441
+ }
442
+ const pin = await rt.setMediaPin({ kind: args.kind, lane: args.lane, model: args.model, account: args.accountId });
443
+ return { kind: args.kind, media: pin };
413
444
  },
414
445
  });
415
446
 
@@ -0,0 +1,152 @@
1
+ /** Setup actions that act on one running session rather than a setting: its
2
+ * fallback position (read in status model/agents), a return to its primary
3
+ * route, and its quota card's actions. The caller's own session changes
4
+ * through its runtime; another session through the daemon's configure lane
5
+ * (daemon-session-control/route-control.mjs), the lane the model picker and
6
+ * quota card use. */
7
+ import {
8
+ liveRouteFallbackPositions,
9
+ routeFallbackHopped,
10
+ routeFallbackPosition,
11
+ routeLeaf,
12
+ sessionChainSource,
13
+ sessionRoutePrimary,
14
+ } from '../../runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs';
15
+ import { sameRouteModel } from '../../runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs';
16
+ import { resolveSessionMode } from '../../runtime/agent/orchestrator/session/agent-mode.mjs';
17
+ import { agentHomeSessionId } from '../../runtime/agent/orchestrator/session/agent-home.mjs';
18
+ import { _liveSessions } from '../../runtime/agent/orchestrator/session/store/live-state.mjs';
19
+ import { assertOwnAgentSettings } from '../services/schedule-access.mjs';
20
+ import { clean } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
21
+
22
+ const modelOf = (route) => ({ provider: route?.provider || '', model: route?.model || '' });
23
+
24
+ /** The calling session's fallback position, and those of the other sessions
25
+ * held here that `keep` selects (status model: Leads; status agents: bots and agents). */
26
+ export function routePositionsStatus({ caller, config, keep }) {
27
+ return {
28
+ ...(caller?.agent ? { session: routeFallbackPosition(caller, config) } : {}),
29
+ sessions: liveRouteFallbackPositions(config, (row) => row.sessionId !== caller?.id && keep(row)),
30
+ };
31
+ }
32
+
33
+ /** The route a reset returns `session` to: the one it left at its first hop,
34
+ * else its settings' route when that is still its primary. */
35
+ function primaryRoute(session, config) {
36
+ if (session.routeFallbackPrimary?.provider) return routeLeaf(session.routeFallbackPrimary);
37
+ const primary = sessionRoutePrimary(session);
38
+ const source = sessionChainSource(session, config);
39
+ return source && sameRouteModel(source, primary) ? routeLeaf(source) : routeLeaf(primary);
40
+ }
41
+
42
+ const actingAgentOf = (session) => resolveSessionMode({ session }).agentId || clean(session?.agent);
43
+
44
+ function createSessionTargets(ctx) {
45
+ const { sessionId: callerId, caller, getSession, scope, executeSessionControl } = ctx;
46
+ const load = (id) => (id === callerId ? caller : getSession?.(id)) || null;
47
+ // A bot's thread acts only on its own sessions; the human's on any.
48
+ const assertTarget = (session, action) => {
49
+ if (scope) assertOwnAgentSettings(scope, actingAgentOf(session), action);
50
+ };
51
+ const target = (id, action, { other = false } = {}) => {
52
+ const sessionId = clean(id) || callerId;
53
+ if (!sessionId) throw new Error('sessionId is required');
54
+ if (other && sessionId === callerId) {
55
+ throw new Error(`${action} applies to another session: this one is running the setup call`);
56
+ }
57
+ const session = load(sessionId);
58
+ if (!session || session.closed === true) throw new Error(`session ${sessionId} not found`);
59
+ assertTarget(session, action);
60
+ return session;
61
+ };
62
+ const control = async (session, method, input) => {
63
+ if (typeof executeSessionControl !== 'function') {
64
+ throw new Error('Another session is reachable only through the Mixdog app or daemon; none hosts this runtime');
65
+ }
66
+ return executeSessionControl(
67
+ { action: 'route', session_id: session.id, method, ...(input ? { input } : {}) },
68
+ { callerSessionId: callerId }
69
+ );
70
+ };
71
+ return { load, target, control, assertTarget };
72
+ }
73
+
74
+ export const SESSION_ROUTE_HANDLERS = {
75
+ async reset_route_fallback(rt, args, ctx) {
76
+ if (Object.hasOwn(args, 'sessionId') && Object.hasOwn(args, 'agent')) {
77
+ throw new Error('Pass sessionId or agent, not both');
78
+ }
79
+ const config = ctx.getConfig();
80
+ const sessions = createSessionTargets(ctx);
81
+ let targets;
82
+ if (Object.hasOwn(args, 'agent')) {
83
+ const agent = clean(args.agent);
84
+ if (!agent) throw new Error('agent is required');
85
+ if (ctx.scope) assertOwnAgentSettings(ctx.scope, agent, 'reset_route_fallback');
86
+ // Its chats held here (home, room seats) and its home thread from disk.
87
+ const byId = new Map();
88
+ for (const session of _liveSessions.values()) {
89
+ if (session?.id && session.closed !== true && resolveSessionMode({ session }).agentId === agent) {
90
+ byId.set(session.id, session);
91
+ }
92
+ }
93
+ const home = agentHomeSessionId(agent);
94
+ if (home && !byId.has(home)) {
95
+ const stored = sessions.load(home);
96
+ if (stored && stored.closed !== true) byId.set(home, stored);
97
+ }
98
+ targets = [...byId.values()];
99
+ } else {
100
+ targets = [sessions.target(args.sessionId, 'reset_route_fallback')];
101
+ }
102
+ const reset = [];
103
+ const unchanged = [];
104
+ for (const session of targets) {
105
+ if (!routeFallbackHopped(session)) {
106
+ unchanged.push({ sessionId: session.id, current: modelOf(session) });
107
+ continue;
108
+ }
109
+ const route = primaryRoute(session, config);
110
+ // The session's own model pick, from its next turn: the switch restarts
111
+ // its chain (fallback index and failures cleared).
112
+ if (session.id === ctx.sessionId) {
113
+ await rt.setRoute(route, { sessionOnly: true, switchEstablished: true, deferUntilNextTurn: true });
114
+ } else {
115
+ await sessions.control(session, 'setRoute', route);
116
+ }
117
+ reset.push({ sessionId: session.id, from: modelOf(session), to: modelOf(route) });
118
+ }
119
+ return { scope: 'session', reset, ...(unchanged.length ? { alreadyOnPrimary: unchanged } : {}) };
120
+ },
121
+ async continue_in_model(_rt, args, ctx) {
122
+ const sessions = createSessionTargets(ctx);
123
+ const session = sessions.target(args.sessionId, 'continue_in_model', { other: true });
124
+ const route = routeLeaf(args.route);
125
+ const continued = (await sessions.control(session, 'continueSessionInModel', route)) !== false;
126
+ return {
127
+ scope: 'session',
128
+ sessionId: session.id,
129
+ continued,
130
+ route: modelOf(route),
131
+ note: 'The stopped turn is not resent: the user resumes it from that conversation (Resume, or a new message).',
132
+ };
133
+ },
134
+ async schedule_quota_resume(_rt, args, ctx) {
135
+ const sessions = createSessionTargets(ctx);
136
+ const session = sessions.target(args.sessionId, 'schedule_quota_resume', { other: true });
137
+ return {
138
+ scope: 'session',
139
+ sessionId: session.id,
140
+ scheduled: (await sessions.control(session, 'scheduleQuotaResume')) === true,
141
+ };
142
+ },
143
+ async cancel_quota_resume(_rt, args, ctx) {
144
+ const sessions = createSessionTargets(ctx);
145
+ const session = sessions.target(args.sessionId, 'cancel_quota_resume', { other: true });
146
+ return {
147
+ scope: 'session',
148
+ sessionId: session.id,
149
+ cancelled: (await sessions.control(session, 'cancelQuotaResume')) === true,
150
+ };
151
+ },
152
+ };