mixdog 1.2.1 → 1.2.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 (557) hide show
  1. package/README.md +18 -0
  2. package/package.json +2 -1
  3. package/scripts/gate-local.mjs +19 -3
  4. package/scripts/model-catalog-audit.mjs +1 -15
  5. package/scripts/provider-pingpong-live.mjs +509 -0
  6. package/src/agents/advisor/AGENT.md +1 -1
  7. package/src/agents/advisor/agent.json +1 -1
  8. package/src/agents/reviewer/AGENT.md +3 -3
  9. package/src/agents/reviewer/agent.json +1 -1
  10. package/src/defaults/shipped-history.json +6 -0
  11. package/src/defaults/skills/setup/SKILL.md +6 -1
  12. package/src/defaults/skills/setup/references/actions.md +44 -22
  13. package/src/defaults/skills/setup/references/surfaces.md +10 -8
  14. package/src/headless-exec.mjs +15 -8
  15. package/src/lib/keychain-cjs.cjs +7 -4
  16. package/src/output-styles/common.md +11 -10
  17. package/src/repl/slash-commands.mjs +16 -1
  18. package/src/rules/agent/AGENT.md +5 -5
  19. package/src/rules/lead/LEAD.md +5 -5
  20. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -4
  21. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +10 -0
  22. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +92 -61
  23. package/src/runtime/agent/orchestrator/cache-mtime.mjs +10 -1
  24. package/src/runtime/agent/orchestrator/config-providers-merge.mjs +7 -0
  25. package/src/runtime/agent/orchestrator/config.mjs +5 -0
  26. package/src/runtime/agent/orchestrator/context/collect.mjs +1 -0
  27. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +26 -7
  28. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +112 -7
  29. package/src/runtime/agent/orchestrator/mcp/client-config.mjs +36 -7
  30. package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +49 -10
  31. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +40 -3
  32. package/src/runtime/agent/orchestrator/mcp/client.mjs +230 -96
  33. package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +103 -0
  34. package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +150 -0
  35. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +111 -0
  36. package/src/runtime/agent/orchestrator/mcp/oauth-probe.mjs +87 -0
  37. package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +95 -0
  38. package/src/runtime/agent/orchestrator/mcp/oauth-request.mjs +122 -0
  39. package/src/runtime/agent/orchestrator/mcp/oauth-shared.mjs +9 -0
  40. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +229 -0
  41. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +188 -29
  42. package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +2 -1
  43. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +48 -20
  44. package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +94 -31
  45. package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +98 -0
  46. package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.mjs +39 -18
  47. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +55 -0
  48. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +48 -37
  49. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +10 -7
  50. package/src/runtime/agent/orchestrator/providers/anthropic-midstream-recovery.mjs +18 -4
  51. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +13 -1
  52. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  53. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-midstream.mjs +10 -1
  54. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-recovery.mjs +23 -9
  55. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +46 -20
  56. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request.mjs +14 -9
  57. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +102 -45
  58. package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +69 -0
  59. package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.mjs +4 -0
  60. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-blocks.mjs +20 -1
  61. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-delta.mjs +15 -2
  62. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-start.mjs +11 -4
  63. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/message-events.mjs +3 -0
  64. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events.mjs +12 -5
  65. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-outcome.mjs +1 -0
  66. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-state.mjs +14 -0
  67. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +10 -1
  68. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +9 -0
  69. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +40 -7
  70. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +30 -9
  71. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +7 -0
  72. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +88 -21
  73. package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +30 -10
  74. package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +106 -0
  75. package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +10 -61
  76. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +94 -15
  77. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
  78. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +117 -72
  79. package/src/runtime/agent/orchestrator/providers/compat-request-policy.mjs +78 -2
  80. package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +75 -10
  81. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  82. package/src/runtime/agent/orchestrator/providers/cursor-wire-guards.mjs +97 -2
  83. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +103 -23
  84. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-messages.mjs +43 -2
  85. package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +103 -33
  86. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +32 -6
  87. package/src/runtime/agent/orchestrator/providers/cursor.mjs +27 -3
  88. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +64 -1
  89. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +70 -6
  90. package/src/runtime/agent/orchestrator/providers/custom-reasoning.mjs +34 -7
  91. package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +32 -6
  92. package/src/runtime/agent/orchestrator/providers/deepseek.mjs +67 -0
  93. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +80 -18
  94. package/src/runtime/agent/orchestrator/providers/gemini-cache-policy.mjs +11 -4
  95. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +46 -8
  96. package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +273 -0
  97. package/src/runtime/agent/orchestrator/providers/gemini-family.mjs +9 -0
  98. package/src/runtime/agent/orchestrator/providers/gemini-history-signatures.mjs +9 -4
  99. package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +41 -5
  100. package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +3 -1
  101. package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +180 -0
  102. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +369 -112
  103. package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +3 -1
  104. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +140 -35
  105. package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +70 -24
  106. package/src/runtime/agent/orchestrator/providers/gemini.mjs +80 -29
  107. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  108. package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
  109. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +69 -19
  110. package/src/runtime/agent/orchestrator/providers/lib/action-input-contract.mjs +60 -0
  111. package/src/runtime/agent/orchestrator/providers/lib/anthropic-native-blocks.mjs +1 -0
  112. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +246 -42
  113. package/src/runtime/agent/orchestrator/providers/lib/anthropic-server-tool-usage.mjs +18 -0
  114. package/src/runtime/agent/orchestrator/providers/lib/anthropic-system-blocks.mjs +27 -6
  115. package/src/runtime/agent/orchestrator/providers/lib/claude-bridge-schema.mjs +388 -0
  116. package/src/runtime/agent/orchestrator/providers/lib/inclusive-usage.mjs +54 -0
  117. package/src/runtime/agent/orchestrator/providers/lib/origin-bound-fetch.mjs +16 -0
  118. package/src/runtime/agent/orchestrator/providers/lib/responses-terminal-fields.mjs +11 -0
  119. package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +8 -0
  120. package/src/runtime/agent/orchestrator/providers/lib/wire-pairing.mjs +48 -22
  121. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +119 -18
  122. package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +13 -15
  123. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +3 -0
  124. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +2 -1
  125. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +1 -1
  126. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
  127. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +33 -1
  128. package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +4 -9
  129. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +2 -0
  130. package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +2 -0
  131. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +123 -61
  132. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +35 -14
  133. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-result.mjs +27 -2
  134. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +37 -7
  135. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/tool-call-acc.mjs +2 -9
  136. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +1 -0
  137. package/src/runtime/agent/orchestrator/providers/openai-compat-error-contract.mjs +50 -0
  138. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +47 -2
  139. package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +47 -25
  140. package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +5 -1
  141. package/src/runtime/agent/orchestrator/providers/openai-compat-reasoning-field.mjs +151 -0
  142. package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +64 -23
  143. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +79 -33
  144. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-head-parity.json +2901 -0
  145. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-parity.fixtures.mjs +172 -0
  146. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-state.mjs +3 -2
  147. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +144 -15
  148. package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +193 -0
  149. package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +66 -2
  150. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +9 -2
  151. package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +2 -0
  152. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +109 -26
  153. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +27 -6
  154. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +34 -20
  155. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +23 -4
  156. package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +37 -28
  157. package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +274 -0
  158. package/src/runtime/agent/orchestrator/providers/openai-http-sse-request.mjs +72 -32
  159. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +23 -10
  160. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +43 -15
  161. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +10 -0
  162. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-items.mjs +18 -7
  163. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-outcome.mjs +5 -2
  164. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-stamps.mjs +2 -2
  165. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-state.mjs +7 -3
  166. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/text-relay.mjs +9 -1
  167. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +2 -2
  168. package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +1 -3
  169. package/src/runtime/agent/orchestrator/providers/openai-model-capabilities.mjs +80 -0
  170. package/src/runtime/agent/orchestrator/providers/openai-oauth-dispatch.mjs +37 -4
  171. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +12 -1
  172. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +22 -2
  173. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +12 -1
  174. package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +76 -33
  175. package/src/runtime/agent/orchestrator/providers/openai-responses-payload-parity.fixture.json +323 -0
  176. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +44 -16
  177. package/src/runtime/agent/orchestrator/providers/openai-retry-policy.mjs +9 -0
  178. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +7 -16
  179. package/src/runtime/agent/orchestrator/providers/openai-ws-handshake.mjs +16 -8
  180. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/refusals.mjs +60 -0
  181. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/tool-calls.mjs +51 -35
  182. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state.mjs +30 -0
  183. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +1 -2
  184. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/policy.mjs +25 -14
  185. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +12 -0
  186. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/terminal-frames.mjs +25 -6
  187. package/src/runtime/agent/orchestrator/providers/openai-ws-terminal.mjs +18 -11
  188. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +41 -22
  189. package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +170 -0
  190. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +75 -70
  191. package/src/runtime/agent/orchestrator/providers/openrouter.mjs +75 -0
  192. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +720 -0
  193. package/src/runtime/agent/orchestrator/providers/registry.mjs +4 -1
  194. package/src/runtime/agent/orchestrator/providers/retry-backoff.mjs +46 -7
  195. package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +123 -219
  196. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +2 -0
  197. package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +148 -45
  198. package/src/runtime/agent/orchestrator/providers/retry-midstream.mjs +81 -16
  199. package/src/runtime/agent/orchestrator/providers/transport-scope.d.mts +6 -0
  200. package/src/runtime/agent/orchestrator/providers/transport-scope.mjs +28 -0
  201. package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +157 -0
  202. package/src/runtime/agent/orchestrator/providers/wire-route.mjs +512 -0
  203. package/src/runtime/agent/orchestrator/providers/xai-tool-search-head-parity.json +1131 -0
  204. package/src/runtime/agent/orchestrator/providers/xai-tool-search.fixtures.mjs +183 -0
  205. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +100 -0
  206. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +3 -0
  207. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +21 -0
  208. package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +15 -0
  209. package/src/runtime/agent/orchestrator/runtime-core/goal-reminder.mjs +0 -18
  210. package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +57 -28
  211. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +19 -16
  212. package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +1 -97
  213. package/src/runtime/agent/orchestrator/runtime-core/provider-request-snapshot.mjs +7 -3
  214. package/src/runtime/agent/orchestrator/runtime-core/session-title.mjs +21 -1
  215. package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +14 -3
  216. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +526 -0
  217. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
  218. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +10 -7
  219. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +176 -71
  220. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +62 -10
  221. package/src/runtime/agent/orchestrator/runtime-core/tool-exposure.mjs +96 -0
  222. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +5 -1
  223. package/src/runtime/agent/orchestrator/session/compact/constants.mjs +3 -2
  224. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +14 -93
  225. package/src/runtime/agent/orchestrator/session/compact/summary-anchor.mjs +5 -0
  226. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +120 -0
  227. package/src/runtime/agent/orchestrator/session/context-tool-schema.mjs +17 -2
  228. package/src/runtime/agent/orchestrator/session/context-utils.mjs +8 -4
  229. package/src/runtime/agent/orchestrator/session/eager-dispatch/admission.mjs +2 -1
  230. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +6 -1
  231. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +91 -1
  232. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +1 -1
  233. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +56 -0
  234. package/src/runtime/agent/orchestrator/session/loop/deferred-call-through.mjs +18 -8
  235. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -0
  236. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +21 -10
  237. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +132 -0
  238. package/src/runtime/agent/orchestrator/session/loop/request-boundary.mjs +30 -0
  239. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +67 -20
  240. package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +104 -19
  241. package/src/runtime/agent/orchestrator/session/loop/tool-call-resolution.mjs +95 -0
  242. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +30 -3
  243. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +15 -4
  244. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +15 -1
  245. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +150 -0
  246. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +7 -2
  247. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +30 -2
  248. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +11 -2
  249. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +2 -1
  250. package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +34 -12
  251. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +57 -21
  252. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +57 -25
  253. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
  254. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
  255. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +8 -0
  256. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +5 -10
  257. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +208 -14
  258. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +16 -4
  259. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +25 -3
  260. package/src/runtime/agent/orchestrator/session/manager/session-fallback-title.mjs +82 -0
  261. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +19 -2
  262. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +3 -0
  263. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +4 -1
  264. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +18 -7
  265. package/src/runtime/agent/orchestrator/session/manager.mjs +2 -1
  266. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +6 -0
  267. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +168 -29
  268. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +96 -0
  269. package/src/runtime/agent/orchestrator/session/side-question.mjs +192 -0
  270. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +17 -3
  271. package/src/runtime/agent/orchestrator/session/store/live-state.mjs +23 -0
  272. package/src/runtime/agent/orchestrator/session/store/paths-heartbeat.mjs +58 -2
  273. package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +6 -3
  274. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +41 -28
  275. package/src/runtime/agent/orchestrator/session/store/session-observers.mjs +4 -2
  276. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +12 -1
  277. package/src/runtime/agent/orchestrator/session/store/write-admission.mjs +3 -1
  278. package/src/runtime/agent/orchestrator/session/store/write-guards.mjs +16 -0
  279. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +22 -10
  280. package/src/runtime/agent/orchestrator/session/store-summary-archived-agent.mjs +34 -15
  281. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +6 -0
  282. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +13 -0
  283. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +13 -1
  284. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +10 -2
  285. package/src/runtime/agent/orchestrator/session/store.mjs +19 -3
  286. package/src/runtime/agent/orchestrator/session/tool-batch/execute-call.mjs +1 -0
  287. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +1 -1
  288. package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +1 -1
  289. package/src/runtime/agent/orchestrator/session/tool-batch/state.mjs +7 -1
  290. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +4 -0
  291. package/src/runtime/agent/orchestrator/session/tool-schema-rejection.mjs +77 -0
  292. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +20 -1
  293. package/src/runtime/agent/orchestrator/tools/bash-policy-scan.mjs +8 -2
  294. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +2 -2
  295. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/command-preflight.mjs +3 -3
  296. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +12 -15
  297. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +4 -1
  298. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +5 -5
  299. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +14 -10
  300. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +49 -34
  301. package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +23 -1
  302. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +1 -1
  303. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1 -1
  304. package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +73 -39
  305. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +18 -1
  306. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -4
  307. package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +88 -12
  308. package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +2 -0
  309. package/src/runtime/computer-bridge/action-schema.mjs +44 -21
  310. package/src/runtime/github/tool.mjs +92 -2
  311. package/src/runtime/memory/index.mjs +1 -0
  312. package/src/runtime/memory/lib/http-router/ingest-routes.mjs +22 -1
  313. package/src/runtime/memory/lib/ingest/transcript-offsets.mjs +2 -0
  314. package/src/runtime/memory/lib/pg/adapter.mjs +98 -1
  315. package/src/runtime/memory/lib/pg/process.mjs +65 -50
  316. package/src/runtime/memory/lib/pg/run-child.mjs +49 -0
  317. package/src/runtime/memory/lib/pg/supervisor.mjs +124 -16
  318. package/src/runtime/memory/lib/runtime-fetcher.mjs +12 -13
  319. package/src/runtime/memory/lib/transcript-ingest.mjs +14 -0
  320. package/src/runtime/office/README.md +2 -2
  321. package/src/runtime/office/authoring/html-document-author.mjs +2 -0
  322. package/src/runtime/office/authoring/pptx-contact-sheet.mjs +2 -0
  323. package/src/runtime/office/authoring/pptx-html-measure.mjs +2 -0
  324. package/src/runtime/office/authoring/pptx-html-runner.mjs +2 -0
  325. package/src/runtime/office/authoring/xlsx-html-runner.mjs +2 -0
  326. package/src/runtime/office/core/office-actions-read.mjs +3 -0
  327. package/src/runtime/office/core/office-render-preview.mjs +2 -0
  328. package/src/runtime/office/core/office-session-dispatch.mjs +7 -0
  329. package/src/runtime/office/core/office-sessionless-actions.mjs +8 -0
  330. package/src/runtime/office/core/office-sessions.mjs +5 -0
  331. package/src/runtime/office/core/office-transactions.mjs +3 -0
  332. package/src/runtime/office/core/office-write-ledger.mjs +98 -0
  333. package/src/runtime/office/design/library/templates/mixdog-executive.mixdog-edit.pptx +0 -0
  334. package/src/runtime/office/index.mjs +30 -0
  335. package/src/runtime/office/pdf/pdf-edit-document.mjs +2 -0
  336. package/src/runtime/office/pdf/pdf-render.mjs +23 -7
  337. package/src/runtime/office/pdf/pdf-security.mjs +2 -0
  338. package/src/runtime/office/portable/portable-opc.mjs +4 -1
  339. package/src/runtime/office/quality/visual-diff.mjs +2 -0
  340. package/src/runtime/office/shared/values.mjs +0 -5
  341. package/src/runtime/shared/background-tasks.mjs +92 -14
  342. package/src/runtime/shared/bounded-file-read.mjs +13 -0
  343. package/src/runtime/shared/bounded-line-diff.mjs +182 -0
  344. package/src/runtime/shared/buffered-appender.mjs +18 -0
  345. package/src/runtime/shared/config.mjs +78 -18
  346. package/src/runtime/shared/err-text.mjs +31 -0
  347. package/src/runtime/shared/error-presentation.mjs +159 -19
  348. package/src/runtime/shared/file-permissions.d.mts +6 -1
  349. package/src/runtime/shared/file-permissions.mjs +54 -6
  350. package/src/runtime/shared/llm/anthropic-betas.mjs +4 -3
  351. package/src/runtime/shared/llm/anthropic-effort.mjs +129 -134
  352. package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +50 -14
  353. package/src/runtime/shared/llm/model-catalog.mjs +53 -40
  354. package/src/runtime/shared/llm/openai-daybreak.mjs +4 -16
  355. package/src/runtime/shared/llm/provider-catalog-registry.mjs +11 -0
  356. package/src/runtime/shared/llm/provider-model-identities.mjs +13 -4
  357. package/src/runtime/shared/llm/usage-accounting.mjs +5 -2
  358. package/src/runtime/shared/llm/usage-ledger-rollup.mjs +3 -0
  359. package/src/runtime/shared/llm/usage-rollup.mjs +0 -1
  360. package/src/runtime/shared/loopback-listener.mjs +9 -0
  361. package/src/runtime/shared/model-display.mjs +13 -0
  362. package/src/runtime/shared/provider-api-key.mjs +2 -0
  363. package/src/runtime/shared/quota-resume-store.mjs +11 -0
  364. package/src/runtime/shared/review-diff.mjs +52 -0
  365. package/src/runtime/shared/session-baseline-store.mjs +960 -0
  366. package/src/runtime/shared/session-sidebar-host.d.mts +4 -0
  367. package/src/runtime/shared/session-sidebar-host.mjs +38 -0
  368. package/src/runtime/shared/slow-span.mjs +114 -0
  369. package/src/runtime/shared/statusline/statusline-format.mjs +4 -4
  370. package/src/runtime/shared/statusline/statusline-segments.mjs +28 -4
  371. package/src/runtime/shared/task-notification-envelope.mjs +2 -2
  372. package/src/runtime/shared/tool-card-model/card-header.mjs +6 -1
  373. package/src/runtime/shared/tool-card-model/generic-detail.mjs +39 -5
  374. package/src/runtime/shared/tool-card-model.mjs +5 -2
  375. package/src/runtime/shared/tool-output-limit.mjs +4 -0
  376. package/src/runtime/shared/tool-primitives.mjs +13 -0
  377. package/src/runtime/shared/tool-result-summary.mjs +8 -1
  378. package/src/runtime/shared/tool-surface.mjs +18 -3
  379. package/src/runtime/shared/tool-work-units.mjs +40 -4
  380. package/src/runtime/shared/transcript-writer/purge.mjs +74 -0
  381. package/src/runtime/shared/transcript-writer.mjs +10 -2
  382. package/src/runtime/shared/turn-changes.mjs +670 -274
  383. package/src/runtime/shared/turn-review-reply.mjs +8 -39
  384. package/src/runtime/shared/turn-snapshot-store.mjs +94 -2
  385. package/src/runtime/shared/turn-snapshot.mjs +986 -690
  386. package/src/runtime/shared/turn-worktree-snapshot.mjs +279 -262
  387. package/src/runtime/shared/user-data-guard.mjs +262 -49
  388. package/src/runtime/shared/worktrees/carry.mjs +531 -0
  389. package/src/runtime/shared/worktrees/contract.mjs +34 -24
  390. package/src/runtime/shared/worktrees/file-recovery.mjs +68 -0
  391. package/src/runtime/shared/worktrees/git-head.mjs +205 -0
  392. package/src/runtime/shared/worktrees/git.mjs +8 -3
  393. package/src/runtime/shared/worktrees/maintenance.mjs +1 -1
  394. package/src/runtime/shared/worktrees/manager.mjs +156 -8
  395. package/src/runtime/shared/worktrees/merge.mjs +31 -21
  396. package/src/runtime/shared/worktrees/paths.d.mts +6 -0
  397. package/src/runtime/shared/worktrees/test-support.mjs +19 -4
  398. package/src/runtime/terminal-bridge/client.mjs +44 -11
  399. package/src/runtime/terminal-bridge/tool-defs.mjs +13 -6
  400. package/src/runtime/tidy/apply.mjs +47 -2
  401. package/src/runtime/tidy/run-engines.mjs +11 -5
  402. package/src/session-runtime/agent-tool-routing.mjs +19 -13
  403. package/src/session-runtime/boot/apis.mjs +19 -5
  404. package/src/session-runtime/boot/facade.mjs +1 -0
  405. package/src/session-runtime/boot/infrastructure.mjs +12 -1
  406. package/src/session-runtime/boot/session.mjs +0 -1
  407. package/src/session-runtime/boot/tools.mjs +4 -1
  408. package/src/session-runtime/cwd-plugins/cwd-apply.mjs +12 -5
  409. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +65 -0
  410. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -1
  411. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +11 -4
  412. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +115 -18
  413. package/src/session-runtime/internal-tool-executor.mjs +6 -2
  414. package/src/session-runtime/lifecycle/cold-heir.mjs +188 -0
  415. package/src/session-runtime/lifecycle/inheritance.mjs +71 -27
  416. package/src/session-runtime/lifecycle/session-catalog.mjs +14 -3
  417. package/src/session-runtime/lifecycle/session-switch/resume.mjs +6 -0
  418. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +29 -6
  419. package/src/session-runtime/lifecycle/teardown.mjs +73 -8
  420. package/src/session-runtime/lifecycle-api.mjs +1 -0
  421. package/src/session-runtime/mcp-glue/connect.mjs +46 -14
  422. package/src/session-runtime/mcp-glue/server-catalog.mjs +20 -3
  423. package/src/session-runtime/mcp-glue.mjs +3 -0
  424. package/src/session-runtime/mcp-oauth-login.mjs +174 -0
  425. package/src/session-runtime/model-route/model-switch-target.mjs +34 -0
  426. package/src/session-runtime/model-route/set-route.mjs +85 -17
  427. package/src/session-runtime/model-route/web-search-route.mjs +1 -1
  428. package/src/session-runtime/native-web-search/routes.mjs +22 -3
  429. package/src/session-runtime/native-web-search-usage.mjs +51 -0
  430. package/src/session-runtime/native-web-search.mjs +10 -1
  431. package/src/session-runtime/new-session-config.mjs +4 -2
  432. package/src/session-runtime/provider-auth/login.mjs +4 -0
  433. package/src/session-runtime/provider-models/model-meta.mjs +12 -3
  434. package/src/session-runtime/quick-model-rows/web-search-rows.mjs +13 -8
  435. package/src/session-runtime/quick-web-search-models.mjs +13 -38
  436. package/src/session-runtime/resource-extension-sync.mjs +4 -0
  437. package/src/session-runtime/resource-mcp-api.mjs +98 -2
  438. package/src/session-runtime/resource-scope-decorate.mjs +10 -3
  439. package/src/session-runtime/runtime-review-api.mjs +21 -2
  440. package/src/session-runtime/services/agent-tool/index-write-queue.mjs +81 -20
  441. package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +13 -1
  442. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +7 -2
  443. package/src/session-runtime/services/agent-tool/lead-worker-index/forgotten-leads.mjs +29 -0
  444. package/src/session-runtime/services/agent-tool/lead-worker-index/index-file.mjs +1 -1
  445. package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +73 -7
  446. package/src/session-runtime/services/agent-tool/send-flow.mjs +22 -4
  447. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +2 -0
  448. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +22 -5
  449. package/src/session-runtime/services/agent-tool/tag-registry.mjs +15 -1
  450. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +8 -1
  451. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -2
  452. package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +10 -1
  453. package/src/session-runtime/services/agent-tool/worker-index.mjs +46 -7
  454. package/src/session-runtime/services/agent-tool/worker-rows.mjs +17 -0
  455. package/src/session-runtime/services/agent-tool.mjs +3 -0
  456. package/src/session-runtime/services/channel-admin.mjs +19 -3
  457. package/src/session-runtime/services/memory-runtime-proxy/calls.mjs +4 -0
  458. package/src/session-runtime/services/memory-runtime-proxy.mjs +1 -0
  459. package/src/session-runtime/services/provider-admin.mjs +81 -15
  460. package/src/session-runtime/session-transcript-purge.mjs +32 -0
  461. package/src/session-runtime/session-turn-api.mjs +14 -5
  462. package/src/session-runtime/settings-builtin-tools-api.mjs +9 -0
  463. package/src/session-runtime/settings-compaction-api.mjs +1 -0
  464. package/src/session-runtime/setup-tool/executor.mjs +36 -6
  465. package/src/session-runtime/setup-tool/extended-actions.mjs +86 -6
  466. package/src/session-runtime/setup-tool/settings-contract.mjs +13 -3
  467. package/src/session-runtime/setup-tool/tool-defs.mjs +13 -4
  468. package/src/session-runtime/skills-api.mjs +3 -5
  469. package/src/session-runtime/tool-surface.mjs +5 -1
  470. package/src/session-runtime/turn/compaction-gate.mjs +25 -0
  471. package/src/session-runtime/turn/session-ops.mjs +7 -5
  472. package/src/session-runtime/turn/side-question-ops.mjs +22 -0
  473. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +5 -1
  474. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +8 -3
  475. package/src/session-runtime/turn/turn-run/turn-settle.mjs +10 -0
  476. package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
  477. package/src/session-runtime/turn/turn-run.mjs +25 -9
  478. package/src/session-runtime/usage-stats-api.mjs +43 -11
  479. package/src/session-runtime/workspace/project-branches.mjs +7 -5
  480. package/src/session-runtime/workspace/transition-steps.mjs +129 -31
  481. package/src/session-runtime/workspace/transitions.mjs +190 -15
  482. package/src/standalone/daemon-agent-control.mjs +9 -1
  483. package/src/standalone/daemon-drain-policy.mjs +8 -0
  484. package/src/standalone/daemon-local-session-bridge.mjs +14 -1
  485. package/src/standalone/daemon-owner.mjs +17 -4
  486. package/src/standalone/daemon-pg-stop.mjs +51 -0
  487. package/src/standalone/daemon-stop-budget.d.mts +2 -0
  488. package/src/standalone/daemon-stop-budget.mjs +18 -0
  489. package/src/standalone/daemon-stored-session-views.mjs +2 -0
  490. package/src/standalone/daemon-telemetry.mjs +74 -0
  491. package/src/standalone/daemon.mjs +64 -8
  492. package/src/standalone/desktop-service-registry.mjs +5 -0
  493. package/src/standalone/local-session-runtime.mjs +70 -19
  494. package/src/standalone/session-client.mjs +42 -0
  495. package/src/standalone/session-protocol.mjs +14 -0
  496. package/src/standalone/session-proxy/projection.mjs +41 -4
  497. package/src/standalone/session-runtime-inline-host/boot-loaders.mjs +15 -1
  498. package/src/standalone/session-runtime-inline-host.mjs +1 -0
  499. package/src/standalone/session-service/agent-tree/agent-cancel.mjs +33 -2
  500. package/src/standalone/session-service/agent-tree/agent-turns.mjs +26 -10
  501. package/src/standalone/session-service/agent-tree.mjs +12 -0
  502. package/src/standalone/session-service/quota-resume.mjs +8 -0
  503. package/src/standalone/session-service/retention/busy.mjs +15 -1
  504. package/src/standalone/session-service/session-calls/session-action.mjs +18 -3
  505. package/src/standalone/session-service-api.mjs +11 -1
  506. package/src/standalone/session-service.mjs +1 -0
  507. package/src/standalone/session-state-patch.d.mts +2 -1
  508. package/src/standalone/session-state-patch.mjs +41 -1
  509. package/src/tui/App.jsx +25 -0
  510. package/src/tui/app/app-view/floating-panel.jsx +26 -1
  511. package/src/tui/app/app-view.jsx +1 -0
  512. package/src/tui/app/btw-controller.mjs +118 -0
  513. package/src/tui/app/create-app-pickers.mjs +6 -0
  514. package/src/tui/app/extension-pickers/mcp-login.mjs +107 -0
  515. package/src/tui/app/extension-pickers/mcp-servers-picker.mjs +2 -1
  516. package/src/tui/app/extension-pickers.mjs +2 -1
  517. package/src/tui/app/model-options.mjs +11 -16
  518. package/src/tui/app/shell-layout/row-budgets.mjs +11 -1
  519. package/src/tui/app/shell-layout/surfaces.mjs +3 -1
  520. package/src/tui/app/slash-commands.mjs +20 -5
  521. package/src/tui/app/slash-dispatch/panel-commands.mjs +16 -1
  522. package/src/tui/app/slash-dispatch/session-commands.mjs +19 -6
  523. package/src/tui/app/use-global-key-input/approval-shortcut.mjs +12 -0
  524. package/src/tui/app/use-global-key-input.mjs +5 -12
  525. package/src/tui/app/use-prompt-handlers.mjs +11 -0
  526. package/src/tui/components/BtwPanel.jsx +53 -0
  527. package/src/tui/components/QuestionPanel.jsx +275 -0
  528. package/src/tui/components/StatusLine.jsx +2 -0
  529. package/src/tui/components/question-panel-model.mjs +156 -0
  530. package/src/tui/components/status-line/local-line.mjs +5 -2
  531. package/src/tui/components/status-line/use-statusline-text.mjs +7 -1
  532. package/src/tui/dist/index.mjs +3221 -1500
  533. package/src/tui/session/context-state.mjs +4 -2
  534. package/src/tui/session/quota-resume.mjs +46 -14
  535. package/src/tui/session/session-api/commands.mjs +43 -24
  536. package/src/tui/session/session-api/controls.mjs +2 -2
  537. package/src/tui/session/session-api/extensions.mjs +22 -0
  538. package/src/tui/session/session-api/integrations.mjs +16 -2
  539. package/src/tui/session/session-api/lifecycle/dispose.mjs +1 -0
  540. package/src/tui/session/session-api/lifecycle.mjs +7 -2
  541. package/src/tui/session/session-api/routes.mjs +71 -7
  542. package/src/tui/session/session-api/settings.mjs +11 -14
  543. package/src/tui/session/session-api.mjs +1 -0
  544. package/src/tui/session/session-flow/drain.mjs +7 -1
  545. package/src/tui/session/session-flow/failed-turn-rewind.mjs +10 -4
  546. package/src/tui/session/tool-approval.mjs +10 -7
  547. package/src/tui/session/transcript-spill.mjs +14 -7
  548. package/src/tui/session/turn-stream.mjs +174 -31
  549. package/src/tui/session/turn.mjs +46 -16
  550. package/src/tui/statusline-ansi-bridge.mjs +1 -1
  551. package/src/ui/statusline-agents.mjs +15 -4
  552. package/src/ui/statusline.mjs +7 -3
  553. package/src/ui/streaming-markdown-heal.mjs +34 -3
  554. package/src/vendor/statusline/src/gateway/claude-current.mjs +14 -4
  555. package/src/workflows/default/WORKFLOW.md +3 -1
  556. package/src/runtime/memory/lib/memory-session-merge.mjs +0 -32
  557. package/src/runtime/shared/llm/usage-session-history.mjs +0 -266
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * src/session-runtime/lifecycle/inheritance.mjs - session inheritance
3
3
  * (/inherit): fit preflight and the carry of another session's conversation
4
- * into the freshly created heir.
4
+ * into the freshly created heir — the live one (inheritFrom) or a cold sibling
5
+ * persisted without switching (inheritToNewSession), through one carry core.
5
6
  */
6
7
  import { hasUserConversationMessage } from '../../runtime/agent/orchestrator/session/manager/prompt-utils.mjs';
7
8
  import { stripEffortConfiguration } from '../../runtime/agent/orchestrator/providers/effort-configuration.mjs';
@@ -14,6 +15,7 @@ import {
14
15
  inheritanceRouteTarget,
15
16
  } from '../inheritance-fit.mjs';
16
17
  import { clean } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
18
+ import { buildColdHeir, committedSource, persistColdHeir } from './cold-heir.mjs';
17
19
 
18
20
  export function createInheritance(deps, { compactConversation, saveSession }) {
19
21
  const { getSession, getRoute, mgr, createCurrentSession, invalidateContextStatusCache, getReservedSessionId } = deps;
@@ -143,6 +145,44 @@ export function createInheritance(deps, { compactConversation, saveSession }) {
143
145
  };
144
146
  }
145
147
 
148
+ /**
149
+ * The ONE carry every inheritance path shares: measure the source's
150
+ * conversation on the heir's scale, compact it for the heir when asked to
151
+ * or when it cannot fit, and append it to the (empty) heir with its
152
+ * inheritance boundary. Refusals throw before the heir is touched; the
153
+ * source is never mutated.
154
+ */
155
+ async function carryConversation(source, target, { compact = false } = {}) {
156
+ // System blocks belong to the session that BUILT them: the target's own
157
+ // prompt was composed for the current model, tool surface, and workflow.
158
+ // Only the conversation itself travels.
159
+ let carried = inheritableMessages(source.messages);
160
+ if (!hasUserConversationMessage(carried)) {
161
+ throw new Error('inheritFrom: the source session has no conversation to carry');
162
+ }
163
+ // Decide BEFORE anything moves, on the heir's own scale. This is the
164
+ // same function inheritancePreflight() answers with, so the surface that
165
+ // offered the carry and the runtime that performs it cannot disagree —
166
+ // and a refusal no longer has to unwind a half-filled transcript.
167
+ const fit = inheritanceFit(carried, target);
168
+ let compacted = false;
169
+ // compact:true summarizes even a conversation that fits; otherwise only
170
+ // one that cannot fit is compacted.
171
+ if (compact || (fit.known && !fit.fits)) {
172
+ const next = await fitConversationForHeir(source, target, carried, fit, { required: compact });
173
+ compacted = next !== carried;
174
+ carried = next;
175
+ }
176
+ target.messages.push(
177
+ ...stripEffortConfiguration(inheritedCompatReplayMessages(structuredClone(carried), source.provider))
178
+ );
179
+ target.inheritedFromSessionId = source.id;
180
+ target.updatedAt = Date.now();
181
+ recordInheritanceBoundary(target, source);
182
+ if (!clean(target.title) && clean(source.title)) target.title = source.title;
183
+ return { messages: carried.length, compacted };
184
+ }
185
+
146
186
  /**
147
187
  * Session inheritance (/inherit): carry another session's conversation
148
188
  * into THIS freshly created session, so the transcript continues under a
@@ -174,41 +214,45 @@ export function createInheritance(deps, { compactConversation, saveSession }) {
174
214
  }
175
215
  const source = mgr.getSession(id);
176
216
  if (!source) throw new Error(`inheritFrom: session ${id} was not found`);
177
- // System blocks belong to the session that BUILT them: the target's own
178
- // prompt was composed for the current model, tool surface, and workflow.
179
- // Only the conversation itself travels.
180
- let carried = inheritableMessages(source.messages);
181
- if (!hasUserConversationMessage(carried)) {
182
- throw new Error('inheritFrom: the source session has no conversation to carry');
183
- }
184
- // Decide BEFORE anything moves, on the heir's own scale. This is the
185
- // same function inheritancePreflight() answers with, so the surface that
186
- // offered the carry and the runtime that performs it cannot disagree —
187
- // and a refusal no longer has to unwind a half-filled transcript.
188
- const fit = inheritanceFit(carried, target);
189
- // compact:true summarizes even a conversation that fits; otherwise only
190
- // one that cannot fit is compacted.
191
- if (compact || (fit.known && !fit.fits)) {
192
- carried = await fitConversationForHeir(source, target, carried, fit, { required: compact });
193
- }
194
- target.messages.push(
195
- ...stripEffortConfiguration(inheritedCompatReplayMessages(structuredClone(carried), source.provider))
196
- );
217
+ const { messages } = await carryConversation(source, target, { compact });
197
218
  invalidateContextStatusCache();
198
- target.inheritedFromSessionId = source.id;
199
- target.updatedAt = Date.now();
200
- recordInheritanceBoundary(target, source);
201
- if (!clean(target.title) && clean(source.title)) target.title = source.title;
202
219
  saveSession(target, { immediate: true });
203
220
  invalidateContextStatusCache();
204
221
  return {
205
222
  sessionId: target.id,
206
223
  sourceSessionId: source.id,
207
- messages: carried.length,
224
+ messages,
208
225
  provider: target.provider,
209
226
  model: target.model,
210
227
  };
211
228
  }
212
229
 
213
- return { inheritancePreflight, inheritFrom };
230
+ /**
231
+ * Inheritance WITHOUT switching: carry a session's conversation (normally
232
+ * the live one, possibly mid-turn) into a NEW session on the same route,
233
+ * cwd and Project, persisted cold for a later resume. The live session
234
+ * stays the current one and is never mutated; only its committed turns
235
+ * travel. Same carry, compaction and refusals as inheritFrom().
236
+ */
237
+ async function inheritToNewSession(sourceSessionId, { compact = false, titleSuffix = '' } = {}) {
238
+ const live = getSession();
239
+ const id = clean(sourceSessionId) || clean(live?.id);
240
+ if (!id) throw new TypeError('inheritToNewSession: source session id is required');
241
+ const stored = id === clean(live?.id) ? live : mgr.getSession(id);
242
+ if (!stored) throw new Error(`inheritFrom: session ${id} was not found`);
243
+ const source = committedSource(stored);
244
+ const heir = buildColdHeir(source);
245
+ const { messages, compacted } = await carryConversation(source, heir, { compact: compact === true });
246
+ // A suffixed title ("… (Fork)") tells the copy apart in session lists;
247
+ // locked as a model title so auto-titling keeps it, renames still win.
248
+ if (titleSuffix && clean(heir.title)) {
249
+ heir.title = `${clean(heir.title)}${titleSuffix}`;
250
+ heir.titleLocked = true;
251
+ heir.titleSource = 'ai';
252
+ }
253
+ await persistColdHeir(heir);
254
+ return { sessionId: heir.id, messages, compacted };
255
+ }
256
+
257
+ return { inheritancePreflight, inheritFrom, inheritToNewSession };
214
258
  }
@@ -15,6 +15,9 @@ import {
15
15
  clean,
16
16
  } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
17
17
 
18
+ import { isRealTurnMessage } from '../../runtime/agent/orchestrator/session/compact/user-message-kind.mjs';
19
+ import { realTurnCount as realTurnCountOf } from '../../runtime/agent/orchestrator/session/store-summary-fields.mjs';
20
+
18
21
  const VISIBLE_OWNERS = ['cli', 'user', 'mixdog'];
19
22
 
20
23
  function isLeadShaped(s, owner, sourceType) {
@@ -39,7 +42,9 @@ function previewOf(s) {
39
42
  const rawPreview = s.preview || '';
40
43
  let preview = isSessionPreviewNoise(rawPreview) ? '' : cleanSessionPreview(rawPreview);
41
44
  let messageCount = Math.max(0, Number(s.messageCount) || 0);
45
+ let realTurnCount = realTurnCountOf(s);
42
46
  if (!preview && Array.isArray(s.messages)) {
47
+ realTurnCount = s.messages.filter(isRealTurnMessage).length;
43
48
  const msgs = s.messages;
44
49
  const userPreviews = msgs
45
50
  .filter((m) => m && m.role === 'user')
@@ -50,10 +55,10 @@ function previewOf(s) {
50
55
  preview = userPreviews[0] || '';
51
56
  messageCount = msgs.filter((m) => m && (m.role === 'user' || m.role === 'assistant')).length;
52
57
  }
53
- return { preview, messageCount };
58
+ return { preview, messageCount, realTurnCount };
54
59
  }
55
60
 
56
- function leadSessionRow(s, heartbeatMtimes) {
61
+ export function leadSessionRow(s, heartbeatMtimes) {
57
62
  // Durable visibility is the catalog boundary. Apply it before the legacy
58
63
  // lead-shape heuristics so an explicitly agent-only child cannot leak just
59
64
  // because its owner field looks ordinary.
@@ -66,10 +71,14 @@ function leadSessionRow(s, heartbeatMtimes) {
66
71
  // One-shot `mixdog exec` runs are never resumable conversations.
67
72
  if (sourceType === 'exec') return null;
68
73
  if (!isLeadShaped(s, owner, sourceType)) return null;
69
- const { preview, messageCount } = previewOf(s);
74
+ const { preview, messageCount, realTurnCount } = previewOf(s);
70
75
  if (!preview && messageCount === 0) return null;
71
76
  return {
72
77
  id: s.id,
78
+ // Durable provenance the desktop uses to tell a real conversation from
79
+ // resume scratch or synthetic-only rows when no title source survives.
80
+ ...(realTurnCount === null ? {} : { realTurnCount }),
81
+ ...(s.detachedReason ? { detachedReason: s.detachedReason } : {}),
73
82
  updatedAt: s.updatedAt,
74
83
  // Conversation-activity timestamp for Recent ordering. Without this the
75
84
  // desktop falls back to updatedAt, which detach/resume bookkeeping
@@ -93,6 +102,8 @@ function leadSessionRow(s, heartbeatMtimes) {
93
102
  // sessions under the sidebar Automations section instead of Recent.
94
103
  sourceType: sourceType || null,
95
104
  sourceName: clean(s.sourceName || '') || null,
105
+ createdAt: Number(s.createdAt) || 0,
106
+ inheritedFromSessionId: clean(s.inheritedFromSessionId || '') || null,
96
107
  };
97
108
  }
98
109
 
@@ -13,6 +13,8 @@ import { isScratchConversation } from '../shared.mjs';
13
13
  import { compactionArchiveRange } from '../../../runtime/agent/orchestrator/session/compaction-archive.mjs';
14
14
  import { stampCompactionMessageIds } from '../../../runtime/agent/orchestrator/session/compaction-archive-capture.mjs';
15
15
  import { boundWorkspaceCwd, restoreWorkspaceAfterResume } from '../../workspace/workspace-state.mjs';
16
+ import { toolExposureMigrationNeeded } from '../../../runtime/agent/orchestrator/runtime-core/tool-exposure.mjs';
17
+ import { markToolSurfaceMigrated } from '../../../runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs';
16
18
 
17
19
  export function resolveResumeCwd(session, currentCwd) {
18
20
  // A worktree-bound Lead resumes in its worktree whatever its desktop class;
@@ -61,10 +63,14 @@ export function createSessionResume(deps, { ingestSessionIntoMemory, closeSurfac
61
63
  } = deps;
62
64
 
63
65
  function restoreResumedToolSurface(target, targetRoute) {
66
+ // A surface persisted with another tool exposure (e.g. a pre-bridge
67
+ // fixed canonical surface) migrates here; legacy selections never widen it.
68
+ const migrating = toolExposureMigrationNeeded(target, targetRoute.provider);
64
69
  applyDeferredToolSurface(target, deferredSurfaceModeForLead(getMode()), getStandaloneTools(), {
65
70
  provider: targetRoute.provider,
66
71
  model: targetRoute.model,
67
72
  });
73
+ if (migrating) markToolSurfaceMigrated(target);
68
74
  }
69
75
 
70
76
  // The resumed session's OWN effort wins. resolveRoute always returns an
@@ -5,7 +5,12 @@
5
5
  */
6
6
  import { clean } from '../../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
7
7
  import { forgetSessionSearch } from '../../../runtime/session-search/session-search-ingest.mjs';
8
- import { listLeadSessions } from '../session-catalog.mjs';
8
+ import { leadSessionRow } from '../session-catalog.mjs';
9
+ import { getStoreDir } from '../../../runtime/agent/orchestrator/session/store/paths-heartbeat.mjs';
10
+ import {
11
+ STORED_SESSION_UNREADABLE,
12
+ _storedSessionFromFile,
13
+ } from '../../../runtime/agent/orchestrator/session/store/serialize.mjs';
9
14
  import { SESSION_ID_PATTERN } from '../shared.mjs';
10
15
  import { replacementSessionCwd } from '../../workspace/workspace-state.mjs';
11
16
 
@@ -50,6 +55,15 @@ export function createSessionDelete(deps, { cancelBackgroundTasks }) {
50
55
  }
51
56
  }
52
57
 
58
+ // Availability is one strict read of the session's own file, filtered like
59
+ // the Lead catalog (an unreadable, foreign, closed or non-Lead record is not
60
+ // deletable). No catalog rebuild, no folder scan.
61
+ function isAvailableLead(sessionId) {
62
+ const stored = _storedSessionFromFile(getStoreDir(), `${sessionId}.json`);
63
+ if (!stored || stored === STORED_SESSION_UNREADABLE || stored.closed === true) return false;
64
+ return leadSessionRow(stored, new Map()) !== null;
65
+ }
66
+
53
67
  // The deleted task's worktree: the autoCleanup setting decides (clean and
54
68
  // merged → removed; otherwise kept). Off the deletion path; never fails it.
55
69
  function releaseDeletedWorktree(sessionId) {
@@ -61,12 +75,18 @@ export function createSessionDelete(deps, { cancelBackgroundTasks }) {
61
75
  async function deleteSession(id) {
62
76
  const sessionId = clean(id);
63
77
  if (!sessionId || !SESSION_ID_PATTERN.test(sessionId)) return false;
64
- const available = listLeadSessions(mgr, { refreshFromStorage: true }).some((row) => row.id === sessionId);
65
- if (!available) return false;
78
+ if (!isAvailableLead(sessionId)) return false;
66
79
  const childIds = ownedAgentSessionIds(sessionId);
67
80
  const current = getSession();
81
+ const cleanupReason = 'desktop-session-delete';
68
82
  if (current?.id !== sessionId) {
69
- const deleted = mgr.deleteSession(sessionId) === true;
83
+ // A turn can still be running in a session that is no longer current.
84
+ // Stop its runtime first (controller abort, shells, generation bump) and
85
+ // release its lead row, agents and tasks, so nothing can write it back.
86
+ if (mgr.closeSession(sessionId, cleanupReason, { tombstone: false }) !== true) return false;
87
+ agentTool?.forgetLeadSession?.(sessionId);
88
+ releaseSessionWork(sessionId, cleanupReason);
89
+ const deleted = mgr.deleteSession(sessionId, { fenceWrites: true }) === true;
70
90
  if (!deleted) return false;
71
91
  void forgetSessionSearch(sessionId);
72
92
  // The parent is already irreversibly gone, so child cleanup is
@@ -74,7 +94,7 @@ export function createSessionDelete(deps, { cancelBackgroundTasks }) {
74
94
  // because its retained-parent proof disappeared with the parent.
75
95
  for (const childId of childIds) {
76
96
  try {
77
- mgr.deleteSession(childId);
97
+ mgr.deleteSession(childId, { fenceWrites: true });
78
98
  } catch {}
79
99
  }
80
100
  void releaseDeletedWorktree(sessionId);
@@ -83,12 +103,15 @@ export function createSessionDelete(deps, { cancelBackgroundTasks }) {
83
103
  // Leaving a worktree-bound Lead: the replacement starts in the Project
84
104
  // folder, and the worktree can go once nothing runs in it.
85
105
  const projectFolder = replacementSessionCwd(current);
86
- const cleanupReason = 'desktop-session-delete';
87
106
  releaseSessionWork(sessionId, cleanupReason);
88
107
  // Active sessions retain a tombstone until the normal sweep. Unlinking
89
108
  // immediately would let a late provider/save continuation resurrect the
90
109
  // deleted conversation after the user has moved to its replacement.
91
110
  if (mgr.closeSession(sessionId, cleanupReason, { tombstone: true }) !== true) return false;
111
+ // The unwinding turn's late saves and its idle lead-row upsert must not
112
+ // touch the deleted conversation (an unguarded save would pass the tombstone).
113
+ mgr.fenceSessionWrites?.(sessionId);
114
+ agentTool?.forgetLeadSession?.(sessionId);
92
115
  void forgetSessionSearch(sessionId);
93
116
  // Active parent deletion uses the same durable tombstone boundary for
94
117
  // every linked child. Their files then mature with the parent instead of
@@ -7,11 +7,13 @@
7
7
  */
8
8
  import { SessionClosedError } from '../../runtime/agent/orchestrator/session/manager/session-errors.mjs';
9
9
  import { forgetSessionSaveBaseline } from '../../runtime/agent/orchestrator/session/store/save-worker.mjs';
10
+ import { flushAllIndexWrites } from '../services/agent-tool/index-write-queue.mjs';
10
11
  import { unregisterLiveSession } from '../../runtime/shared/staged-update.mjs';
11
12
  import { abortRuntime, closeCanonicalSession, closeOwnSession, dispatchSessionEnd } from './teardown/session-close.mjs';
12
13
  import { settleAll, settleDetached, startRuntimeStops, startWorkStops } from './teardown/runtime-stops.mjs';
13
14
 
14
- const PROCESS_EXIT_REASON = /exit|quit|shutdown|sighup|sigint|sigterm/;
15
+ const CLOSE_ALL_DEADLINE_MS = 5000;
16
+ const PROCESS_EXIT_REASON =/exit|quit|shutdown|sighup|sigint|sigterm/;
15
17
  const WARMUP_TIMER_KEYS = [
16
18
  'providerSetupWarmupTimer',
17
19
  'providerWarmupTimer',
@@ -118,6 +120,45 @@ export function createTeardown(deps, { ingestSessionIntoMemory, closeSurfaceSess
118
120
  } catch {}
119
121
  }
120
122
 
123
+ // Agent closes that have not settled yet (one per close invocation, kept
124
+ // past the bounded wait if it overran).
125
+ const outstandingCloses = new Set();
126
+
127
+ /** Persist every queued index write. A close only ever drains: the shared
128
+ * indexes stay registered (exit hook included) for the closes still to come. */
129
+ async function drainIndexWrites() {
130
+ try {
131
+ await agentTool.flushIndexWrites?.();
132
+ await flushAllIndexWrites();
133
+ } catch {}
134
+ }
135
+
136
+ // The disposal in progress (if any). New closes wait for it before starting
137
+ // their own closeAll, so no close produces writes while registrations are
138
+ // being released.
139
+ let indexDisposal = null;
140
+
141
+ /** Release this runtime's per-runtime index registrations (exit callbacks,
142
+ * approval writer, store refs) once its drain has completed. Idempotent. A
143
+ * drain that cannot complete tears nothing down: the registrations stay until
144
+ * a later close drains successfully. Exit flushing itself is process-owned. */
145
+ function disposeIndexes(settle) {
146
+ // Assign before disposal runs (deferred), clear on settlement: a synchronous
147
+ // dispose must not clear the slot before it is assigned.
148
+ indexDisposal ??= Promise.resolve()
149
+ .then(async () => {
150
+ if (typeof agentTool.disposeAsync === 'function') await agentTool.disposeAsync({ settle });
151
+ else agentTool.dispose?.({ settle });
152
+ })
153
+ .catch(() => {
154
+ /* drain failed: registrations are kept for a later retry */
155
+ })
156
+ .finally(() => {
157
+ indexDisposal = null;
158
+ });
159
+ return indexDisposal;
160
+ }
161
+
121
162
  async function close(reason = 'cli-exit', options = {}) {
122
163
  quiesce(reason);
123
164
  const scope = teardownScope(reason, options, String(getSession()?.id || ''));
@@ -160,16 +201,40 @@ export function createTeardown(deps, { ingestSessionIntoMemory, closeSurfaceSess
160
201
  // memory and must leave workers/rows for the re-materialized owner.
161
202
  // Scoped teardown closes only THIS session's workers — the worker index
162
203
  // is shared across every Lead in the process.
204
+ let closing = null;
205
+ while (indexDisposal) await indexDisposal;
163
206
  if (scope.teardownReapsWork) {
207
+ // Every close runs its OWN scope's closeAll: a scoped close for one Lead
208
+ // never stands in for a process-wide close. Closing produces final index
209
+ // writes (settled rows, tombstones); the deadline only bounds how long
210
+ // teardown waits and does not stop the close.
211
+ const settled = () => {
212
+ outstandingCloses.delete(closing);
213
+ };
214
+ closing = new Promise((resolve) =>
215
+ resolve(agentTool.closeAll(reason, scope.scopedTeardown ? { callerSessionId: scope.closingSessionId } : {}))
216
+ ).then(settled, settled);
217
+ outstandingCloses.add(closing);
164
218
  try {
165
- agentTool.closeAll(reason, scope.scopedTeardown ? { callerSessionId: scope.closingSessionId } : {});
219
+ await withTeardownDeadline(closing, CLOSE_ALL_DEADLINE_MS, false);
166
220
  } catch {}
167
221
  }
168
- // Release the worker/lead index exit registrations (persists pending rows
169
- // first). Idempotent, so repeated closes of one runtime dispose once.
170
- try {
171
- agentTool.dispose?.({ settle: scope.teardownReapsWork === true });
172
- } catch {}
222
+ // Drain after the closes settle, then release this runtime's index
223
+ // registrations. A close that overran its deadline chains this onto its
224
+ // settlement (teardown may return before then). The live outstanding set is
225
+ // re-checked after every wait and drain, so the single disposal runs only
226
+ // when no close is outstanding; the check and the disposal start share one
227
+ // tick, and closes admitted afterwards wait for it.
228
+ const finish = async () => {
229
+ for (;;) {
230
+ while (outstandingCloses.size > 0) await Promise.allSettled([...outstandingCloses]);
231
+ await drainIndexWrites();
232
+ if (outstandingCloses.size === 0) break;
233
+ }
234
+ await disposeIndexes(scope.teardownReapsWork === true);
235
+ };
236
+ if (outstandingCloses.size > 0) void finish();
237
+ else await finish();
173
238
  const { ok, stops } = await inOwnLoopTurn(() => {
174
239
  const runtimeStops = startRuntimeStops(deps, reason, scope);
175
240
  const closed = closeOwnSession(deps, closeSurfaceSession, reason);
@@ -178,7 +243,7 @@ export function createTeardown(deps, { ingestSessionIntoMemory, closeSurfaceSess
178
243
  if (scope.detach) await settleDetached(withTeardownDeadline, stops);
179
244
  else await settleAll(withTeardownDeadline, stops);
180
245
  // The session went away (idle eviction included): free its save baselines.
181
- if (scope.closingSessionId) forgetSessionSaveBaseline(scope.closingSessionId);
246
+ if (scope.closingSessionId) forgetSessionSaveBaseline(scope.closingSessionId, { releaseWriteControl: true });
182
247
  // Self-update stages in the background and swaps on the next clean launch
183
248
  // (see staged-update.mjs) — nothing installs at shutdown. On a real
184
249
  // process exit we just drop this session's live-refcount pid file so a
@@ -42,5 +42,6 @@ export function createLifecycleApi(deps) {
42
42
  resume: switching.resume,
43
43
  inheritancePreflight: inheritance.inheritancePreflight,
44
44
  inheritFrom: inheritance.inheritFrom,
45
+ inheritToNewSession: inheritance.inheritToNewSession,
45
46
  };
46
47
  }
@@ -24,11 +24,17 @@ export function createMcpConnect({
24
24
  const fingerprint = (cfg) => JSON.stringify(cfg);
25
25
  // Roots advertised by cwd-following servers track this runtime's cwd.
26
26
  const syncScopeRoot = () => mcpClient.setMcpScopeRoot?.(getMcpScopeId(), clean(getCurrentCwd?.()));
27
+ // Abort the pending interactive OAuth login of one server's connect.
28
+ const cancelMcpLogin = (name) => mcpClient.cancelMcpLogin?.(clean(name), scopeOptions());
29
+ // Answer it with the redirect URL pasted from the browser.
30
+ const completeMcpLogin = (name, callbackUrl) => mcpClient.completeMcpLogin(clean(name), callbackUrl, scopeOptions());
27
31
 
28
32
  // Connect/disconnect exactly one server in the live registry, leaving all
29
33
  // others untouched. Used by the enable/disable toggle so a single-server
30
- // change never triggers a full disconnectAll()/reconnect freeze.
31
- async function applyMcpServerConnection(name, enabled, servers = resolveEffectiveMcpServers().servers) {
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) {
32
38
  const target = clean(name);
33
39
  if (!target) return;
34
40
  // Changing this server's state clears any stale failure record for it.
@@ -46,11 +52,16 @@ export function createMcpConnect({
46
52
  // between the close and the connect never resurrects the server.
47
53
  try {
48
54
  ensurePluginServerData(cfg);
49
- await mcpClient.reconnectSingleMcpServer(getMcpScopeId(), target, cfg);
55
+ await mcpClient.reconnectSingleMcpServer(
56
+ getMcpScopeId(),
57
+ target,
58
+ cfg,
59
+ login ? { interactive: true, openBrowser: login.openBrowser } : {}
60
+ );
50
61
  } catch (error) {
51
- const failures = Array.isArray(error?.failures)
52
- ? error.failures
53
- : [{ name: target, msg: error?.message || String(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];
54
65
  state.mcpFailures = [...(state.mcpFailures || []), ...failures];
55
66
  }
56
67
  }
@@ -72,7 +83,13 @@ export function createMcpConnect({
72
83
  ]);
73
84
  }
74
85
 
75
- async function connectConfiguredMcp({ reset = false, only = null, enabled = true, reconcile = false } = {}) {
86
+ async function connectConfiguredMcp({
87
+ reset = false,
88
+ only = null,
89
+ enabled = true,
90
+ reconcile = false,
91
+ login = null,
92
+ } = {}) {
76
93
  if (envFlag('MIXDOG_DISABLE_MCP')) {
77
94
  ++state.mcpConnectGeneration;
78
95
  state.mcpFailures = [];
@@ -87,6 +104,9 @@ export function createMcpConnect({
87
104
  // been requested in the meantime. Registering as in-flight makes a later
88
105
  // reset serialize behind us instead of interleaving disconnect/connect.
89
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);
90
110
  // Atomically capture the current generation AND the prior in-flight
91
111
  // promise in the same synchronous step, then chain our op onto it. No
92
112
  // await sits between the capture and the `state.mcpConnectInFlight = run`
@@ -106,7 +126,7 @@ export function createMcpConnect({
106
126
  }
107
127
  if (gen !== state.mcpConnectGeneration) return;
108
128
  if (reconcile) await reconcileServers();
109
- else await applyMcpServerConnection(only, enabled);
129
+ else await applyMcpServerConnection(only, enabled, undefined, login);
110
130
  })();
111
131
  state.mcpConnectInFlight = run;
112
132
  try {
@@ -189,23 +209,35 @@ export function createMcpConnect({
189
209
  budgetMs = Math.min(budgetMs, requestedMaxWaitMs);
190
210
  }
191
211
  }
192
- // Swallow the in-flight rejection: failures are already captured in
193
- // state.mcpFailures, and this gate must never reject the turn.
194
- const settled = Promise.resolve(inFlight).catch(() => {});
195
212
  // Budget disabled (0/off) = no per-server startup timeout, so the connect
196
213
  // promise may never settle; never gate the turn on it — fall back to the
197
214
  // legacy fire-and-forget behavior (late servers use the deferred path).
198
215
  if (!(budgetMs > 0)) return;
199
216
  let timer = null;
217
+ let expired = false;
200
218
  const budget = new Promise((resolveBudget) => {
201
- timer = setTimeout(resolveBudget, budgetMs);
219
+ timer = setTimeout(() => {
220
+ expired = true;
221
+ resolveBudget();
222
+ }, budgetMs);
202
223
  });
203
224
  try {
204
- await Promise.race([settled, budget]);
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
+ }
205
237
  } finally {
206
238
  if (timer) clearTimeout(timer);
207
239
  }
208
240
  }
209
241
 
210
- return { connectConfiguredMcp, awaitInitialMcpConnect };
242
+ return { connectConfiguredMcp, awaitInitialMcpConnect, cancelMcpLogin, completeMcpLogin };
211
243
  }
@@ -5,9 +5,11 @@ import { clean } from '../../runtime/agent/orchestrator/runtime-core/session-tex
5
5
  import { envFlag } from '../../runtime/shared/env.mjs';
6
6
  import { resolveMcpServerCatalog } from './plugin-servers.mjs';
7
7
 
8
- function mcpServerStatus(cfg, live, fail) {
8
+ function mcpServerStatus(cfg, live, fail, loginPending) {
9
9
  if (cfg?.enabled === false) return 'disabled';
10
10
  if (live) return 'connected';
11
+ // Until an interactive sign-in completes the server still needs it.
12
+ if (loginPending || fail?.authRequired) return 'sign-in required';
11
13
  return fail ? 'failed' : 'disconnected';
12
14
  }
13
15
 
@@ -43,6 +45,12 @@ export function createMcpServerCatalog({
43
45
  * runtime's session cwd. */
44
46
  function resolveCatalog() {
45
47
  if (envFlag('MIXDOG_DISABLE_MCP')) return EMPTY_CATALOG;
48
+ return resolveConfiguredCatalog();
49
+ }
50
+
51
+ /** Every configured server (user config plus plugin declarations, disabled
52
+ * ones included) whether or not this runtime connects MCP at all. */
53
+ function resolveConfiguredCatalog() {
46
54
  let plugins = [];
47
55
  let registryWarning = null;
48
56
  try {
@@ -115,20 +123,28 @@ export function createMcpServerCatalog({
115
123
  const fail =
116
124
  failures.get(name) || (dead ? { msg: live.error || live.lastError || 'MCP server is not alive' } : undefined);
117
125
  const entry = entries[name] || {};
126
+ const loginPending = !(live && !dead) && mcpClient.isMcpLoginPending?.(name, getMcpScopeId()) === true;
127
+ const status = mcpServerStatus(cfg, live && !dead, fail, loginPending);
128
+ // Needing an OAuth sign-in is a state, not an error.
129
+ const authRequired = status === 'sign-in required';
118
130
  const row = {
119
131
  name,
120
132
  configured: true,
121
133
  enabled: cfg?.enabled !== false,
122
134
  connected: Boolean(live) && !dead,
123
- status: mcpServerStatus(cfg, live && !dead, fail),
135
+ status,
124
136
  transport: mcpTransportLabel(cfg),
125
137
  toolCount: live?.toolCount || 0,
126
138
  tools: live?.tools || [],
127
139
  capabilities: live?.capabilities || { tools: false, prompts: false, resources: false },
128
- error: fail?.msg || null,
140
+ error: authRequired ? null : fail?.msg || null,
129
141
  warnings: [...(entry.warnings || []), ...(live?.warnings || [])],
130
142
  source: sources[name] || 'config',
131
143
  };
144
+ if (authRequired) row.authRequired = true;
145
+ const oauth = mcpClient.getMcpServerOAuthState?.(cfg);
146
+ // Why the last sign-in did not complete (already credential-scrubbed).
147
+ if (oauth) row.oauth = authRequired && fail?.msg ? { ...oauth, lastError: fail.msg } : oauth;
132
148
  if (entry.owner) {
133
149
  row.owner = entry.owner;
134
150
  row.pluginEnabled = entry.pluginEnabled;
@@ -180,6 +196,7 @@ export function createMcpServerCatalog({
180
196
  return {
181
197
  mcpTransportLabel,
182
198
  resolveCatalog,
199
+ resolveConfiguredCatalog,
183
200
  resolveEffectiveMcpServers,
184
201
  pluginServer,
185
202
  getMcpServerConfig,
@@ -37,11 +37,14 @@ export function createMcpGlue({
37
37
  return {
38
38
  mcpStatus: catalog.mcpStatus,
39
39
  resolveMcpCatalog: catalog.resolveCatalog,
40
+ resolveConfiguredMcpCatalog: catalog.resolveConfiguredCatalog,
40
41
  pluginMcpServer: catalog.pluginServer,
41
42
  pluginMcpSummary: catalog.pluginMcpSummary,
42
43
  getMcpServerConfig: catalog.getMcpServerConfig,
43
44
  connectConfiguredMcp: connect.connectConfiguredMcp,
44
45
  awaitInitialMcpConnect: connect.awaitInitialMcpConnect,
46
+ cancelMcpLogin: connect.cancelMcpLogin,
47
+ completeMcpLogin: connect.completeMcpLogin,
45
48
  normalizeMcpServerInput,
46
49
  };
47
50
  }