mixdog 1.3.5 → 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 (341) hide show
  1. package/NOTICE.md +2 -2
  2. package/README.md +2 -2
  3. package/package.json +2 -11
  4. package/scripts/daemon-stub.mjs +1 -1
  5. package/scripts/gate-local.mjs +4 -2
  6. package/scripts/native-release-status.mjs +2 -2
  7. package/scripts/recall-history-cases.json +1 -1
  8. package/scripts/suite-health.mjs +10 -1
  9. package/scripts/test.mjs +7 -11
  10. package/src/agents/reviewer/AGENT.md +7 -11
  11. package/src/agents/reviewer/agent.json +1 -1
  12. package/src/defaults/shipped-history.json +17 -1
  13. package/src/defaults/skills/browser-use/SKILL.md +59 -11
  14. package/src/defaults/skills/code-tidy/SKILL.md +142 -270
  15. package/src/defaults/skills/code-tidy/references/agent-cleanup.md +4 -4
  16. package/src/defaults/skills/code-tidy/references/cleanup-report.md +31 -76
  17. package/src/defaults/skills/code-tidy/references/dead-code.md +6 -12
  18. package/src/defaults/skills/computer-use/SKILL.md +33 -9
  19. package/src/defaults/skills/setup/SKILL.md +22 -0
  20. package/src/defaults/skills/setup/references/actions.md +39 -5
  21. package/src/defaults/skills/setup/references/surfaces.md +11 -2
  22. package/src/defaults/skills/visualize/SKILL.md +2 -1
  23. package/src/lib/rules-builder.cjs +10 -8
  24. package/src/rules/agent/AGENT.md +6 -6
  25. package/src/rules/lead/LEAD.md +18 -20
  26. package/src/rules/shared/05-tool-calls.md +5 -6
  27. package/src/rules/shared/10-tool-workflow.md +2 -0
  28. package/src/rules/shared/60-verification.md +7 -5
  29. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -3
  30. package/src/runtime/agent/orchestrator/agent-runtime/title-completion.mjs +3 -1
  31. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +3 -1
  32. package/src/runtime/agent/orchestrator/config.mjs +17 -1
  33. package/src/runtime/agent/orchestrator/context/collect.mjs +3 -0
  34. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +72 -67
  35. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +54 -1
  36. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +3 -3
  37. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +176 -72
  38. package/src/runtime/agent/orchestrator/mcp/client.mjs +19 -0
  39. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +181 -133
  40. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +42 -6
  41. package/src/runtime/agent/orchestrator/providers/anthropic-cache-warm.mjs +165 -0
  42. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  43. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +4 -1
  44. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +14 -14
  45. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +145 -66
  46. package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +25 -3
  47. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +64 -33
  48. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +135 -87
  49. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +123 -102
  50. package/src/runtime/agent/orchestrator/providers/cache-warm-admission.mjs +90 -0
  51. package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +25 -9
  52. package/src/runtime/agent/orchestrator/providers/cursor.mjs +22 -13
  53. package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +17 -3
  54. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +52 -10
  55. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +8 -1
  56. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
  57. package/src/runtime/agent/orchestrator/providers/npm-cli-version.mjs +11 -3
  58. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +2 -1
  59. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -9
  60. package/src/runtime/agent/orchestrator/providers/openai-ws-send/send-context.mjs +22 -3
  61. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +43 -5
  62. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/handshake-failure.mjs +3 -1
  63. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/stream-failure.mjs +6 -3
  64. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +65 -1
  65. package/src/runtime/agent/orchestrator/providers/quota-reading.mjs +38 -12
  66. package/src/runtime/agent/orchestrator/providers/registry.mjs +26 -0
  67. package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +39 -7
  68. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +6 -0
  69. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +12 -7
  70. package/src/runtime/agent/orchestrator/runtime-core/compaction-config-keys.mjs +62 -0
  71. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +37 -16
  72. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-delta.mjs +100 -23
  73. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +2 -0
  74. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +19 -4
  75. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +125 -30
  76. package/src/runtime/agent/orchestrator/session/cache-warm.mjs +792 -0
  77. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +133 -22
  78. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +475 -58
  79. package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +42 -17
  80. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +4 -1
  81. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +11 -5
  82. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +0 -2
  83. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -4
  84. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +1 -1
  85. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +18 -22
  86. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +32 -3
  87. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +3 -1
  88. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +1 -1
  89. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +17 -3
  90. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +7 -2
  91. package/src/runtime/agent/orchestrator/session/loop/tool-exec/after-hook.mjs +2 -1
  92. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +3 -2
  93. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +16 -2
  94. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +16 -1
  95. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +10 -0
  96. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -10
  97. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +12 -3
  98. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -2
  99. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +19 -1
  100. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +133 -90
  101. package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +31 -4
  102. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +30 -18
  103. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +23 -13
  104. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +11 -2
  105. package/src/runtime/agent/orchestrator/session/manager/provider-replay.mjs +13 -0
  106. package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +129 -0
  107. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +20 -2
  108. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +2 -0
  109. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +8 -0
  110. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +1 -1
  111. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +7 -2
  112. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +102 -15
  113. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +18 -5
  114. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +19 -8
  115. package/src/runtime/agent/orchestrator/session/prompt-cache-idle-gaps.mjs +113 -0
  116. package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +127 -0
  117. package/src/runtime/agent/orchestrator/session/routine-result.mjs +36 -9
  118. package/src/runtime/agent/orchestrator/session/sensitive-projection.mjs +100 -0
  119. package/src/runtime/agent/orchestrator/session/sensitive-values.mjs +257 -0
  120. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +20 -7
  121. package/src/runtime/agent/orchestrator/session/side-question.mjs +18 -0
  122. package/src/runtime/agent/orchestrator/session/skill-update-notice.mjs +60 -0
  123. package/src/runtime/agent/orchestrator/session/store/live-state.mjs +6 -3
  124. package/src/runtime/agent/orchestrator/session/store/save-delta.mjs +9 -5
  125. package/src/runtime/agent/orchestrator/session/store/serialize.mjs +9 -8
  126. package/src/runtime/agent/orchestrator/session/store/session-persistence-events.mjs +29 -0
  127. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +8 -6
  128. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +12 -2
  129. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +44 -14
  130. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +84 -3
  131. package/src/runtime/agent/orchestrator/session/stored-attachment-history.mjs +32 -0
  132. package/src/runtime/agent/orchestrator/session/thinking-binding.mjs +108 -0
  133. package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +2 -2
  134. package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +2 -1
  135. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +18 -1
  136. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-status.mjs +9 -7
  137. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-text.mjs +2 -2
  138. package/src/runtime/agent/orchestrator/session/turn-failure.mjs +5 -1
  139. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +5 -2
  140. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +12 -1
  141. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/misuse-guard.mjs +240 -0
  142. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +1 -1
  143. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/shell-cwd.mjs +38 -0
  144. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +25 -5
  145. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +65 -7
  146. package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +7 -1
  147. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +36 -13
  148. package/src/runtime/browser-bridge/action-schema.mjs +13 -2
  149. package/src/runtime/browser-bridge/client.mjs +73 -8
  150. package/src/runtime/browser-bridge/input-fields.mjs +8 -9
  151. package/src/runtime/browser-bridge/mail-links.mjs +107 -0
  152. package/src/runtime/browser-bridge/tool-defs.mjs +1 -1
  153. package/src/runtime/channels/lib/config.mjs +6 -61
  154. package/src/runtime/channels/lib/crash-log.mjs +1 -1
  155. package/src/runtime/channels/lib/event-pipeline.mjs +4 -19
  156. package/src/runtime/channels/lib/event-queue.mjs +3 -18
  157. package/src/runtime/channels/lib/owned-runtime/automation-services.mjs +2 -2
  158. package/src/runtime/channels/lib/owned-runtime/automation-start.mjs +3 -4
  159. package/src/runtime/channels/lib/owned-runtime/config-reload.mjs +4 -59
  160. package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +33 -115
  161. package/src/runtime/channels/lib/owned-runtime/owned-stop.mjs +5 -22
  162. package/src/runtime/channels/lib/owned-runtime/state.mjs +2 -8
  163. package/src/runtime/channels/lib/owned-runtime.mjs +6 -23
  164. package/src/runtime/channels/lib/parent-bridge.mjs +2 -2
  165. package/src/runtime/channels/lib/runtime-paths.mjs +4 -43
  166. package/src/runtime/channels/lib/scheduler.mjs +33 -147
  167. package/src/runtime/channels/lib/schedules-db.mjs +34 -11
  168. package/src/runtime/channels/lib/status-snapshot.mjs +1 -1
  169. package/src/runtime/channels/lib/tool-dispatch.mjs +1 -2
  170. package/src/runtime/channels/lib/webhooks-db.mjs +5 -9
  171. package/src/runtime/channels/lib/worker-bootstrap.mjs +0 -1
  172. package/src/runtime/channels/lib/worker-ipc.mjs +0 -1
  173. package/src/runtime/channels/lib/worker-main.mjs +10 -34
  174. package/src/runtime/computer-bridge/action-schema.mjs +42 -1
  175. package/src/runtime/computer-bridge/actions.mjs +9 -1
  176. package/src/runtime/computer-bridge/client.mjs +180 -8
  177. package/src/runtime/computer-bridge/core-actions.mjs +2 -2
  178. package/src/runtime/computer-bridge/error-recovery.mjs +27 -2
  179. package/src/runtime/computer-bridge/linux-readiness.mjs +65 -23
  180. package/src/runtime/computer-bridge/pending-continuation.mjs +1 -1
  181. package/src/runtime/connectors/connector-accounts-mcp.mjs +230 -0
  182. package/src/runtime/connectors/gmail-app-password.mjs +172 -39
  183. package/src/runtime/connectors/google-mcp.mjs +10 -12
  184. package/src/runtime/memory/lib/pg/adapter.mjs +77 -70
  185. package/src/runtime/memory/lib/pg/supervisor.mjs +24 -12
  186. package/src/runtime/memory/lib/runtime-fetcher.mjs +23 -5
  187. package/src/runtime/session-search/session-search-db.mjs +73 -9
  188. package/src/runtime/session-search/session-search-ingest.mjs +110 -20
  189. package/src/runtime/shared/abort-race.mjs +89 -0
  190. package/src/runtime/shared/agent-avatar.d.mts +0 -1
  191. package/src/runtime/shared/agent-kind.mjs +4 -3
  192. package/src/runtime/shared/bounded-download.mjs +8 -3
  193. package/src/runtime/shared/config.mjs +2 -13
  194. package/src/runtime/shared/connector-accounts.mjs +20 -10
  195. package/src/runtime/shared/connector-catalog.d.mts +29 -0
  196. package/src/runtime/shared/connector-catalog.mjs +150 -0
  197. package/src/runtime/shared/connector-triggers.mjs +22 -19
  198. package/src/runtime/shared/defaults-separation.mjs +2 -14
  199. package/src/runtime/shared/delegated-approval-host.mjs +103 -11
  200. package/src/runtime/shared/err-text.mjs +24 -15
  201. package/src/runtime/shared/file-permissions.d.mts +0 -8
  202. package/src/runtime/shared/file-permissions.mjs +0 -11
  203. package/src/runtime/shared/git-direct.test-support.mjs +19 -0
  204. package/src/runtime/shared/heartbeat-interval.d.mts +4 -0
  205. package/src/runtime/shared/heartbeat-interval.mjs +19 -0
  206. package/src/runtime/shared/idle-auto-clear-gate.mjs +104 -0
  207. package/src/runtime/shared/llm/usage-rollup.mjs +1 -1
  208. package/src/runtime/shared/mixy-presence.mjs +22 -0
  209. package/src/runtime/shared/orchestration.d.mts +2 -0
  210. package/src/runtime/shared/pg-schema-db.mjs +27 -16
  211. package/src/runtime/shared/profile-config.cjs +1 -1
  212. package/src/runtime/shared/prompt-origin.d.mts +4 -0
  213. package/src/runtime/shared/prompt-origin.mjs +21 -0
  214. package/src/runtime/shared/provider-accounts.mjs +7 -1
  215. package/src/runtime/shared/provider-display-name.mjs +29 -0
  216. package/src/runtime/shared/provider-timeout-phase.mjs +2 -0
  217. package/src/runtime/shared/routine-trigger.d.mts +2 -0
  218. package/src/runtime/shared/routine-trigger.mjs +10 -0
  219. package/src/runtime/shared/sensitive-path-rules.d.mts +2 -10
  220. package/src/runtime/shared/sensitive-path-rules.mjs +19 -41
  221. package/src/runtime/shared/session-activity-signal.mjs +34 -1
  222. package/src/runtime/shared/session-sidebar-host.d.mts +1 -1
  223. package/src/runtime/shared/session-sidebar-host.mjs +7 -3
  224. package/src/runtime/shared/session-turn-prelude.mjs +18 -0
  225. package/src/runtime/shared/tool-result-summary.mjs +1 -14
  226. package/src/runtime/shared/worktrees/test-support.mjs +9 -4
  227. package/src/runtime/terminal-bridge/client.mjs +10 -1
  228. package/src/runtime/visualize-bridge/client.mjs +27 -8
  229. package/src/runtime/visualize-bridge/tool-defs.mjs +22 -8
  230. package/src/runtime/visualize-bridge/tool-meta.d.mts +5 -0
  231. package/src/runtime/visualize-bridge/tool-meta.mjs +22 -0
  232. package/src/runtime/web-search/lib/fetch-pipeline.mjs +2 -2
  233. package/src/session-runtime/agent-home-delivery.mjs +448 -185
  234. package/src/session-runtime/agent-task-sessions.mjs +329 -180
  235. package/src/session-runtime/boot/apis.mjs +9 -0
  236. package/src/session-runtime/boot/infrastructure.mjs +4 -0
  237. package/src/session-runtime/boot/providers.mjs +3 -0
  238. package/src/session-runtime/boot/tools.mjs +6 -1
  239. package/src/session-runtime/config-lifecycle.mjs +6 -0
  240. package/src/session-runtime/connector-merge-migration.mjs +305 -0
  241. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +132 -28
  242. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +2 -0
  243. package/src/session-runtime/lifecycle/inheritance.mjs +16 -3
  244. package/src/session-runtime/lifecycle/session-catalog.mjs +3 -1
  245. package/src/session-runtime/lifecycle/teardown/runtime-stops.mjs +50 -4
  246. package/src/session-runtime/lifecycle/teardown.mjs +23 -5
  247. package/src/session-runtime/mcp-glue/server-catalog.mjs +55 -3
  248. package/src/session-runtime/mcp-oauth-login.mjs +5 -3
  249. package/src/session-runtime/mixy-heartbeat.mjs +153 -33
  250. package/src/session-runtime/notification-bus.mjs +31 -117
  251. package/src/session-runtime/notification-completion-queue.mjs +54 -0
  252. package/src/session-runtime/notification-listeners.mjs +76 -0
  253. package/src/session-runtime/owned-automation-run.mjs +2 -1
  254. package/src/session-runtime/prewarm/channel-start.mjs +3 -7
  255. package/src/session-runtime/resource-mcp-api.mjs +143 -224
  256. package/src/session-runtime/resource-mcp-connector-accounts.mjs +163 -0
  257. package/src/session-runtime/resource-mcp-oauth-api.mjs +217 -0
  258. package/src/session-runtime/resource-mcp-secrets.mjs +82 -0
  259. package/src/session-runtime/resource-mcp-servers.mjs +78 -0
  260. package/src/session-runtime/resource-mcp-store.mjs +49 -0
  261. package/src/session-runtime/routine-owner.mjs +40 -9
  262. package/src/session-runtime/services/agent-tool/close-flow.mjs +3 -0
  263. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +7 -2
  264. package/src/session-runtime/services/agent-tool/tag-registry.mjs +1 -0
  265. package/src/session-runtime/services/agent-tool/worker-index/awaiting-user-sync.mjs +51 -0
  266. package/src/session-runtime/services/agent-tool/worker-index/disposal.mjs +55 -0
  267. package/src/session-runtime/services/agent-tool/worker-index/released-store.mjs +123 -0
  268. package/src/session-runtime/services/agent-tool/worker-index/restart-settlement.mjs +112 -0
  269. package/src/session-runtime/services/agent-tool/worker-index/row-reads.mjs +36 -0
  270. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -0
  271. package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +17 -4
  272. package/src/session-runtime/services/agent-tool/worker-index/row-writes.mjs +58 -0
  273. package/src/session-runtime/services/agent-tool/worker-index.mjs +41 -241
  274. package/src/session-runtime/services/agent-tool/worker-rows.mjs +3 -0
  275. package/src/session-runtime/services/channel-admin.mjs +7 -13
  276. package/src/session-runtime/services/channel-session-router.mjs +4 -2
  277. package/src/session-runtime/services/seeds.mjs +0 -28
  278. package/src/session-runtime/session-compaction-sync.mjs +27 -0
  279. package/src/session-runtime/session-create/session-options.mjs +1 -0
  280. package/src/session-runtime/settings-builtin-tools-api.mjs +11 -0
  281. package/src/session-runtime/settings-compaction-api.mjs +2 -40
  282. package/src/session-runtime/settings-system-api.mjs +13 -1
  283. package/src/session-runtime/setup-tool/agent-settings-actions.mjs +65 -0
  284. package/src/session-runtime/setup-tool/executor.mjs +145 -22
  285. package/src/session-runtime/setup-tool/extended-actions.mjs +8 -3
  286. package/src/session-runtime/setup-tool/session-route-actions.mjs +152 -0
  287. package/src/session-runtime/setup-tool/settings-contract.mjs +4 -3
  288. package/src/session-runtime/setup-tool/tool-defs.mjs +64 -5
  289. package/src/session-runtime/tool-policy/agent-profile-fold.mjs +17 -13
  290. package/src/session-runtime/tool-policy/policy-stage.mjs +16 -2
  291. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
  292. package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +4 -0
  293. package/src/standalone/channel-transport/client-registry/client-records.mjs +65 -0
  294. package/src/standalone/channel-transport/client-registry/client-registration.mjs +118 -0
  295. package/src/standalone/channel-transport/client-registry.mjs +24 -163
  296. package/src/standalone/channel-transport.mjs +1 -1
  297. package/src/standalone/daemon-channels-loader.mjs +10 -8
  298. package/src/standalone/daemon-session-control/list-page.mjs +72 -0
  299. package/src/standalone/daemon-session-control/request.mjs +51 -0
  300. package/src/standalone/daemon-session-control/room-host.mjs +57 -0
  301. package/src/standalone/daemon-session-control/route-control.mjs +48 -0
  302. package/src/standalone/daemon-session-control/send-turn.mjs +60 -0
  303. package/src/standalone/daemon-session-control/worktree-control.mjs +40 -0
  304. package/src/standalone/daemon-session-control.mjs +124 -188
  305. package/src/standalone/daemon.mjs +26 -3
  306. package/src/standalone/session-client.mjs +7 -3
  307. package/src/standalone/session-protocol.mjs +5 -0
  308. package/src/standalone/session-service/agent-tree/agent-child.mjs +1 -0
  309. package/src/standalone/session-service/quota-resume.mjs +3 -0
  310. package/src/standalone/session-service/retention/busy.mjs +65 -1
  311. package/src/standalone/session-service/session-calls/session-action.mjs +23 -2
  312. package/src/standalone/session-service/session-calls/session-views.mjs +8 -0
  313. package/src/standalone/session-tool-output.mjs +15 -7
  314. package/src/tui/app/model-options.mjs +3 -28
  315. package/src/tui/dist/index.mjs +58 -64
  316. package/src/tui/session/agent-job-feed/model-notification.mjs +3 -0
  317. package/src/tui/session/initial-state.mjs +3 -0
  318. package/src/tui/session/notice-surface.mjs +7 -1
  319. package/src/tui/session/queue-helpers.mjs +3 -42
  320. package/src/tui/session/session-api/routes-agent-definitions.mjs +14 -2
  321. package/src/tui/session/session-api/routes-write.mjs +5 -1
  322. package/src/tui/session/session-api/routes.mjs +1 -0
  323. package/src/tui/session/session-api/settings.mjs +17 -1
  324. package/src/tui/session/session-flow/auto-clear.mjs +4 -33
  325. package/src/tui/session/session-flow/drain-closed-queue.mjs +38 -0
  326. package/src/tui/session/session-flow/drain-enqueue.mjs +86 -0
  327. package/src/tui/session/session-flow/drain-retry.mjs +35 -0
  328. package/src/tui/session/session-flow/drain.mjs +118 -149
  329. package/src/tui/session/session-flow/queue/entry-shape.mjs +5 -0
  330. package/src/tui/session/session-flow/queue/take-entries.mjs +5 -2
  331. package/src/tui/session/tool-approval-delegation.mjs +64 -0
  332. package/src/tui/session/tool-approval-request.mjs +39 -0
  333. package/src/tui/session/tool-approval-settle.mjs +62 -0
  334. package/src/tui/session/tool-approval.mjs +69 -115
  335. package/src/tui/session/tui-steering-persist.mjs +8 -1
  336. package/src/tui/session/turn-stream.mjs +19 -2
  337. package/src/tui/session/turn.mjs +1 -0
  338. package/src/vendor/statusline/bin/statusline-route.mjs +1 -219
  339. package/src/vendor/statusline/src/gateway/route-meta.mjs +35 -13
  340. package/scripts/code-graph-description-contract.mjs +0 -188
  341. package/src/runtime/channels/lib/owned-runtime/ownership-refresh.mjs +0 -36
@@ -0,0 +1,65 @@
1
+ /** Setup actions for per-agent and per-project switches and the bot roster:
2
+ * action permissions, connector switches, archived bots, bot order and
3
+ * prompt-cache warming. Each goes through the runtime method its settings
4
+ * surface uses. Permission and connector writes are made as a remote client's
5
+ * are (`noWiden`): a chat may take authority away, never give an agent more
6
+ * without asking — that stays with the person in the desktop app. */
7
+ import { agentPermissions, setAgentPermission } from '../../runtime/agent/orchestrator/session/human-gate/store.mjs';
8
+ import { clean } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
9
+
10
+ const NO_WIDEN = Object.freeze({ noWiden: true });
11
+
12
+ function requireText(value, label) {
13
+ const text = clean(value);
14
+ if (!text) throw new Error(`${label} is required`);
15
+ return text;
16
+ }
17
+
18
+ export const AGENT_SETTINGS_HANDLERS = {
19
+ async read_agent_settings(rt, args) {
20
+ const agent = requireText(args.agent, 'agent');
21
+ const [permissions, connectors, alwaysAllowedTools] = await Promise.all([
22
+ agentPermissions(agent),
23
+ rt.getAgentConnectors(agent),
24
+ rt.getAgentAlwaysAllowedTools(agent),
25
+ ]);
26
+ return { agent, permissions, connectors, alwaysAllowedTools };
27
+ },
28
+ read_project_connectors: async (rt, args) => {
29
+ const projectPath = requireText(args.projectPath, 'projectPath');
30
+ return { projectPath, connectors: await rt.getProjectConnectors(projectPath) };
31
+ },
32
+ async set_agent_permission(_rt, args) {
33
+ const agent = requireText(args.agent, 'agent');
34
+ return { agent, permissions: await setAgentPermission(agent, args.actionKind, args.permissionMode, NO_WIDEN) };
35
+ },
36
+ async set_agent_connector(rt, args) {
37
+ const agent = requireText(args.agent, 'agent');
38
+ const name = requireText(args.name, 'name');
39
+ return { agent, connectors: await rt.setAgentConnectorEnabled(agent, name, args.enabled, NO_WIDEN) };
40
+ },
41
+ async set_agent_connector_tool(rt, args) {
42
+ const agent = requireText(args.agent, 'agent');
43
+ const name = requireText(args.name, 'name');
44
+ const tool = requireText(args.tool, 'tool');
45
+ await rt.setAgentConnectorToolPermission(agent, name, tool, args.permission, NO_WIDEN);
46
+ return { agent, connector: name, tool, permission: args.permission };
47
+ },
48
+ async set_project_connector(rt, args) {
49
+ const projectPath = requireText(args.projectPath, 'projectPath');
50
+ const name = requireText(args.name, 'name');
51
+ return { projectPath, connectors: await rt.setProjectConnectorEnabled(projectPath, name, args.enabled, NO_WIDEN) };
52
+ },
53
+ async restore_definition(rt, args) {
54
+ if (args.definitionKind !== 'agent') {
55
+ throw new Error('Only an archived agent or bot can be restored; a deleted workflow is gone');
56
+ }
57
+ const name = requireText(args.name, 'name');
58
+ await rt.restoreAgentDefinition(name);
59
+ return { restored: name, note: 'Its chat is back; its routines stay off until they are turned on.' };
60
+ },
61
+ set_bot_order: (rt, args) => ({ botOrder: rt.setBotOrder(args.botOrder).order }),
62
+ set_cache_warming_enabled: (rt, args) => ({
63
+ promptCacheWarming: rt.setCacheWarmingEnabled(args.enabled).enabled,
64
+ }),
65
+ };
@@ -7,6 +7,10 @@ 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';
@@ -30,8 +34,32 @@ import { schemaValueError } from '../../runtime/shared/schema-value-error.mjs';
30
34
  import { clean } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
31
35
  import { CONNECTOR_IDS, connectorById } from '../../runtime/shared/connector-catalog.mjs';
32
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
+ ]);
33
59
 
34
- 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)';
35
63
 
36
64
  const PROFILE_VALUE_IDS = Object.freeze({
37
65
  language: profileConfig.PROFILE_LANGUAGES.map((entry) => entry.id),
@@ -39,8 +67,20 @@ const PROFILE_VALUE_IDS = Object.freeze({
39
67
  });
40
68
 
41
69
  const ACTION_APPLIES_TO = {
42
- set_agent_route:
43
- "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',
44
84
  set_orchestration_mode:
45
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",
46
86
  set_recap_enabled: 'background Memory cycles; no restart required',
@@ -48,7 +88,9 @@ const ACTION_APPLIES_TO = {
48
88
  set_ai_session_titles: 'titles generated from now on; existing titles are unchanged',
49
89
  set_quota_auto_resume: 'subsequent usage-limit stops; turning it off cancels pending resumes',
50
90
  set_model_preferences: 'the /model picker and model management lists',
51
- set_heartbeat_enabled: "Mixy's next heartbeat check (every 30 minutes); no restart required",
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",
52
94
  set_reactions_enabled:
53
95
  "bots' chats; while off a reaction is refused at once and new chats are not offered the tool; reactions already left stay visible",
54
96
  set_worktree_settings: 'new tasks and worktrees created afterwards; existing worktrees are unchanged',
@@ -124,6 +166,31 @@ function routeInput(source) {
124
166
  return next;
125
167
  }
126
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
+
127
194
  function apiKeySource(row) {
128
195
  if (row.env) return `env:${row.envName}`;
129
196
  return row.stored ? 'keychain' : 'none';
@@ -232,6 +299,18 @@ function mcpRows(status) {
232
299
  error: server.error || null,
233
300
  ...(server.warnings?.length ? { warnings: server.warnings } : {}),
234
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
+ : {}),
235
314
  })),
236
315
  ...(status?.warnings?.length ? { warnings: status.warnings } : {}),
237
316
  ...(status?.declarationErrors?.length ? { declarationErrors: status.declarationErrors } : {}),
@@ -284,14 +363,20 @@ async function featureStatus(rt, getConfig) {
284
363
  browser: { active: builtinFeatureActive(config, 'browser') },
285
364
  computer: { active: builtinFeatureActive(config, 'computer') },
286
365
  terminal: { active: builtinFeatureActive(config, 'terminal') },
287
- heartbeat: { enabled: config?.heartbeat !== false },
366
+ heartbeat: {
367
+ enabled: config?.heartbeat !== false,
368
+ intervalMinutes: normalizeHeartbeatIntervalMinutes(config?.heartbeatIntervalMinutes),
369
+ },
288
370
  reactions: { enabled: config?.reactions !== false },
289
371
  voice: rt.getVoiceStatus ? voiceFeatureStatus(await rt.getVoiceStatus()) : null,
290
372
  };
291
373
  }
292
374
 
293
375
  async function mediaDefaults(rt) {
294
- const [pins, ...rows] = await Promise.all([rt.getMediaPins(), ...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
+ ]);
295
380
  const fieldsOf = (row, fields) => (row ? Object.fromEntries(fields.map((field) => [field, row[field]])) : null);
296
381
  return {
297
382
  // The default generator (Settings → Built-in → Media generation); null = Auto.
@@ -335,7 +420,7 @@ const SETUP_STATUS_READERS = {
335
420
  features: await featureStatus(rt, getConfig),
336
421
  onboarding: rt.getOnboardingStatus?.() || null,
337
422
  }),
338
- model: (rt) => ({
423
+ model: (rt, { getConfig, caller }) => ({
339
424
  route: {
340
425
  ...mainRoute(rt),
341
426
  autoEffort: rt.autoEffort !== false,
@@ -343,11 +428,16 @@ const SETUP_STATUS_READERS = {
343
428
  fallbacks: (rt.listPresets?.() || []).find((preset) => preset?.id === 'workflow-lead')?.fallbacks || [],
344
429
  },
345
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' }),
346
433
  }),
347
- agents: (rt) => ({
434
+ agents: (rt, { getConfig, caller }) => ({
348
435
  agents: rt.listAgents?.() || [],
349
436
  orchestrationMode: rt.getOrchestrationMode(),
350
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,
351
441
  }),
352
442
  workflow: (rt) => ({ workflows: rt.listWorkflows?.() || [] }),
353
443
  websearch: (rt) => ({
@@ -366,7 +456,11 @@ const SETUP_STATUS_READERS = {
366
456
  };
367
457
  },
368
458
  autoclear: (rt) => rt.getAutoClear?.() || {},
369
- compaction: (rt) => ({ ...(rt.getCompactionSettings?.() || {}), ...(rt.getSessionTitleSettings?.() || {}) }),
459
+ compaction: (rt) => ({
460
+ ...(rt.getCompactionSettings?.() || {}),
461
+ ...(rt.getSessionTitleSettings?.() || {}),
462
+ ...(rt.getCacheWarmingSettings ? { promptCacheWarming: rt.getCacheWarmingSettings().enabled } : {}),
463
+ }),
370
464
  memory: (rt) => ({ ...(rt.getToolModuleSettings?.()?.memory || {}), recap: rt.getRecapSettings?.() || null }),
371
465
  'local-provider': (rt) => rt.getToolModuleSettings().localProvider,
372
466
  features: (rt, { getConfig }) => featureStatus(rt, getConfig),
@@ -496,22 +590,28 @@ const SETUP_INSTALLABLE_FEATURES = [
496
590
  // Action handlers: (rt, args, { requestDesktop, readStatus, openSurface }).
497
591
  // `rt` is null for `status` and `open`, which never touch the facade.
498
592
  const SETUP_ACTION_HANDLERS = {
499
- status: async (_rt, args, { readStatus, requestDesktop }) => {
593
+ status: async (_rt, args, { readStatus, requestDesktop, caller }) => {
500
594
  const domain = clean(args.domain) || 'summary';
501
595
  requireEnum(domain, SETUP_STATUS_DOMAINS, 'domain');
502
- return { domain, ...(await readStatus(domain, requestDesktop, args.detail === true)) };
596
+ return { domain, ...(await readStatus(domain, requestDesktop, args.detail === true, caller)) };
503
597
  },
504
598
  open: (_rt, args, { openSurface }) => openSurface(args.target),
505
- set_route: async (rt, args) => {
599
+ set_route: async (rt, args, context) => {
506
600
  const route = routeInput(args.route);
507
601
  const next = await rt.setRoute(route, { keepMainModel: false });
508
- return { route: next, appliesTo: 'next session (a conversation keeps its frozen route)' };
602
+ return { route: next, ...chainReceipt(args.route, context, { kind: 'lead' }) };
509
603
  },
510
- set_agent_route: async (rt, args) => {
604
+ set_agent_route: async (rt, args, context) => {
511
605
  const agent = requireText(args.agent, 'agent');
512
606
  const route = routeInput(args.route);
513
- 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
+ };
514
612
  },
613
+ ...SESSION_ROUTE_HANDLERS,
614
+ ...AGENT_SETTINGS_HANDLERS,
515
615
  set_web_search_route: async (rt, args) => ({ route: await rt.setWebSearchRoute(routeInput(args.route)) }),
516
616
  set_workflow: (rt, args) => rt.setWorkflow(requireText(args.workflow, 'workflow')),
517
617
  set_output_style: async (rt, args) => {
@@ -621,14 +721,18 @@ const SETUP_ACTION_HANDLERS = {
621
721
  set_system_shell: (rt, args) => rt.setSystemShell({ command: clean(args.command) }),
622
722
  set_auto_update: (rt, args) => rt.setAutoUpdate(requireBoolean(args.enabled)),
623
723
  set_ai_session_titles: (rt, args) => rt.setAiSessionTitles(requireBoolean(args.enabled)),
624
- set_quota_auto_resume: async (rt, args) => ({ quotaAutoResume: await rt.setQuotaAutoResume(requireBoolean(args.enabled)) }),
724
+ set_quota_auto_resume: async (rt, args) => ({
725
+ quotaAutoResume: await rt.setQuotaAutoResume(requireBoolean(args.enabled)),
726
+ }),
625
727
  set_model_preferences: async (rt, args) => {
626
728
  const change = { model: requireText(args.model, 'model') };
627
- for (const key of ['hidden', 'favorite']) if (Object.hasOwn(args, key)) change[key] = requireBoolean(args[key], key);
729
+ for (const key of ['hidden', 'favorite'])
730
+ if (Object.hasOwn(args, key)) change[key] = requireBoolean(args[key], key);
628
731
  if (Object.keys(change).length === 1) throw new Error('hidden or favorite is required');
629
732
  return rt.setModelPreferences(requireText(args.provider, 'provider'), [change]);
630
733
  },
631
734
  set_heartbeat_enabled: (rt, args) => rt.setHeartbeatEnabled(requireBoolean(args.enabled)),
735
+ set_heartbeat_interval: (rt, args) => rt.setHeartbeatInterval(requireHeartbeatIntervalMinutes(args.minutes)),
632
736
  set_reactions_enabled: (rt, args) => rt.setReactionsEnabled(requireBoolean(args.enabled)),
633
737
  set_worktree_settings: (rt, args) => rt.setWorktreeSettings(args.worktree),
634
738
  forget_provider_auth: async (rt, args) => {
@@ -736,6 +840,8 @@ const READ_ONLY_SETUP_ACTIONS = new Set([
736
840
  'search_local_models',
737
841
  'inspect_hf_model',
738
842
  'local_model_details',
843
+ 'read_agent_settings',
844
+ 'read_project_connectors',
739
845
  ]);
740
846
  // Actions that start or steer work rather than saving a setting.
741
847
  const BACKGROUND_SETUP_ACTIONS = new Set([
@@ -744,6 +850,10 @@ const BACKGROUND_SETUP_ACTIONS = new Set([
744
850
  'cancel_local_installation',
745
851
  'reconnect_mcp',
746
852
  'run_automation',
853
+ 'reset_route_fallback',
854
+ 'continue_in_model',
855
+ 'schedule_quota_resume',
856
+ 'cancel_quota_resume',
747
857
  ]);
748
858
 
749
859
  function setupMutationReceipt(action, result) {
@@ -768,6 +878,7 @@ export function createSetupToolExecutor({
768
878
  getSessionId,
769
879
  getCallerSession,
770
880
  flushSettings,
881
+ executeSessionControl = null,
771
882
  }) {
772
883
  const desktop = createSetupUiRequests({ notifySessionUi, getSessionId });
773
884
  const api = () => {
@@ -776,11 +887,11 @@ export function createSetupToolExecutor({
776
887
  return facade;
777
888
  };
778
889
 
779
- async function readStatus(domain, requestDesktop, detail) {
890
+ async function readStatus(domain, requestDesktop, detail, caller = null) {
780
891
  const rt = api();
781
892
  if (SETUP_DESKTOP_DOMAINS.includes(domain)) return requestDesktop({ action: 'status', domain });
782
893
  if (!Object.hasOwn(SETUP_STATUS_READERS, domain)) throw new Error(`setup: unknown status domain "${domain}"`);
783
- return SETUP_STATUS_READERS[domain](rt, { domain, detail, getConfig: () => getConfig?.() || {} });
894
+ return SETUP_STATUS_READERS[domain](rt, { domain, detail, caller, getConfig: () => getConfig?.() || {} });
784
895
  }
785
896
 
786
897
  function openSurface(target) {
@@ -809,15 +920,16 @@ export function createSetupToolExecutor({
809
920
  // it never creates agents, definitions or automations, nor changes any
810
921
  // other setting. Hosts without session provenance (no getCallerSession)
811
922
  // skip this gate; automation actions still refuse an unknown caller.
923
+ let scope = null;
812
924
  if (getCallerSession && !READ_ONLY_SETUP_ACTIONS.has(action)) {
813
- const scope = scheduleCallerScope(caller);
925
+ scope = scheduleCallerScope(caller);
814
926
  assertInteractiveCaller(scope, action);
815
927
  // Agent definitions (contacts) change only from Mixy; another agent's
816
928
  // home changes only its own agent settings.
817
929
  if (AGENT_DEFINITION_ACTIONS.has(action) && args.definitionKind === 'agent') {
818
930
  assertAgentEditorAccess(scope, caller, action);
819
931
  }
820
- if (action === 'set_agent_route') assertOwnAgentSettings(scope, args.agent, action);
932
+ if (OWN_AGENT_ACTIONS.has(action)) assertOwnAgentSettings(scope, args.agent, action);
821
933
  }
822
934
  // Mixy's heartbeat session takes only its two routine actions, reads included.
823
935
  assertHeartbeatSetupAction(sessionId, action, args);
@@ -825,7 +937,18 @@ export function createSetupToolExecutor({
825
937
  if (!Object.hasOwn(SETUP_ACTION_HANDLERS, action)) {
826
938
  return executeExtendedSetupAction(rt, args, requestDesktop, { sessionId, caller });
827
939
  }
828
- 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
+ });
829
952
  }
830
953
 
831
954
  return {
@@ -71,7 +71,10 @@ function modelDetail(row) {
71
71
  defaultModelParameters: row.defaultModelParameters,
72
72
  supportsMaxMode: row.supportsMaxMode,
73
73
  };
74
- return { ...Object.fromEntries(Object.entries(detail).filter(([, value]) => isPresent(value))), ...modelPreferenceFields(row) };
74
+ return {
75
+ ...Object.fromEntries(Object.entries(detail).filter(([, value]) => isPresent(value))),
76
+ ...modelPreferenceFields(row),
77
+ };
75
78
  }
76
79
 
77
80
  function findModel(rows, id, provider) {
@@ -420,7 +423,7 @@ export const EXTENDED_SETUP_HANDLERS = Object.freeze({
420
423
  if (own(args, 'model') || own(args, 'accountId')) throw new Error('model and accountId need a lane');
421
424
  return { kind: args.kind, media: await rt.setMediaPin({ kind: args.kind, lane: '' }) };
422
425
  }
423
- const lane =(await rt.listMediaLanes()).find((row) => row.id === args.lane);
426
+ const lane = (await rt.listMediaLanes()).find((row) => row.id === args.lane);
424
427
  if (!lane) throw new Error(`Unknown media lane "${args.lane}"; list lanes with the media tool`);
425
428
  if (!lane.authenticated) throw new Error(`${lane.label} is not signed in; open providers first`);
426
429
  const spec = lane[args.kind];
@@ -431,7 +434,9 @@ export const EXTENDED_SETUP_HANDLERS = Object.freeze({
431
434
  if (own(args, 'accountId')) {
432
435
  if (lane.authType !== 'oauth') throw new Error(`${lane.id} signs in with an API key; it has no accounts`);
433
436
  if (!rt.getProviderAccounts(lane.authProvider).accounts.some((row) => row.id === args.accountId)) {
434
- throw new Error(`Unknown ${lane.authProvider} account "${args.accountId}"; list accounts with status providers`);
437
+ throw new Error(
438
+ `Unknown ${lane.authProvider} account "${args.accountId}"; list accounts with status providers`
439
+ );
435
440
  }
436
441
  }
437
442
  const pin = await rt.setMediaPin({ kind: args.kind, lane: args.lane, model: args.model, account: args.accountId });
@@ -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
+ };
@@ -42,7 +42,7 @@ export const SETUP_EXTENDED_ACTION_FIELDS = Object.freeze({
42
42
  });
43
43
 
44
44
  /** Mirrors RULE_ACTIONS in services/hook-bus/rule-store.mjs. */
45
- export const SETUP_HOOK_RULE_ACTIONS = Object.freeze(['allow', 'deny', 'block', 'modify', 'rewrite', 'ask']);
45
+ const SETUP_HOOK_RULE_ACTIONS = Object.freeze(['allow', 'deny', 'block', 'modify', 'rewrite', 'ask']);
46
46
 
47
47
  /** Mirrors ACTIVITY_RAIL_PIN_IDS in apps/desktop/src/shared/activity-rail-pins.ts. */
48
48
  export const SETUP_ACTIVITY_RAIL_PIN_IDS = Object.freeze([
@@ -224,7 +224,6 @@ export const SETUP_EXTENDED_PROPERTIES = Object.freeze({
224
224
  desktop: object({
225
225
  keepAwake: boolean,
226
226
  runInBackground: boolean,
227
- turnNotifications: boolean,
228
227
  usagePinned: boolean,
229
228
  usageRailShortLong: boolean,
230
229
  usageAccountsExpanded: boolean,
@@ -232,6 +231,7 @@ export const SETUP_EXTENDED_PROPERTIES = Object.freeze({
232
231
  usageRailSlotsAll: boolean,
233
232
  computerObserveOnly: boolean,
234
233
  browserAutoSignIn: boolean,
234
+ saveChatLogins: boolean,
235
235
  notificationVolume: {
236
236
  type: 'integer',
237
237
  minimum: 0,
@@ -273,7 +273,8 @@ export const SETUP_EXTENDED_PROPERTIES = Object.freeze({
273
273
  sidePanelPosition: {
274
274
  type: 'string',
275
275
  enum: ['right', 'bottom', 'auto'],
276
- description: 'Where the side dock sits on desktop: beside the chat, below it, or by window shape (default right).',
276
+ description:
277
+ 'Where the side dock sits on desktop: beside the chat, below it, or by window shape (default right).',
277
278
  },
278
279
  timeFormat: {
279
280
  type: 'string',