mixdog 1.3.1 → 1.3.3

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 (468) hide show
  1. package/README.md +8 -4
  2. package/package.json +8 -8
  3. package/scripts/native-release-status.mjs +238 -0
  4. package/scripts/verify-runtime-dependency-versions.mjs +158 -0
  5. package/src/agents/reviewer/AGENT.md +5 -7
  6. package/src/agents/reviewer/agent.json +1 -1
  7. package/src/defaults/shipped-history.json +9 -1
  8. package/src/defaults/skills/browser-use/SKILL.md +6 -3
  9. package/src/defaults/skills/local-provider/SKILL.md +33 -6
  10. package/src/defaults/skills/pptx/scripts/qc-pages.mjs +3 -2
  11. package/src/defaults/skills/setup/SKILL.md +23 -1
  12. package/src/defaults/skills/setup/references/actions.md +50 -3
  13. package/src/defaults/skills/setup/references/surfaces.md +23 -3
  14. package/src/defaults/skills/setup-guide/SKILL.md +7 -3
  15. package/src/defaults/skills/visualize/SKILL.md +9 -6
  16. package/src/defaults/skills/visualize/references/craft.md +8 -1
  17. package/src/help.mjs +1 -1
  18. package/src/lib/keychain-cjs.cjs +4 -1
  19. package/src/lib/rules-builder.cjs +14 -7
  20. package/src/rules/agent-mode/AGENT_MODE.md +8 -5
  21. package/src/rules/agent-mode/MIXY.md +6 -8
  22. package/src/rules/lead/LEAD.md +7 -10
  23. package/src/rules/shared/10-tool-workflow.md +4 -0
  24. package/src/rules/shared/20-research.md +2 -0
  25. package/src/rules/shared/45-destructive.md +5 -1
  26. package/src/rules/shared/60-verification.md +6 -4
  27. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +5 -0
  28. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/session-prep.mjs +8 -0
  29. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +6 -1
  30. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +114 -26
  31. package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +8 -0
  32. package/src/runtime/agent/orchestrator/agent-runtime/room-supervisor-completion.mjs +5 -4
  33. package/src/runtime/agent/orchestrator/agent-trace.mjs +3 -0
  34. package/src/runtime/agent/orchestrator/cache-break-trace.mjs +4 -0
  35. package/src/runtime/agent/orchestrator/config-presets.mjs +2 -0
  36. package/src/runtime/agent/orchestrator/config-storage.mjs +10 -3
  37. package/src/runtime/agent/orchestrator/config.mjs +11 -0
  38. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +9 -2
  39. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +97 -5
  40. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +6 -10
  41. package/src/runtime/agent/orchestrator/mcp/client.mjs +7 -4
  42. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +84 -32
  43. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +38 -11
  44. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +15 -3
  45. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +1 -1
  46. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  47. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +84 -42
  48. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -0
  49. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +14 -1
  50. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +6 -2
  51. package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +27 -8
  52. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  53. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +31 -4
  54. package/src/runtime/agent/orchestrator/providers/cursor.mjs +4 -1
  55. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +14 -1
  56. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +11 -8
  57. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  58. package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -0
  59. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +16 -0
  60. package/src/runtime/agent/orchestrator/providers/lib/tail-reminder-split.mjs +13 -0
  61. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +16 -1
  62. package/src/runtime/agent/orchestrator/providers/mixdog-local.mjs +3 -1
  63. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +1 -0
  64. package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +5 -5
  65. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +9 -5
  66. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +9 -1
  67. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +2 -1
  68. package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +30 -3
  69. package/src/runtime/agent/orchestrator/providers/openai-model-capabilities.mjs +1 -1
  70. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +3 -1
  71. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +61 -8
  72. package/src/runtime/agent/orchestrator/providers/registry.mjs +19 -0
  73. package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +13 -1
  74. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +90 -24
  75. package/src/runtime/agent/orchestrator/runtime-core/fs-utils.mjs +8 -1
  76. package/src/runtime/agent/orchestrator/runtime-core/goal-mutations.mjs +1 -1
  77. package/src/runtime/agent/orchestrator/runtime-core/goal-runtime.mjs +5 -0
  78. package/src/runtime/agent/orchestrator/runtime-core/goal-storage.mjs +68 -22
  79. package/src/runtime/agent/orchestrator/runtime-core/goal-tool-exec.mjs +1 -1
  80. package/src/runtime/agent/orchestrator/runtime-core/goal-turns.mjs +10 -1
  81. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +4 -6
  82. package/src/runtime/agent/orchestrator/runtime-core/route-context-state.mjs +81 -0
  83. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +2 -1
  84. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +52 -6
  85. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +35 -13
  86. package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +7 -6
  87. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +1 -1
  88. package/src/runtime/agent/orchestrator/runtime-core/workflow/packs.mjs +8 -4
  89. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +8 -3
  90. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +1 -1
  91. package/src/runtime/agent/orchestrator/session/agent-mode.mjs +0 -6
  92. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.d.mts +1 -1
  93. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.mjs +22 -4
  94. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +30 -2
  95. package/src/runtime/agent/orchestrator/session/agent-room.mjs +196 -45
  96. package/src/runtime/agent/orchestrator/session/attachment-preflight.mjs +80 -0
  97. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
  98. package/src/runtime/agent/orchestrator/session/compact/continuation.mjs +15 -6
  99. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +45 -19
  100. package/src/runtime/agent/orchestrator/session/compact/forked-summary.mjs +3 -0
  101. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +44 -0
  102. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +185 -37
  103. package/src/runtime/agent/orchestrator/session/compact/summary-mode.mjs +16 -0
  104. package/src/runtime/agent/orchestrator/session/compact/summary-route.mjs +87 -0
  105. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +2 -0
  106. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +6 -2
  107. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +4 -1
  108. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +1 -1
  109. package/src/runtime/agent/orchestrator/session/context-utils.mjs +41 -10
  110. package/src/runtime/agent/orchestrator/session/human-gate/delegation.mjs +240 -0
  111. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +102 -35
  112. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +20 -0
  113. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +19 -9
  114. package/src/runtime/agent/orchestrator/session/human-gate/run-policy.mjs +73 -0
  115. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +55 -27
  116. package/src/runtime/agent/orchestrator/session/human-gate/unattended-port.mjs +15 -0
  117. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +4 -1
  118. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +199 -109
  119. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +5 -0
  120. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/continuation.mjs +2 -0
  121. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/empty-nudge.mjs +1 -1
  122. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/output-limit.mjs +62 -13
  123. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +22 -6
  124. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +6 -2
  125. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +5 -5
  126. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +10 -0
  127. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +3 -0
  128. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +33 -8
  129. package/src/runtime/agent/orchestrator/session/loop/tool-exec/call-prep.mjs +5 -0
  130. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +27 -0
  131. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +2 -1
  132. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +12 -0
  133. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +11 -5
  134. package/src/runtime/agent/orchestrator/session/manager/agent-listing-notice.mjs +102 -0
  135. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +2 -0
  136. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +13 -1
  137. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +13 -2
  138. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +27 -14
  139. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +14 -0
  140. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
  141. package/src/runtime/agent/orchestrator/session/manager/ask-turn-steering-drain.mjs +1 -0
  142. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +44 -18
  143. package/src/runtime/agent/orchestrator/session/manager/idle-cleanup.mjs +32 -1
  144. package/src/runtime/agent/orchestrator/session/manager/orchestration-mode-switch-notice.mjs +115 -0
  145. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +42 -0
  146. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +6 -0
  147. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +3 -9
  148. package/src/runtime/agent/orchestrator/session/manager/pending-spool-file.mjs +1 -1
  149. package/src/runtime/agent/orchestrator/session/manager/pending-spool-shard.mjs +5 -3
  150. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +36 -0
  151. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +40 -1
  152. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +12 -0
  153. package/src/runtime/agent/orchestrator/session/manager/runtime-state.mjs +20 -0
  154. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +28 -9
  155. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +55 -27
  156. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +4 -9
  157. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +14 -1
  158. package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +7 -3
  159. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +6 -15
  160. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +24 -8
  161. package/src/runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs +94 -13
  162. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +52 -11
  163. package/src/runtime/agent/orchestrator/session/provider-prefix-guard.mjs +21 -3
  164. package/src/runtime/agent/orchestrator/session/routine-result.d.mts +18 -3
  165. package/src/runtime/agent/orchestrator/session/routine-result.mjs +126 -12
  166. package/src/runtime/agent/orchestrator/session/runtime-user-context.mjs +3 -1
  167. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +16 -3
  168. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +3 -1
  169. package/src/runtime/agent/orchestrator/session/store/canonical-reader.mjs +41 -7
  170. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +10 -2
  171. package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +14 -2
  172. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +15 -1
  173. package/src/runtime/agent/orchestrator/session/store/sweep/stale-sweep.mjs +5 -4
  174. package/src/runtime/agent/orchestrator/session/store/sweep/sub-agent-retention.mjs +140 -0
  175. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-plan.mjs +5 -0
  176. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-record.mjs +15 -5
  177. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +49 -1
  178. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +80 -1
  179. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +40 -5
  180. package/src/runtime/agent/orchestrator/session/store-summary-last-message.mjs +47 -2
  181. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +33 -1
  182. package/src/runtime/agent/orchestrator/session/store-summary-visibility.d.mts +5 -0
  183. package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +37 -0
  184. package/src/runtime/agent/orchestrator/session/store.mjs +1 -0
  185. package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +125 -0
  186. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +4 -0
  187. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +78 -43
  188. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +35 -5
  189. package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +42 -2
  190. package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +47 -14
  191. package/src/runtime/agent/orchestrator/tools/builtin/atomic-write.mjs +25 -5
  192. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +6 -3
  193. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +13 -14
  194. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/transport-artifacts.mjs +9 -0
  195. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +19 -14
  196. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +7 -1
  197. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +127 -20
  198. package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +557 -0
  199. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +10 -12
  200. package/src/runtime/agent/orchestrator/tools/builtin/pdf-test-fixtures.mjs +5 -3
  201. package/src/runtime/agent/orchestrator/tools/builtin/read-image.mjs +4 -2
  202. package/src/runtime/agent/orchestrator/tools/builtin/read-single-media.mjs +47 -2
  203. package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +264 -87
  204. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +10 -4
  205. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +42 -23
  206. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +8 -4
  207. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +9 -30
  208. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +24 -12
  209. package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +3 -2
  210. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +6 -2
  211. package/src/runtime/agent/orchestrator/tools/patch/delete-quarantine.mjs +2 -1
  212. package/src/runtime/agent/orchestrator/tools/patch/replay-capture.mjs +4 -2
  213. package/src/runtime/agent/orchestrator/tools/patch/rollback-state.mjs +33 -2
  214. package/src/runtime/agent/orchestrator/tools/patch-manifest.json +13 -13
  215. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +5 -3
  216. package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +3 -1
  217. package/src/runtime/attachments/attachment-reference.mjs +155 -0
  218. package/src/runtime/attachments/legacy-office.mjs +110 -0
  219. package/src/runtime/attachments/limits.mjs +5 -1
  220. package/src/runtime/attachments/office-extract.mjs +3 -6
  221. package/src/runtime/attachments/pdf-extract.mjs +84 -10
  222. package/src/runtime/attachments/prompt-restore.mjs +70 -0
  223. package/src/runtime/attachments/store.mjs +109 -33
  224. package/src/runtime/browser-bridge/client.mjs +73 -20
  225. package/src/runtime/channels/lib/runtime-paths.mjs +1 -1
  226. package/src/runtime/channels/lib/scheduler.mjs +14 -7
  227. package/src/runtime/channels/lib/state-file.mjs +15 -15
  228. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +14 -13
  229. package/src/runtime/channels/lib/webhook/relay-tunnel.mjs +22 -18
  230. package/src/runtime/channels/lib/webhook/session-dispatch.mjs +14 -36
  231. package/src/runtime/channels/lib/worker-main.mjs +3 -2
  232. package/src/runtime/effort-judge/compact-tokenizer.mjs +8 -2
  233. package/src/runtime/effort-judge/judge-client.mjs +66 -16
  234. package/src/runtime/effort-judge/model-install.mjs +47 -7
  235. package/src/runtime/local-provider/asset-installer.mjs +10 -4
  236. package/src/runtime/local-provider/catalog.mjs +5 -2
  237. package/src/runtime/local-provider/registered-models.mjs +64 -27
  238. package/src/runtime/local-provider/server.mjs +2 -3
  239. package/src/runtime/media/adapters/image-api.mjs +89 -0
  240. package/src/runtime/media/auth.mjs +64 -1
  241. package/src/runtime/media/catalog.mjs +66 -4
  242. package/src/runtime/media/jobs.mjs +4 -0
  243. package/src/runtime/media/lanes.mjs +46 -5
  244. package/src/runtime/media/media-usage.mjs +17 -0
  245. package/src/runtime/media/renditions.mjs +15 -15
  246. package/src/runtime/media/store.mjs +36 -35
  247. package/src/runtime/media/tool.mjs +8 -1
  248. package/src/runtime/memory/lib/cycle-scheduler/health-ledger.mjs +8 -2
  249. package/src/runtime/memory/lib/embedding-model-files.mjs +30 -7
  250. package/src/runtime/memory/lib/ingest/transcript-tail.mjs +16 -7
  251. package/src/runtime/memory/lib/memory-chunk-quality.mjs +14 -2
  252. package/src/runtime/memory/lib/pg/process.mjs +8 -4
  253. package/src/runtime/memory/lib/project-id-resolver.mjs +5 -1
  254. package/src/runtime/memory/lib/runtime-fetcher.mjs +6 -1
  255. package/src/runtime/office/core/journal.mjs +7 -14
  256. package/src/runtime/office/design/library/design-library-core.mjs +4 -16
  257. package/src/runtime/office/pdf/ocr-engine-worker.mjs +37 -0
  258. package/src/runtime/office/pdf/ocr-engine.mjs +146 -0
  259. package/src/runtime/office/pdf/pdf-ocr.mjs +51 -21
  260. package/src/runtime/office/pdf/pdf-render.mjs +30 -18
  261. package/src/runtime/office/portable/ooxml-validator.mjs +2 -2
  262. package/src/runtime/office/portable/portable-soffice.mjs +135 -29
  263. package/src/runtime/shared/agent-avatar-sprites.mjs +23 -8
  264. package/src/runtime/shared/agent-avatar.mjs +3 -2
  265. package/src/runtime/shared/agent-kind-migration.mjs +200 -0
  266. package/src/runtime/shared/agent-message-envelope.d.mts +6 -0
  267. package/src/runtime/shared/agent-message-envelope.mjs +44 -0
  268. package/src/runtime/shared/agent-reap-state.d.mts +4 -0
  269. package/src/runtime/shared/agent-room-host.d.mts +31 -0
  270. package/src/runtime/shared/agent-room-host.mjs +58 -0
  271. package/src/runtime/shared/agent-store.mjs +24 -7
  272. package/src/runtime/shared/agent-task-starts.mjs +27 -0
  273. package/src/runtime/shared/atomic-file.d.mts +50 -0
  274. package/src/runtime/shared/atomic-file.mjs +394 -132
  275. package/src/runtime/shared/background-tasks.mjs +7 -4
  276. package/src/runtime/shared/bot-hops.mjs +35 -0
  277. package/src/runtime/shared/bot-split-migration.mjs +1 -1
  278. package/src/runtime/shared/bounded-download.mjs +4 -2
  279. package/src/runtime/shared/config.mjs +39 -9
  280. package/src/runtime/shared/defaults-separation.mjs +6 -2
  281. package/src/runtime/shared/delegated-approval-host.mjs +74 -0
  282. package/src/runtime/shared/err-text.mjs +4 -0
  283. package/src/runtime/shared/error-presentation.mjs +22 -4
  284. package/src/runtime/shared/file-has-data.mjs +28 -0
  285. package/src/runtime/shared/llm/usage-ledger-import.mjs +37 -18
  286. package/src/runtime/shared/pending-agent-work.mjs +54 -0
  287. package/src/runtime/shared/process-lifecycle.mjs +8 -12
  288. package/src/runtime/shared/projects.mjs +50 -41
  289. package/src/runtime/shared/provider-accounts.mjs +17 -12
  290. package/src/runtime/shared/redact-directories.mjs +18 -0
  291. package/src/runtime/shared/remove-directory.mjs +28 -0
  292. package/src/runtime/shared/routine-trigger.d.mts +6 -1
  293. package/src/runtime/shared/routine-trigger.mjs +14 -6
  294. package/src/runtime/shared/run-child.d.mts +3 -0
  295. package/src/runtime/shared/run-child.mjs +24 -3
  296. package/src/runtime/shared/session-baseline-store.mjs +8 -2
  297. package/src/runtime/shared/session-sidebar-host.d.mts +11 -2
  298. package/src/runtime/shared/session-sidebar-host.mjs +16 -3
  299. package/src/runtime/shared/singleton-owner.mjs +7 -6
  300. package/src/runtime/shared/skill-tool-dependencies.mjs +4 -10
  301. package/src/runtime/shared/staged-update.mjs +4 -2
  302. package/src/runtime/shared/statusline/statusline-segments.mjs +4 -15
  303. package/src/runtime/shared/tool-execution-contract.mjs +10 -0
  304. package/src/runtime/shared/tool-work-units.mjs +12 -9
  305. package/src/runtime/shared/transcript-metadata.mjs +28 -3
  306. package/src/runtime/shared/transcript-writer/session-record.mjs +7 -6
  307. package/src/runtime/shared/turn-snapshot-store.mjs +5 -4
  308. package/src/runtime/shared/turn-snapshot.mjs +277 -124
  309. package/src/runtime/shared/user-cwd.mjs +9 -4
  310. package/src/runtime/shared/user-data-guard.mjs +92 -61
  311. package/src/runtime/shared/utf8-cut.mjs +10 -0
  312. package/src/runtime/shared/worktrees/activity-gate.mjs +251 -61
  313. package/src/runtime/shared/worktrees/contract.mjs +11 -3
  314. package/src/runtime/shared/worktrees/maintenance.mjs +11 -2
  315. package/src/runtime/shared/worktrees/manager.mjs +419 -52
  316. package/src/runtime/shared/worktrees/registry.mjs +29 -27
  317. package/src/runtime/shared/worktrees/tool-host.mjs +39 -0
  318. package/src/runtime/tidy/core-install.mjs +26 -4
  319. package/src/runtime/tidy/install.mjs +91 -23
  320. package/src/runtime/tidy/process.mjs +41 -8
  321. package/src/runtime/tidy/resolve.mjs +10 -7
  322. package/src/runtime/web-search/lib/config.mjs +4 -16
  323. package/src/session-runtime/agent-home-delivery.mjs +542 -204
  324. package/src/session-runtime/agent-profile-changes.mjs +16 -0
  325. package/src/session-runtime/agent-room-delivery.mjs +81 -35
  326. package/src/session-runtime/agent-task-sessions.mjs +811 -179
  327. package/src/session-runtime/agent-tool-routing.mjs +1 -0
  328. package/src/session-runtime/boot/apis.mjs +48 -24
  329. package/src/session-runtime/boot/config.mjs +1 -0
  330. package/src/session-runtime/boot/facade.mjs +16 -7
  331. package/src/session-runtime/boot/infrastructure.mjs +14 -2
  332. package/src/session-runtime/boot/providers.mjs +2 -2
  333. package/src/session-runtime/boot/shared.mjs +12 -6
  334. package/src/session-runtime/boot/tools.mjs +23 -3
  335. package/src/session-runtime/bot-status.mjs +432 -0
  336. package/src/session-runtime/config-lifecycle/lead-route-sync.mjs +29 -0
  337. package/src/session-runtime/config-lifecycle.mjs +60 -55
  338. package/src/session-runtime/cwd-plugins/core-memory-context.mjs +2 -4
  339. package/src/session-runtime/held-requests.mjs +31 -33
  340. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +10 -1
  341. package/src/session-runtime/internal-tool-executor/feature-tools.mjs +2 -2
  342. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +24 -26
  343. package/src/session-runtime/lifecycle/session-catalog.mjs +12 -2
  344. package/src/session-runtime/lifecycle/teardown.mjs +11 -3
  345. package/src/session-runtime/mcp-glue/connect.mjs +100 -186
  346. package/src/session-runtime/mcp-glue/initial-connect-gate.mjs +57 -0
  347. package/src/session-runtime/mcp-glue/server-connection.mjs +71 -0
  348. package/src/session-runtime/media-api.mjs +5 -2
  349. package/src/session-runtime/model-recency.mjs +1 -0
  350. package/src/session-runtime/model-route/route-tuning.mjs +4 -1
  351. package/src/session-runtime/model-route/set-route.mjs +61 -1
  352. package/src/session-runtime/owned-automation-run.mjs +8 -1
  353. package/src/session-runtime/provider-models/row-hydration.mjs +8 -1
  354. package/src/session-runtime/quick-model-rows/model-meta.mjs +2 -0
  355. package/src/session-runtime/quick-model-rows/provider-rows.mjs +7 -1
  356. package/src/session-runtime/route-resolution.mjs +16 -8
  357. package/src/session-runtime/route-state.mjs +1 -59
  358. package/src/session-runtime/routine-home-turn.mjs +5 -3
  359. package/src/session-runtime/runtime-bootstrap.mjs +5 -3
  360. package/src/session-runtime/runtime-paths.mjs +11 -5
  361. package/src/session-runtime/runtime-review-api.mjs +4 -1
  362. package/src/session-runtime/runtime-tunables.mjs +6 -0
  363. package/src/session-runtime/services/agent-tool/close-flow/close-all.mjs +11 -2
  364. package/src/session-runtime/services/agent-tool/close-flow.mjs +4 -2
  365. package/src/session-runtime/services/agent-tool/execute-send.mjs +1 -0
  366. package/src/session-runtime/services/agent-tool/execute-spawn.mjs +1 -0
  367. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +34 -19
  368. package/src/session-runtime/services/agent-tool/restart-notify.mjs +10 -0
  369. package/src/session-runtime/services/agent-tool/send-flow.mjs +25 -21
  370. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +7 -0
  371. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +6 -3
  372. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +8 -5
  373. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +22 -2
  374. package/src/session-runtime/services/agent-tool/worker-index.mjs +4 -6
  375. package/src/session-runtime/services/agent-tool.mjs +6 -0
  376. package/src/session-runtime/services/daemon-crash-capture/daemon-exit-record.mjs +1 -1
  377. package/src/session-runtime/services/daemon-crash-capture/retention.mjs +4 -3
  378. package/src/session-runtime/services/hook-bus/config-loader.mjs +14 -5
  379. package/src/session-runtime/services/hook-bus/rule-file.mjs +49 -11
  380. package/src/session-runtime/services/plugin-admin.mjs +26 -26
  381. package/src/session-runtime/services/provider-admin.mjs +26 -6
  382. package/src/session-runtime/services/schedule-access.mjs +33 -4
  383. package/src/session-runtime/services/seeds.mjs +4 -3
  384. package/src/session-runtime/services/usage-dashboard.mjs +1 -1
  385. package/src/session-runtime/services/usage-stats-model.mjs +2 -1
  386. package/src/session-runtime/services/worktree-access.mjs +27 -0
  387. package/src/session-runtime/session-create/materialize.mjs +2 -3
  388. package/src/session-runtime/session-create/session-options.mjs +15 -2
  389. package/src/session-runtime/session-hooks.mjs +9 -17
  390. package/src/session-runtime/settings-builtin-tools-api.mjs +65 -1
  391. package/src/session-runtime/settings-compaction-api.mjs +86 -39
  392. package/src/session-runtime/setup-tool/executor.mjs +12 -2
  393. package/src/session-runtime/setup-tool/settings-contract.mjs +34 -1
  394. package/src/session-runtime/setup-tool/tool-defs.mjs +20 -3
  395. package/src/session-runtime/skills-api.mjs +12 -5
  396. package/src/session-runtime/tool-policy/agent-profile-fold.mjs +349 -0
  397. package/src/session-runtime/tool-policy/mode-switch.mjs +54 -0
  398. package/src/session-runtime/tool-policy/pending-switches.mjs +59 -0
  399. package/src/session-runtime/tool-policy/policy-stage.mjs +183 -0
  400. package/src/session-runtime/tool-policy/session-switches.mjs +169 -0
  401. package/src/session-runtime/tool-policy/surface-helpers.mjs +114 -0
  402. package/src/session-runtime/tool-policy/workflow-overlay.mjs +145 -0
  403. package/src/session-runtime/tool-policy-refresh.mjs +48 -369
  404. package/src/session-runtime/tool-surface.mjs +10 -4
  405. package/src/session-runtime/turn/session-ops.mjs +8 -0
  406. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +95 -88
  407. package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
  408. package/src/session-runtime/turn/turn-run.mjs +7 -2
  409. package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +54 -7
  410. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +527 -333
  411. package/src/session-runtime/workflow-agents-api/agent-route.mjs +85 -63
  412. package/src/session-runtime/workflow-agents-api/shared.mjs +5 -1
  413. package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +14 -1
  414. package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +6 -3
  415. package/src/session-runtime/workspace/transition-steps.mjs +1 -18
  416. package/src/session-runtime/workspace/transitions.mjs +279 -67
  417. package/src/session-runtime/workspace/workspace-state.mjs +7 -6
  418. package/src/session-runtime/workspace/worktree-tool.mjs +194 -0
  419. package/src/standalone/daemon-agent-control.mjs +4 -1
  420. package/src/standalone/daemon-lag-profiler.mjs +3 -1
  421. package/src/standalone/daemon-session-control.mjs +173 -15
  422. package/src/standalone/daemon-telemetry.mjs +9 -0
  423. package/src/standalone/daemon.mjs +33 -10
  424. package/src/standalone/local-session-runtime.mjs +33 -16
  425. package/src/standalone/session-client.mjs +7 -2
  426. package/src/standalone/session-protocol.mjs +8 -0
  427. package/src/standalone/session-runtime-inline-host/boot-loaders.mjs +2 -0
  428. package/src/standalone/session-service/agent-route-follow.mjs +69 -0
  429. package/src/standalone/session-service/session-calls/session-turns.mjs +33 -4
  430. package/src/standalone/session-service.mjs +21 -0
  431. package/src/standalone/session-state-projection.mjs +19 -9
  432. package/src/standalone/session-tool-output.mjs +36 -9
  433. package/src/standalone/staged-import.mjs +47 -0
  434. package/src/tui/App.jsx +2 -0
  435. package/src/tui/app/maintenance-pickers/auto-clear-picker.mjs +19 -13
  436. package/src/tui/app/message-selector.mjs +15 -2
  437. package/src/tui/app/model-options.mjs +16 -2
  438. package/src/tui/app/model-picker.mjs +2 -1
  439. package/src/tui/app/route-pickers.mjs +3 -1
  440. package/src/tui/app/settings-picker/settings-rows.mjs +2 -2
  441. package/src/tui/app/slash-commands.mjs +1 -1
  442. package/src/tui/app/usage-context-panels/context-model.mjs +8 -2
  443. package/src/tui/dist/index.mjs +617 -369
  444. package/src/tui/paste-attachments.mjs +0 -1
  445. package/src/tui/paste-text-policy.mjs +4 -5
  446. package/src/tui/prompt-history-store.mjs +16 -21
  447. package/src/tui/session/agent-job-feed/execution-delivery.mjs +4 -1
  448. package/src/tui/session/context-state.mjs +4 -0
  449. package/src/tui/session/notice-surface.mjs +3 -0
  450. package/src/tui/session/session-api/intake/abort.mjs +52 -18
  451. package/src/tui/session/session-api/intake/rewind.mjs +39 -4
  452. package/src/tui/session/session-api/media.mjs +2 -1
  453. package/src/tui/session/session-api/routes-agent-definitions.mjs +76 -0
  454. package/src/tui/session/session-api/routes-write.mjs +203 -0
  455. package/src/tui/session/session-api/routes.mjs +23 -247
  456. package/src/tui/session/session-api/settings.mjs +14 -1
  457. package/src/tui/session/session-flow/drain.mjs +28 -3
  458. package/src/tui/session/session-flow/failed-turn-rewind.mjs +4 -2
  459. package/src/tui/session/session-flow/queue/take-entries.mjs +8 -0
  460. package/src/tui/session/session-flow/steering.mjs +8 -3
  461. package/src/tui/session/tool-approval.mjs +142 -14
  462. package/src/tui/session/transcript-intake.mjs +2 -0
  463. package/src/tui/session/turn.mjs +23 -14
  464. package/src/tui/transcript-tool-failures.mjs +8 -2
  465. package/src/vendor/statusline/src/gateway/route-meta.mjs +7 -8
  466. package/src/vendor/statusline/src/gateway/session-routes.mjs +6 -7
  467. package/src/rules/agent-mode/ROUTINE.md +0 -6
  468. package/src/session-runtime/room-member-gate.mjs +0 -48
@@ -3,7 +3,9 @@
3
3
  // connect. All mutable progress lives on the caller-owned `state`.
4
4
  import { clean } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
5
5
  import { envFlag } from '../../runtime/shared/env.mjs';
6
+ import { awaitInitialConnect } from './initial-connect-gate.mjs';
6
7
  import { ensurePluginServerData } from './plugin-servers.mjs';
8
+ import { createServerConnection } from './server-connection.mjs';
7
9
 
8
10
  export function createMcpConnect({
9
11
  mcpClient,
@@ -29,58 +31,100 @@ export function createMcpConnect({
29
31
  // Answer it with the redirect URL pasted from the browser.
30
32
  const completeMcpLogin = (name, callbackUrl) => mcpClient.completeMcpLogin(clean(name), callbackUrl, scopeOptions());
31
33
 
32
- // Connect/disconnect exactly one server in the live registry, leaving all
33
- // others untouched. Used by the enable/disable toggle so a single-server
34
- // change never triggers a full disconnectAll()/reconnect freeze. Only an
35
- // explicit sign-in passes `login` ({ openBrowser }): every other connect is
36
- // non-interactive and reports a server that needs one as sign-in required.
37
- async function applyMcpServerConnection(name, enabled, servers = resolveEffectiveMcpServers().servers, login = null) {
38
- const target = clean(name);
39
- if (!target) return;
40
- // Changing this server's state clears any stale failure record for it.
41
- if (Array.isArray(state.mcpFailures)) {
42
- state.mcpFailures = state.mcpFailures.filter((row) => row.name !== target);
43
- }
44
- const cfg = servers[target];
45
- if (enabled === false || !cfg || cfg.enabled === false) {
46
- applied().delete(target);
47
- await mcpClient.disconnectMcpServer?.(target, scopeOptions());
48
- return;
34
+ const { applyMcpServerConnection, reconcileServers } = createServerConnection({
35
+ mcpClient,
36
+ state,
37
+ resolveEffectiveMcpServers,
38
+ getMcpScopeId,
39
+ scopeOptions,
40
+ applied,
41
+ fingerprint,
42
+ syncScopeRoot,
43
+ });
44
+
45
+ // Wait for a prior run (its failures were already captured in state).
46
+ async function settled(prior) {
47
+ if (!prior) return;
48
+ try {
49
+ await prior;
50
+ } catch {
51
+ /* prior run's failures already captured */
49
52
  }
50
- applied().set(target, fingerprint(cfg));
51
- // Close-then-connect as one generation-guarded op: a shutdown landing
52
- // between the close and the connect never resurrects the server.
53
+ }
54
+
55
+ // Register `run` as the in-flight connect so a later reset serializes
56
+ // behind it instead of interleaving disconnect/connect.
57
+ async function trackInFlight(run) {
58
+ state.mcpConnectInFlight = run;
53
59
  try {
54
- ensurePluginServerData(cfg);
55
- await mcpClient.reconnectSingleMcpServer(
56
- getMcpScopeId(),
57
- target,
58
- cfg,
59
- login ? { interactive: true, openBrowser: login.openBrowser } : {}
60
- );
61
- } catch (error) {
62
- const failure = { name: target, msg: error?.message || String(error) };
63
- if (error?.authRequired) failure.authRequired = true;
64
- const failures = Array.isArray(error?.failures) ? error.failures : [failure];
65
- state.mcpFailures = [...(state.mcpFailures || []), ...failures];
60
+ await run;
61
+ } finally {
62
+ if (state.mcpConnectInFlight === run) state.mcpConnectInFlight = null;
66
63
  }
67
64
  }
68
65
 
69
- // Bring the registry in line with the effective set: drop servers that are
70
- // gone or disabled, (re)connect servers that are new or whose resolved
71
- // config changed; untouched servers keep their connection.
72
- async function reconcileServers() {
73
- syncScopeRoot();
74
- const { servers } = resolveEffectiveMcpServers();
75
- const known = applied();
76
- const drops = [...known.keys()].filter((name) => !servers[name] || servers[name].enabled === false);
77
- const connects = Object.entries(servers)
78
- .filter(([name, cfg]) => cfg?.enabled !== false && known.get(name) !== fingerprint(cfg))
79
- .map(([name]) => name);
80
- await Promise.allSettled([
81
- ...drops.map((name) => applyMcpServerConnection(name, false, servers)),
82
- ...connects.map((name) => applyMcpServerConnection(name, true, servers)),
83
- ]);
66
+ // Scoped single-server toggle (or reconcile): non-superseding. It must NEVER
67
+ // cancel a pending full {reset} (cwd-change/boot). So do not bump the
68
+ // generation; just wait for any in-flight run, then bail if a newer full
69
+ // reset has been requested in the meantime.
70
+ async function connectScoped({ only, enabled, reconcile, login }) {
71
+ // A disable/remove must not wait out an interactive login of the
72
+ // connect it queues behind: cancel that login first.
73
+ if (only && enabled === false) cancelMcpLogin(only);
74
+ // Atomically capture the current generation AND the prior in-flight
75
+ // promise in the same synchronous step, then chain our op onto it. No
76
+ // await sits between the capture and the `state.mcpConnectInFlight = run`
77
+ // assignment, so concurrent {only} calls queue FIFO instead of resuming
78
+ // together and clobbering the in-flight slot. We never bump the
79
+ // generation; a {reset} does, so any {only} queued behind a reset sees
80
+ // the newer generation when its turn comes and bails.
81
+ const gen = state.mcpConnectGeneration;
82
+ const prev = state.mcpConnectInFlight;
83
+ await trackInFlight(
84
+ (async () => {
85
+ await settled(prev);
86
+ if (gen !== state.mcpConnectGeneration) return;
87
+ if (reconcile) await reconcileServers();
88
+ else await applyMcpServerConnection(only, enabled, undefined, login);
89
+ })()
90
+ );
91
+ }
92
+
93
+ // Serialize reconnects: boot connect, cwd-change reset, and rapid cwd
94
+ // switches must never interleave their disconnect/connect phases, or an
95
+ // older run finishing after a newer reset could re-add stale servers into
96
+ // the shared client registry. Approach: a generation token + a single
97
+ // in-flight promise. Each call bumps the generation, waits for any prior
98
+ // run to finish, then bails if a newer call has superseded it — leaving the
99
+ // latest requested effective-server-set in the registry.
100
+ async function connectAll({ reset }) {
101
+ const gen = ++state.mcpConnectGeneration;
102
+ await settled(state.mcpConnectInFlight);
103
+ if (gen !== state.mcpConnectGeneration) return;
104
+ await trackInFlight(
105
+ (async () => {
106
+ if (reset) {
107
+ await mcpClient.disconnectAll?.(scopeOptions());
108
+ applied().clear();
109
+ }
110
+ state.mcpFailures = [];
111
+ syncScopeRoot();
112
+ const { servers } = resolveEffectiveMcpServers();
113
+ for (const [name, cfg] of Object.entries(servers)) {
114
+ if (cfg?.enabled === false) applied().delete(name);
115
+ else applied().set(name, fingerprint(cfg));
116
+ }
117
+ if (Object.keys(servers).length === 0) return;
118
+ try {
119
+ for (const cfg of Object.values(servers)) if (cfg?.enabled !== false) ensurePluginServerData(cfg);
120
+ await mcpClient.connectMcpServers(servers, scopeOptions());
121
+ } catch (error) {
122
+ state.mcpFailures = Array.isArray(error?.failures)
123
+ ? error.failures
124
+ : [{ name: 'mcp', msg: error?.message || String(error) }];
125
+ }
126
+ })()
127
+ );
84
128
  }
85
129
 
86
130
  async function connectConfiguredMcp({
@@ -98,146 +142,16 @@ export function createMcpConnect({
98
142
  else await mcpClient.disconnectAll?.(scopeOptions());
99
143
  return mcpStatus();
100
144
  }
101
- // Scoped single-server toggle: non-superseding. It must NEVER cancel a
102
- // pending full {reset} (cwd-change/boot). So do not bump the generation;
103
- // just wait for any in-flight run, then bail if a newer full reset has
104
- // been requested in the meantime. Registering as in-flight makes a later
105
- // reset serialize behind us instead of interleaving disconnect/connect.
106
- if (only || reconcile) {
107
- // A disable/remove must not wait out an interactive login of the
108
- // connect it queues behind: cancel that login first.
109
- if (only && enabled === false) cancelMcpLogin(only);
110
- // Atomically capture the current generation AND the prior in-flight
111
- // promise in the same synchronous step, then chain our op onto it. No
112
- // await sits between the capture and the `state.mcpConnectInFlight = run`
113
- // assignment, so concurrent {only} calls queue FIFO instead of resuming
114
- // together and clobbering the in-flight slot. We never bump the
115
- // generation; a {reset} does, so any {only} queued behind a reset sees
116
- // the newer generation when its turn comes and bails.
117
- const gen = state.mcpConnectGeneration;
118
- const prev = state.mcpConnectInFlight;
119
- const run = (async () => {
120
- if (prev) {
121
- try {
122
- await prev;
123
- } catch {
124
- /* prior run's failures already captured */
125
- }
126
- }
127
- if (gen !== state.mcpConnectGeneration) return;
128
- if (reconcile) await reconcileServers();
129
- else await applyMcpServerConnection(only, enabled, undefined, login);
130
- })();
131
- state.mcpConnectInFlight = run;
132
- try {
133
- await run;
134
- } finally {
135
- if (state.mcpConnectInFlight === run) state.mcpConnectInFlight = null;
136
- }
137
- return mcpStatus();
138
- }
139
- // Serialize reconnects: boot connect, cwd-change reset, and rapid cwd
140
- // switches must never interleave their disconnect/connect phases, or an
141
- // older run finishing after a newer reset could re-add stale servers into
142
- // the shared client registry. Approach: a generation token + a single
143
- // in-flight promise. Each call bumps the generation, waits for any prior
144
- // run to finish, then bails if a newer call has superseded it — leaving the
145
- // latest requested effective-server-set in the registry.
146
- const gen = ++state.mcpConnectGeneration;
147
- if (state.mcpConnectInFlight) {
148
- try {
149
- await state.mcpConnectInFlight;
150
- } catch {
151
- /* prior run's failures already captured */
152
- }
153
- }
154
- if (gen !== state.mcpConnectGeneration) return mcpStatus();
155
- const run = (async () => {
156
- if (reset) {
157
- await mcpClient.disconnectAll?.(scopeOptions());
158
- applied().clear();
159
- }
160
- state.mcpFailures = [];
161
- syncScopeRoot();
162
- const { servers } = resolveEffectiveMcpServers();
163
- for (const [name, cfg] of Object.entries(servers)) {
164
- if (cfg?.enabled === false) applied().delete(name);
165
- else applied().set(name, fingerprint(cfg));
166
- }
167
- if (Object.keys(servers).length === 0) return;
168
- try {
169
- for (const cfg of Object.values(servers)) if (cfg?.enabled !== false) ensurePluginServerData(cfg);
170
- await mcpClient.connectMcpServers(servers, scopeOptions());
171
- } catch (error) {
172
- state.mcpFailures = Array.isArray(error?.failures)
173
- ? error.failures
174
- : [{ name: 'mcp', msg: error?.message || String(error) }];
175
- }
176
- })();
177
- state.mcpConnectInFlight = run;
178
- try {
179
- await run;
180
- } finally {
181
- if (state.mcpConnectInFlight === run) state.mcpConnectInFlight = null;
182
- }
145
+ if (only || reconcile) await connectScoped({ only, enabled, reconcile, login });
146
+ else await connectAll({ reset });
183
147
  return mcpStatus();
184
148
  }
185
149
 
186
- // Turn gate: await the in-flight INITIAL connect, bounded by both the global
187
- // server startup budget and the caller's TTFT grace. A server still
188
- // connecting after the grace flows through the existing late-tool deferred
189
- // announcement path unchanged.
190
- async function awaitInitialMcpConnect(maxWaitMs = undefined) {
191
- const inFlight = state.mcpConnectInFlight;
192
- if (!inFlight) return;
193
- let budgetMs = 10000;
194
- try {
195
- // The gate must outlast the slowest enabled server's own startup budget;
196
- // a server with its budget disabled (0) means the connect may never
197
- // settle, so the whole gate is disabled (handled below).
198
- const configs = Object.values(resolveEffectiveMcpServers().servers).filter((cfg) => cfg?.enabled !== false);
199
- const budgets = (configs.length > 0 ? configs : [{}])
200
- .map((cfg) => mcpClient.resolveMcpStartupTimeoutMs?.(cfg))
201
- .filter((value) => Number.isFinite(value));
202
- if (budgets.length > 0) budgetMs = budgets.includes(0) ? 0 : Math.max(...budgets);
203
- } catch {
204
- /* fall back to default budget */
205
- }
206
- if (maxWaitMs !== null && maxWaitMs !== undefined) {
207
- const requestedMaxWaitMs = Number(maxWaitMs);
208
- if (Number.isFinite(requestedMaxWaitMs) && requestedMaxWaitMs >= 0) {
209
- budgetMs = Math.min(budgetMs, requestedMaxWaitMs);
210
- }
211
- }
212
- // Budget disabled (0/off) = no per-server startup timeout, so the connect
213
- // promise may never settle; never gate the turn on it — fall back to the
214
- // legacy fire-and-forget behavior (late servers use the deferred path).
215
- if (!(budgetMs > 0)) return;
216
- let timer = null;
217
- let expired = false;
218
- const budget = new Promise((resolveBudget) => {
219
- timer = setTimeout(() => {
220
- expired = true;
221
- resolveBudget();
222
- }, budgetMs);
223
- });
224
- try {
225
- // Follow replacement connects (a reconnect queued behind the captured
226
- // run installs a new in-flight promise) inside ONE fixed deadline.
227
- // Failures are already captured in state.mcpFailures, so rejections are
228
- // swallowed: this gate must never reject the turn.
229
- let current = inFlight;
230
- while (current && !expired) {
231
- await Promise.race([Promise.resolve(current).catch(() => {}), budget]);
232
- if (expired) break;
233
- // Let a queued reconnect install its promise before re-checking.
234
- await Promise.race([new Promise((resolveTick) => setImmediate(resolveTick)), budget]);
235
- current = state.mcpConnectInFlight;
236
- }
237
- } finally {
238
- if (timer) clearTimeout(timer);
239
- }
240
- }
241
-
242
- return { connectConfiguredMcp, awaitInitialMcpConnect, cancelMcpLogin, completeMcpLogin };
150
+ return {
151
+ connectConfiguredMcp,
152
+ awaitInitialMcpConnect: (maxWaitMs) =>
153
+ awaitInitialConnect({ state, mcpClient, resolveEffectiveMcpServers }, maxWaitMs),
154
+ cancelMcpLogin,
155
+ completeMcpLogin,
156
+ };
243
157
  }
@@ -0,0 +1,57 @@
1
+ // The turn gate on the initial MCP connect: a bounded wait, never a rejection.
2
+
3
+ // Turn gate: await the in-flight INITIAL connect, bounded by both the global
4
+ // server startup budget and the caller's TTFT grace. A server still
5
+ // connecting after the grace flows through the existing late-tool deferred
6
+ // announcement path unchanged.
7
+ export async function awaitInitialConnect({ state, mcpClient, resolveEffectiveMcpServers }, maxWaitMs = undefined) {
8
+ const inFlight = state.mcpConnectInFlight;
9
+ if (!inFlight) return;
10
+ let budgetMs = 10000;
11
+ try {
12
+ // The gate must outlast the slowest enabled server's own startup budget;
13
+ // a server with its budget disabled (0) means the connect may never
14
+ // settle, so the whole gate is disabled (handled below).
15
+ const configs = Object.values(resolveEffectiveMcpServers().servers).filter((cfg) => cfg?.enabled !== false);
16
+ const budgets = (configs.length > 0 ? configs : [{}])
17
+ .map((cfg) => mcpClient.resolveMcpStartupTimeoutMs?.(cfg))
18
+ .filter((value) => Number.isFinite(value));
19
+ if (budgets.length > 0) budgetMs = budgets.includes(0) ? 0 : Math.max(...budgets);
20
+ } catch {
21
+ /* fall back to default budget */
22
+ }
23
+ if (maxWaitMs !== null && maxWaitMs !== undefined) {
24
+ const requestedMaxWaitMs = Number(maxWaitMs);
25
+ if (Number.isFinite(requestedMaxWaitMs) && requestedMaxWaitMs >= 0) {
26
+ budgetMs = Math.min(budgetMs, requestedMaxWaitMs);
27
+ }
28
+ }
29
+ // Budget disabled (0/off) = no per-server startup timeout, so the connect
30
+ // promise may never settle; never gate the turn on it — fall back to the
31
+ // legacy fire-and-forget behavior (late servers use the deferred path).
32
+ if (!(budgetMs > 0)) return;
33
+ let timer = null;
34
+ let expired = false;
35
+ const budget = new Promise((resolveBudget) => {
36
+ timer = setTimeout(() => {
37
+ expired = true;
38
+ resolveBudget();
39
+ }, budgetMs);
40
+ });
41
+ try {
42
+ // Follow replacement connects (a reconnect queued behind the captured
43
+ // run installs a new in-flight promise) inside ONE fixed deadline.
44
+ // Failures are already captured in state.mcpFailures, so rejections are
45
+ // swallowed: this gate must never reject the turn.
46
+ let current = inFlight;
47
+ while (current && !expired) {
48
+ await Promise.race([Promise.resolve(current).catch(() => {}), budget]);
49
+ if (expired) break;
50
+ // Let a queued reconnect install its promise before re-checking.
51
+ await Promise.race([new Promise((resolveTick) => setImmediate(resolveTick)), budget]);
52
+ current = state.mcpConnectInFlight;
53
+ }
54
+ } finally {
55
+ if (timer) clearTimeout(timer);
56
+ }
57
+ }
@@ -0,0 +1,71 @@
1
+ // Connecting, disconnecting and reconciling individual MCP servers in the live
2
+ // registry. The caller (connect.mjs) owns the serialization and the shared state.
3
+ import { clean } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
4
+ import { ensurePluginServerData } from './plugin-servers.mjs';
5
+
6
+ export function createServerConnection({
7
+ mcpClient,
8
+ state,
9
+ resolveEffectiveMcpServers,
10
+ getMcpScopeId,
11
+ scopeOptions,
12
+ applied,
13
+ fingerprint,
14
+ syncScopeRoot,
15
+ }) {
16
+ // Connect/disconnect exactly one server in the live registry, leaving all
17
+ // others untouched. Used by the enable/disable toggle so a single-server
18
+ // change never triggers a full disconnectAll()/reconnect freeze. Only an
19
+ // explicit sign-in passes `login` ({ openBrowser }): every other connect is
20
+ // non-interactive and reports a server that needs one as sign-in required.
21
+ async function applyMcpServerConnection(name, enabled, servers = resolveEffectiveMcpServers().servers, login = null) {
22
+ const target = clean(name);
23
+ if (!target) return;
24
+ // Changing this server's state clears any stale failure record for it.
25
+ if (Array.isArray(state.mcpFailures)) {
26
+ state.mcpFailures = state.mcpFailures.filter((row) => row.name !== target);
27
+ }
28
+ const cfg = servers[target];
29
+ if (enabled === false || !cfg || cfg.enabled === false) {
30
+ applied().delete(target);
31
+ await mcpClient.disconnectMcpServer?.(target, scopeOptions());
32
+ return;
33
+ }
34
+ applied().set(target, fingerprint(cfg));
35
+ // Close-then-connect as one generation-guarded op: a shutdown landing
36
+ // between the close and the connect never resurrects the server.
37
+ try {
38
+ ensurePluginServerData(cfg);
39
+ await mcpClient.reconnectSingleMcpServer(
40
+ getMcpScopeId(),
41
+ target,
42
+ cfg,
43
+ login ? { interactive: true, openBrowser: login.openBrowser } : {}
44
+ );
45
+ } catch (error) {
46
+ const failure = { name: target, msg: error?.message || String(error) };
47
+ if (error?.authRequired) failure.authRequired = true;
48
+ const failures = Array.isArray(error?.failures) ? error.failures : [failure];
49
+ state.mcpFailures = [...(state.mcpFailures || []), ...failures];
50
+ }
51
+ }
52
+
53
+ // Bring the registry in line with the effective set: drop servers that are
54
+ // gone or disabled, (re)connect servers that are new or whose resolved
55
+ // config changed; untouched servers keep their connection.
56
+ async function reconcileServers() {
57
+ syncScopeRoot();
58
+ const { servers } = resolveEffectiveMcpServers();
59
+ const known = applied();
60
+ const drops = [...known.keys()].filter((name) => !servers[name] || servers[name].enabled === false);
61
+ const connects = Object.entries(servers)
62
+ .filter(([name, cfg]) => cfg?.enabled !== false && known.get(name) !== fingerprint(cfg))
63
+ .map(([name]) => name);
64
+ await Promise.allSettled([
65
+ ...drops.map((name) => applyMcpServerConnection(name, false, servers)),
66
+ ...connects.map((name) => applyMcpServerConnection(name, true, servers)),
67
+ ]);
68
+ }
69
+
70
+ return { applyMcpServerConnection, reconcileServers };
71
+ }
@@ -41,8 +41,11 @@ export function createMediaApi() {
41
41
  });
42
42
  return { status: await module.getVoiceStatus(), error };
43
43
  },
44
- async listMediaLanes() {
45
- return (await lanes()).listMediaLanes();
44
+ // `budgetMs` (Studio) caps the per-lane catalog wait; slower lanes come
45
+ // back `catalogPending`. Capped so a caller cannot park the call.
46
+ async listMediaLanes(options) {
47
+ const budgetMs = Math.min(30_000, Math.max(0, Math.trunc(Number(options?.budgetMs) || 0)));
48
+ return (await lanes()).listMediaLanes({ budgetMs });
46
49
  },
47
50
  // The Studio's current selection is the runtime's default: a tool call
48
51
  // that omits lane/model follows it. Studio pushes on every settled change.
@@ -11,6 +11,7 @@ function customRowFields(name, m) {
11
11
  if (!isCustomProviderName(name)) return {};
12
12
  return {
13
13
  ...(typeof m.name === 'string' && m.name ? { name: m.name } : {}),
14
+ ...(typeof m.providerName === 'string' && m.providerName ? { providerName: m.providerName } : {}),
14
15
  ...(typeof m.hidden === 'boolean' ? { hidden: m.hidden } : {}),
15
16
  ...(typeof m.manual === 'boolean' ? { manual: m.manual } : {}),
16
17
  ...(m.customReasoning && typeof m.customReasoning === 'object' ? { customReasoning: m.customReasoning } : {}),
@@ -67,7 +67,10 @@ export function createRouteTuning(deps, persist) {
67
67
  },
68
68
  async setEffort(value) {
69
69
  const normalized = normalizeEffortInput(value);
70
- setRouteState({ ...getRoute(), effort: normalized });
70
+ // An explicit level is the user's choice for this route: Auto effort
71
+ // would replace it on the next turn (its base is fixed), so the pick
72
+ // turns the route's Auto off until it is switched back on.
73
+ setRouteState({ ...getRoute(), effort: normalized, ...(normalized ? { autoEffort: false } : {}) });
71
74
  const modelMeta = await lookupModelMeta(getRoute().provider, getRoute().model);
72
75
  const fastCapable = selectionFastCapable(getRoute().provider, modelMeta, normalized, getRoute().modelParameters);
73
76
  await commitRouteTuning(fastCapable, modelMeta);
@@ -24,6 +24,11 @@ import {
24
24
  } from '../../runtime/agent/orchestrator/runtime-core/session-route-policy.mjs';
25
25
  import { rebuildToolSurfaceForProviderWithMigration } from '../../runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs';
26
26
  import { UNOWNED_LEASE, openRouteLease } from './route-ownership.mjs';
27
+ import { resolveSessionMode } from '../../runtime/agent/orchestrator/session/agent-mode.mjs';
28
+ import { ownerRouteFallbacks } from '../../runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs';
29
+ import { normalizeWorkflowRoute } from '../../runtime/agent/orchestrator/runtime-core/workflow.mjs';
30
+ import { routeChain, validateRouteChainProviders } from '../../runtime/shared/model-route-chain.mjs';
31
+ import { resolveChainEntry } from '../workflow-agents-api/agent-route.mjs';
27
32
 
28
33
  /** The request, completed from the live route where the caller left gaps. */
29
34
  function completeRequest(deps, next) {
@@ -105,6 +110,32 @@ async function recreateEmptySession(deps, lease) {
105
110
  if (closed) await createCurrentSession('model-switch-empty');
106
111
  }
107
112
 
113
+ /** The fallback chain a Lead-class session follows on `route`: its owner's chain
114
+ * while the route is the owner's primary model, none otherwise (see
115
+ * owner-route-fallbacks.mjs). Nothing for an agent's session, which keeps its own. */
116
+ export function sessionRouteFallbackFields(deps, session, route) {
117
+ const { mode, agentId } = resolveSessionMode({ session });
118
+ if (!agentId) return {};
119
+ return {
120
+ routeFallbacks: ownerRouteFallbacks({
121
+ sessionMode: mode,
122
+ agentId,
123
+ provider: route.provider,
124
+ model: route.model,
125
+ config: deps.getConfig(),
126
+ }),
127
+ };
128
+ }
129
+
130
+ /** A same-model selection (a bot profile saved with new fallbacks) restarts the
131
+ * addressed session's chain when the owner's chain no longer matches it. */
132
+ function refreshSessionFallbacks(deps, session) {
133
+ const fields = sessionRouteFallbackFields(deps, session, deps.getRoute());
134
+ if (!fields.routeFallbacks) return;
135
+ if (JSON.stringify(fields.routeFallbacks) === JSON.stringify(session.routeFallbacks || [])) return;
136
+ deps.mgr.updateSessionRoute?.(session.id, fields, { session });
137
+ }
138
+
108
139
  /** Rewrite the addressed empty session to the route it was asked to run on. */
109
140
  function updateSessionInPlace(deps, session, updateOptions) {
110
141
  const { getRoute, getSession, setSession, mgr, statusRoutes } = deps;
@@ -119,6 +150,7 @@ function updateSessionInPlace(deps, session, updateOptions) {
119
150
  contextPercent: route.contextPercent,
120
151
  contextTokens: route.contextTokens || null,
121
152
  selectedContextWindow: route.selectedContextWindow || null,
153
+ ...sessionRouteFallbackFields(deps, session, route),
122
154
  };
123
155
  const updated = mgr.updateSessionRoute?.(session.id, fields, updateOptions);
124
156
  if (updated) setSession(updated);
@@ -183,6 +215,27 @@ function switchSessionInPlace(deps, session) {
183
215
  return recorded ? boundary : null;
184
216
  }
185
217
 
218
+ /** The Lead's fallback models after this selection, checked like an agent's
219
+ * chain (leaf routes, distinct providers): an explicit list replaces the
220
+ * stored one ([] clears it), a selection without one keeps it. */
221
+ async function leadFallbackChain(deps, next, primary) {
222
+ const storedPreset = findPreset(deps.getConfig(), workflowPresetId('lead'));
223
+ const requested = hasOwn(next || {}, 'fallbacks') ? next.fallbacks : storedPreset?.fallbacks;
224
+ if (!Array.isArray(requested) || !requested.length) return [];
225
+ const chain = routeChain({ provider: primary.provider, model: primary.model, fallbacks: requested });
226
+ validateRouteChainProviders(chain);
227
+ const storedChain = routeChain(storedPreset || {});
228
+ // Each entry goes through the agents' normalizer (fast clamp, context window).
229
+ const resolved = [];
230
+ for (const entry of chain.slice(1)) {
231
+ const previous = storedChain.find((c) => c.provider === entry.provider && c.model === entry.model);
232
+ resolved.push(await resolveChainEntry(deps, entry, previous || {}));
233
+ }
234
+ return (
235
+ normalizeWorkflowRoute({ provider: primary.provider, model: primary.model, fallbacks: resolved })?.fallbacks || []
236
+ );
237
+ }
238
+
186
239
  /** Validate, persist and adopt the requested route as the runtime's route. */
187
240
  async function adoptRoute(deps, persist, next, options, lease) {
188
241
  const {
@@ -195,6 +248,12 @@ async function adoptRoute(deps, persist, next, options, lease) {
195
248
  scheduleStatuslineUsageRefresh,
196
249
  } = deps;
197
250
  const { selectedRoute, fastCapable, modelMeta } = await resolveSelectedRoute(deps, next);
251
+ // The Lead's fallback chain travels with an explicit main-model choice only
252
+ // (the Lead editor, the setup tool); a session's own pick never touches it.
253
+ const persistedRoute =
254
+ options?.keepMainModel === false && options?.sessionOnly !== true
255
+ ? { ...selectedRoute, fallbacks: await leadFallbackChain(deps, next, selectedRoute) }
256
+ : selectedRoute;
198
257
  // The Lead route a conditional fill wrote, until the file confirms it.
199
258
  let filled = null;
200
259
  // Provider readiness may outlast the session the selection was made for.
@@ -222,7 +281,7 @@ async function adoptRoute(deps, persist, next, options, lease) {
222
281
  // The main model changes only on an explicit main-model choice
223
282
  // (keepMainModel: false, e.g. the setup tool); every other selection
224
283
  // — the session surface's model pick, an heir — keeps it.
225
- const leadRoute = persist.persistAdoptedModelSettings(selectedRoute, {
284
+ const leadRoute = persist.persistAdoptedModelSettings(persistedRoute, {
226
285
  keepMainModel: options?.keepMainModel !== false,
227
286
  onlyIfAbsent: options?.onlyIfEmpty === true,
228
287
  });
@@ -340,6 +399,7 @@ export function createSetRoute(deps, persist) {
340
399
  return modelSwitch ? { ...getRoute(), modelSwitch } : getRoute();
341
400
  }
342
401
  persist.applySessionTuning();
402
+ refreshSessionFallbacks(deps, session);
343
403
  return getRoute();
344
404
  }
345
405
  // An addressed runtime keeps its durable id: the empty session is updated
@@ -101,7 +101,7 @@ async function queueResult(row, item, entry, { deliver, recordDelivery, onDelive
101
101
  /**
102
102
  * `row` is `{ name, ownerAgentId }`; `run({ signal })` resolves `{ sessionId,
103
103
  * result }` (`signal` is the caller's cancellation, if any). Resolves the
104
- * outcome, or `{ skipped: true, reason: 'owner' | 'overlap' | 'backoff' }`
104
+ * outcome, or `{ skipped: true, reason: 'owner' | 'overlap' | 'backoff' | 'paused' }`
105
105
  * (an owner skip also names `ownerProblem` and its `message`); a failed run
106
106
  * rejects.
107
107
  */
@@ -141,6 +141,13 @@ export async function runOwnedAutomation(kind, row, run, deps = {}) {
141
141
  const state = await Promise.resolve()
142
142
  .then(() => record.state(name))
143
143
  .catch(() => null);
144
+ // Switched off (by the user, or by the failure limit): the cron claim and
145
+ // the webhook handler already refuse it, but a one-shot timer or a
146
+ // dispatch that raced the pause must not run it either.
147
+ if (state?.enabled === false) {
148
+ log(`${kind} "${name}": skipped, switched off`);
149
+ return await skip('paused');
150
+ }
144
151
  const wait = routineBackoffRemaining(state, now(), limits);
145
152
  if (wait > 0) {
146
153
  log(`${kind} "${name}": skipped, backing off ${Math.ceil(wait / 1000)}s after failures in a row`);
@@ -45,8 +45,15 @@ export function hydrateProviderModelRow(cfg, row) {
45
45
  const modelParameterOptions = addedOptions.length
46
46
  ? [...existingOptions.filter((option) => !addedOptions.some((added) => added.id === option?.id)), ...addedOptions]
47
47
  : row.modelParameterOptions;
48
+ const isCustom = row.provider?.startsWith?.('custom-');
49
+ const customName = isCustom ? String(cfg?.providers?.[row.provider]?.name || '').trim() : '';
50
+ // A custom provider's name always comes from the current configuration; a
51
+ // row's own (possibly renamed or removed) name is dropped.
52
+ const base = { ...row };
53
+ if (isCustom) delete base.providerName;
48
54
  return {
49
- ...row,
55
+ ...base,
56
+ ...(customName ? { providerName: customName } : {}),
50
57
  ...(alias ? { display: alias, displayAlias: alias } : {}),
51
58
  ...(modelParameterOptions ? { modelParameterOptions } : {}),
52
59
  effortOptions: effortItemsFor(row.provider, row, null),
@@ -5,10 +5,12 @@ import { getModelMetadataSync } from '../../runtime/agent/orchestrator/providers
5
5
  import { isCustomProviderName } from '../../runtime/agent/orchestrator/providers/custom-reasoning.mjs';
6
6
 
7
7
  const CUSTOM_DECLARED_FIELDS = [
8
+ 'supportsVision',
8
9
  'supportsReasoning',
9
10
  'reasoningOptions',
10
11
  'reasoningLevels',
11
12
  'reasoningContentField',
13
+ 'providerName',
12
14
  'hidden',
13
15
  'manual',
14
16
  'customReasoning',