mixdog 1.2.0 → 1.2.2

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 (386) hide show
  1. package/README.md +7 -5
  2. package/package.json +1 -1
  3. package/scripts/gate-local.mjs +19 -3
  4. package/scripts/model-catalog-audit.mjs +1 -15
  5. package/src/agents/reviewer/AGENT.md +3 -3
  6. package/src/agents/reviewer/agent.json +1 -1
  7. package/src/defaults/shipped-history.json +5 -0
  8. package/src/defaults/skills/pptx/SKILL.md +6 -6
  9. package/src/defaults/skills/pptx/references/review.md +6 -4
  10. package/src/defaults/skills/pptx/scripts/source-extract.mjs +1 -1
  11. package/src/defaults/skills/setup/references/actions.md +13 -3
  12. package/src/defaults/skills/visualize/SKILL.md +12 -9
  13. package/src/repl/slash-commands.mjs +16 -1
  14. package/src/rules/shared/60-verification.md +3 -1
  15. package/src/rules/shared/70-delivery.md +4 -2
  16. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -4
  17. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +10 -0
  18. package/src/runtime/agent/orchestrator/cache-mtime.mjs +26 -5
  19. package/src/runtime/agent/orchestrator/config.mjs +10 -4
  20. package/src/runtime/agent/orchestrator/context/collect.mjs +2 -1
  21. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +11 -5
  22. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +74 -21
  23. package/src/runtime/agent/orchestrator/mcp/client-config.mjs +122 -15
  24. package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +54 -11
  25. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +104 -9
  26. package/src/runtime/agent/orchestrator/mcp/client.mjs +358 -140
  27. package/src/runtime/agent/orchestrator/mcp/lazy-output-validator.mjs +25 -0
  28. package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +103 -0
  29. package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +150 -0
  30. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +111 -0
  31. package/src/runtime/agent/orchestrator/mcp/oauth-probe.mjs +87 -0
  32. package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +95 -0
  33. package/src/runtime/agent/orchestrator/mcp/oauth-request.mjs +122 -0
  34. package/src/runtime/agent/orchestrator/mcp/oauth-shared.mjs +9 -0
  35. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +229 -0
  36. package/src/runtime/agent/orchestrator/mcp/plugin-placeholders.mjs +54 -0
  37. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +294 -44
  38. package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +2 -1
  39. package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +94 -31
  40. package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +96 -0
  41. package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.mjs +39 -18
  42. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +55 -0
  43. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +25 -21
  44. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +3 -1
  45. package/src/runtime/agent/orchestrator/providers/anthropic-midstream-recovery.mjs +18 -4
  46. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +13 -1
  47. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  48. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-midstream.mjs +10 -1
  49. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-recovery.mjs +23 -9
  50. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +46 -20
  51. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request.mjs +14 -9
  52. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +73 -35
  53. package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +69 -0
  54. package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.mjs +4 -0
  55. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +8 -1
  56. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +53 -18
  57. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +30 -9
  58. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +7 -0
  59. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +30 -14
  60. package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +2 -1
  61. package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +93 -0
  62. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +82 -9
  63. package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +9 -0
  64. package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +337 -0
  65. package/src/runtime/agent/orchestrator/providers/command-code-oauth-tokens.mjs +159 -0
  66. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +447 -0
  67. package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +38 -5
  68. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  69. package/src/runtime/agent/orchestrator/providers/cursor-wire-guards.mjs +97 -2
  70. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +102 -23
  71. package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +103 -33
  72. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +29 -6
  73. package/src/runtime/agent/orchestrator/providers/cursor.mjs +18 -1
  74. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +7 -4
  75. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +86 -19
  76. package/src/runtime/agent/orchestrator/providers/gemini-cache-policy.mjs +1 -1
  77. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +29 -8
  78. package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +191 -0
  79. package/src/runtime/agent/orchestrator/providers/gemini-family.mjs +9 -0
  80. package/src/runtime/agent/orchestrator/providers/gemini-history-signatures.mjs +9 -4
  81. package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +3 -1
  82. package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +180 -0
  83. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +21 -82
  84. package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +4 -1
  85. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +71 -12
  86. package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +21 -20
  87. package/src/runtime/agent/orchestrator/providers/gemini.mjs +97 -51
  88. package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
  89. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +68 -19
  90. package/src/runtime/agent/orchestrator/providers/lib/gemini-model-catalog.mjs +7 -1
  91. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +3 -0
  92. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +2 -1
  93. package/src/runtime/agent/orchestrator/providers/model-proxy-bridge.mjs +1080 -0
  94. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +15 -0
  95. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +35 -0
  96. package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +4 -9
  97. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +12 -0
  98. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +6 -5
  99. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +5 -3
  100. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-result.mjs +22 -0
  101. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +1 -0
  102. package/src/runtime/agent/orchestrator/providers/openai-compat-error-contract.mjs +50 -0
  103. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +6 -2
  104. package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +11 -0
  105. package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +23 -0
  106. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +16 -8
  107. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-state.mjs +3 -2
  108. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +94 -13
  109. package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +193 -0
  110. package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +66 -2
  111. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +2 -1
  112. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +6 -0
  113. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +6 -3
  114. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +23 -14
  115. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +26 -6
  116. package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +42 -25
  117. package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +274 -0
  118. package/src/runtime/agent/orchestrator/providers/openai-http-sse-request.mjs +72 -32
  119. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +18 -11
  120. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-outcome.mjs +1 -2
  121. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-stamps.mjs +2 -2
  122. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-state.mjs +2 -3
  123. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +2 -2
  124. package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +1 -3
  125. package/src/runtime/agent/orchestrator/providers/openai-oauth-dispatch.mjs +34 -3
  126. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +5 -1
  127. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +22 -2
  128. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +5 -0
  129. package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +5 -9
  130. package/src/runtime/agent/orchestrator/providers/openai-retry-policy.mjs +9 -0
  131. package/src/runtime/agent/orchestrator/providers/openai-ws-handshake.mjs +16 -8
  132. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +1 -2
  133. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/policy.mjs +25 -14
  134. package/src/runtime/agent/orchestrator/providers/openai-ws-terminal.mjs +18 -11
  135. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +35 -24
  136. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +581 -0
  137. package/src/runtime/agent/orchestrator/providers/registry.mjs +35 -9
  138. package/src/runtime/agent/orchestrator/providers/retry-backoff.mjs +46 -7
  139. package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +118 -219
  140. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +2 -0
  141. package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +148 -45
  142. package/src/runtime/agent/orchestrator/providers/retry-midstream.mjs +81 -16
  143. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +3 -0
  144. package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +3 -0
  145. package/src/runtime/agent/orchestrator/runtime-core/extension-scopes.mjs +15 -55
  146. package/src/runtime/agent/orchestrator/runtime-core/goal-control.mjs +15 -2
  147. package/src/runtime/agent/orchestrator/runtime-core/goal-reminder.mjs +0 -18
  148. package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +57 -28
  149. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +19 -16
  150. package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +268 -181
  151. package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +4 -1
  152. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +21 -0
  153. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +54 -3
  154. package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +284 -0
  155. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +270 -0
  156. package/src/runtime/agent/orchestrator/session/catalog-db/sqlite-gate.mjs +27 -0
  157. package/src/runtime/agent/orchestrator/session/catalog-db/worker.mjs +628 -0
  158. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +6 -2
  159. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +184 -0
  160. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +136 -0
  161. package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +128 -0
  162. package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +127 -0
  163. package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +101 -0
  164. package/src/runtime/agent/orchestrator/session/explicit-skills.mjs +83 -5
  165. package/src/runtime/agent/orchestrator/session/imported-message.mjs +11 -0
  166. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +2 -1
  167. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -1
  168. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +3 -2
  169. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +10 -4
  170. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +4 -3
  171. package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +102 -18
  172. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -0
  173. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +3 -1
  174. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +42 -5
  175. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +14 -3
  176. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +20 -12
  177. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +50 -23
  178. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +4 -0
  179. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +22 -14
  180. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +1 -1
  181. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +18 -7
  182. package/src/runtime/agent/orchestrator/session/manager.mjs +0 -1
  183. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +4 -3
  184. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +155 -29
  185. package/src/runtime/agent/orchestrator/session/store/listing.mjs +6 -2
  186. package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +6 -3
  187. package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +6 -0
  188. package/src/runtime/agent/orchestrator/session/store/write-guards.mjs +16 -0
  189. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +22 -10
  190. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +819 -0
  191. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +12 -0
  192. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +22 -12
  193. package/src/runtime/agent/orchestrator/session/store-summary-reader-catalog-fixture.mjs +158 -0
  194. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +183 -374
  195. package/src/runtime/agent/orchestrator/session/synthetic-user-envelope.mjs +3 -1
  196. package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +1 -1
  197. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +41 -3
  198. package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +38 -17
  199. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +2 -2
  200. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +12 -15
  201. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +17 -1
  202. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -0
  203. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +5 -5
  204. package/src/runtime/agent/orchestrator/tools/builtin/lib/list-helpers.mjs +2 -2
  205. package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +6 -6
  206. package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +173 -22
  207. package/src/runtime/agent/orchestrator/tools/builtin/read-single-guards.mjs +8 -4
  208. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +5 -4
  209. package/src/runtime/agent/orchestrator/tools/builtin/read-source-windows.mjs +15 -2
  210. package/src/runtime/agent/orchestrator/tools/builtin/search-glob-tool.mjs +13 -10
  211. package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +1 -1
  212. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +17 -12
  213. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +2 -2
  214. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +1 -1
  215. package/src/runtime/agent/orchestrator/tools/patch/apply-patch/codex-batch.mjs +4 -4
  216. package/src/runtime/agent/orchestrator/tools/patch/apply-patch/route.mjs +2 -2
  217. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +1 -1
  218. package/src/runtime/agent/orchestrator/tools/patch/read-redirects.mjs +34 -30
  219. package/src/runtime/agent/orchestrator/tools/patch/sequence/units.mjs +7 -7
  220. package/src/runtime/agent/orchestrator/tools/patch/sequence.mjs +1 -1
  221. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +4 -0
  222. package/src/runtime/attachments/store.d.mts +3 -0
  223. package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +1 -1
  224. package/src/runtime/channels/lib/webhook/session-dispatch.mjs +16 -4
  225. package/src/runtime/channels/lib/worker-main.mjs +17 -14
  226. package/src/runtime/computer-bridge/linux-readiness.mjs +0 -2
  227. package/src/runtime/effort-judge/judge-client.mjs +56 -18
  228. package/src/runtime/memory/lib/memory.mjs +1 -1
  229. package/src/runtime/memory/lib/pg/process.mjs +34 -24
  230. package/src/runtime/memory/lib/pg/supervisor.mjs +16 -13
  231. package/src/runtime/memory/lib/session-ingest.mjs +2 -2
  232. package/src/runtime/office/README.md +1 -1
  233. package/src/runtime/office/shared/values.mjs +0 -5
  234. package/src/runtime/shared/atomic-file.mjs +25 -4
  235. package/src/runtime/shared/background-tasks.mjs +92 -14
  236. package/src/runtime/shared/err-text.mjs +25 -0
  237. package/src/runtime/shared/error-presentation.mjs +159 -19
  238. package/src/runtime/shared/extension-scopes.mjs +5 -3
  239. package/src/runtime/shared/file-lock.mjs +37 -11
  240. package/src/runtime/shared/llm/anthropic-betas.mjs +4 -3
  241. package/src/runtime/shared/llm/anthropic-effort.mjs +134 -131
  242. package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +50 -14
  243. package/src/runtime/shared/llm/model-catalog.mjs +53 -52
  244. package/src/runtime/shared/llm/openai-daybreak.mjs +4 -16
  245. package/src/runtime/shared/llm/provider-catalog-registry.mjs +17 -0
  246. package/src/runtime/shared/llm/provider-model-identities.mjs +13 -4
  247. package/src/runtime/shared/llm/usage-rollup.mjs +0 -1
  248. package/src/runtime/shared/model-display.mjs +13 -0
  249. package/src/runtime/shared/plugin-manifest.mjs +18 -9
  250. package/src/runtime/shared/pristine-execution-contract.json +9 -2
  251. package/src/runtime/shared/pristine-execution.mjs +28 -4
  252. package/src/runtime/shared/provider-accounts.mjs +124 -20
  253. package/src/runtime/shared/provider-api-key.mjs +12 -0
  254. package/src/runtime/shared/quota-resume-store.mjs +11 -0
  255. package/src/runtime/shared/session-baseline-store.mjs +960 -0
  256. package/src/runtime/shared/shell-display.mjs +9 -3
  257. package/src/runtime/shared/skill-mentions.d.mts +10 -0
  258. package/src/runtime/shared/skill-mentions.mjs +133 -0
  259. package/src/runtime/shared/skill-script-runtime.mjs +264 -0
  260. package/src/runtime/shared/statusline/statusline-format.mjs +4 -4
  261. package/src/runtime/shared/task-notification-envelope.mjs +2 -2
  262. package/src/runtime/shared/tool-primitives.mjs +35 -2
  263. package/src/runtime/shared/turn-changes.mjs +447 -219
  264. package/src/runtime/shared/turn-snapshot-store.mjs +182 -1
  265. package/src/runtime/shared/turn-snapshot.mjs +170 -48
  266. package/src/runtime/shared/turn-worktree-snapshot.mjs +160 -51
  267. package/src/runtime/shared/worker-requests.mjs +15 -2
  268. package/src/runtime/shared/worktrees/contract.mjs +0 -24
  269. package/src/runtime/shared/worktrees/test-support.mjs +19 -4
  270. package/src/session-runtime/automation-agents.mjs +8 -2
  271. package/src/session-runtime/boot/apis.mjs +61 -0
  272. package/src/session-runtime/boot/config.mjs +5 -2
  273. package/src/session-runtime/boot/facade.mjs +1 -0
  274. package/src/session-runtime/boot/infrastructure.mjs +35 -18
  275. package/src/session-runtime/conversation-import/discovery.mjs +698 -0
  276. package/src/session-runtime/conversation-import/service.mjs +369 -0
  277. package/src/session-runtime/cwd-plugins/cwd-apply.mjs +10 -5
  278. package/src/session-runtime/cwd-plugins/plugin-status.mjs +47 -42
  279. package/src/session-runtime/lifecycle/session-catalog.mjs +2 -0
  280. package/src/session-runtime/lifecycle/teardown.mjs +1 -1
  281. package/src/session-runtime/mcp-glue/connect.mjs +83 -16
  282. package/src/session-runtime/mcp-glue/plugin-servers.mjs +499 -0
  283. package/src/session-runtime/mcp-glue/server-catalog.mjs +135 -33
  284. package/src/session-runtime/mcp-glue.mjs +25 -2
  285. package/src/session-runtime/mcp-oauth-login.mjs +174 -0
  286. package/src/session-runtime/model-route/model-switch-target.mjs +34 -0
  287. package/src/session-runtime/model-route/set-route.mjs +83 -15
  288. package/src/session-runtime/model-route/web-search-route.mjs +1 -1
  289. package/src/session-runtime/native-web-search/routes.mjs +22 -3
  290. package/src/session-runtime/new-session-config.mjs +3 -2
  291. package/src/session-runtime/notification-bus.mjs +9 -2
  292. package/src/session-runtime/provider-auth/accounts.mjs +33 -15
  293. package/src/session-runtime/provider-models/model-meta.mjs +12 -3
  294. package/src/session-runtime/provider-models/web-search-catalog.mjs +5 -1
  295. package/src/session-runtime/provider-readiness.mjs +16 -10
  296. package/src/session-runtime/quick-model-rows/web-search-rows.mjs +13 -8
  297. package/src/session-runtime/quick-web-search-models.mjs +13 -38
  298. package/src/session-runtime/resource-extension-sync.mjs +24 -5
  299. package/src/session-runtime/resource-mcp-api.mjs +198 -78
  300. package/src/session-runtime/resource-plugins-api.mjs +29 -41
  301. package/src/session-runtime/resource-scope-decorate.mjs +6 -8
  302. package/src/session-runtime/resource-skills-api.mjs +23 -2
  303. package/src/session-runtime/runtime-review-api.mjs +13 -0
  304. package/src/session-runtime/schedule-session-run.mjs +1 -1
  305. package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +13 -1
  306. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +7 -2
  307. package/src/session-runtime/services/agent-tool/send-flow.mjs +22 -4
  308. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +2 -0
  309. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +22 -5
  310. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +8 -1
  311. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -2
  312. package/src/session-runtime/services/agent-tool/worker-rows.mjs +17 -0
  313. package/src/session-runtime/services/channel-session-router.mjs +4 -1
  314. package/src/session-runtime/services/hook-bus/command-handler.mjs +4 -1
  315. package/src/session-runtime/services/hook-bus/handlers.mjs +7 -3
  316. package/src/session-runtime/services/provider-admin.mjs +64 -18
  317. package/src/session-runtime/services/usage-dashboard-model.mjs +3 -0
  318. package/src/session-runtime/settings-compaction-api.mjs +1 -0
  319. package/src/session-runtime/setup-tool/executor.mjs +12 -1
  320. package/src/session-runtime/setup-tool/extended-actions.mjs +1 -0
  321. package/src/session-runtime/setup-tool/settings-contract.mjs +1 -0
  322. package/src/session-runtime/setup-tool/tool-defs.mjs +7 -1
  323. package/src/session-runtime/skills-api.mjs +7 -3
  324. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
  325. package/src/session-runtime/webhook-session-run.mjs +16 -4
  326. package/src/skill-script-entry.mjs +36 -0
  327. package/src/standalone/daemon-drain-policy.mjs +8 -0
  328. package/src/standalone/daemon-stored-session-views.mjs +11 -3
  329. package/src/standalone/daemon-telemetry.mjs +55 -0
  330. package/src/standalone/daemon.mjs +44 -11
  331. package/src/standalone/session-protocol.mjs +12 -0
  332. package/src/standalone/session-service/agent-tree/agent-cancel.mjs +33 -2
  333. package/src/standalone/session-service/agent-tree/agent-turns.mjs +26 -10
  334. package/src/standalone/session-service/agent-tree.mjs +12 -0
  335. package/src/standalone/session-service/quota-resume.mjs +8 -0
  336. package/src/standalone/session-service/retention/busy.mjs +15 -1
  337. package/src/standalone/session-service/session-calls/session-action.mjs +6 -1
  338. package/src/standalone/session-service-api.mjs +5 -0
  339. package/src/standalone/session-service.mjs +1 -0
  340. package/src/tui/App.jsx +9 -1
  341. package/src/tui/app/app-view/floating-panel.jsx +9 -1
  342. package/src/tui/app/app-view.jsx +7 -0
  343. package/src/tui/app/create-app-pickers.mjs +4 -0
  344. package/src/tui/app/extension-pickers/mcp-login.mjs +107 -0
  345. package/src/tui/app/extension-pickers/mcp-servers-picker.mjs +2 -1
  346. package/src/tui/app/extension-pickers/plugins-pickers.mjs +35 -16
  347. package/src/tui/app/extension-pickers.mjs +2 -1
  348. package/src/tui/app/model-options.mjs +13 -16
  349. package/src/tui/app/skill-mention-palette.mjs +88 -0
  350. package/src/tui/app/slash-commands.mjs +7 -2
  351. package/src/tui/app/slash-dispatch/panel-commands.mjs +16 -1
  352. package/src/tui/app/slash-palette.mjs +23 -7
  353. package/src/tui/app/transcript-row-estimate.mjs +11 -3
  354. package/src/tui/app/use-prompt-draft-flow.mjs +7 -2
  355. package/src/tui/app/use-prompt-state.mjs +5 -0
  356. package/src/tui/components/Message.jsx +22 -1
  357. package/src/tui/components/PromptInput.jsx +21 -3
  358. package/src/tui/components/SlashCommandPalette.jsx +12 -3
  359. package/src/tui/components/StatusLine.jsx +2 -0
  360. package/src/tui/components/TranscriptItem.jsx +9 -1
  361. package/src/tui/components/prompt-input/key-handler.mjs +21 -8
  362. package/src/tui/components/status-line/local-line.mjs +5 -2
  363. package/src/tui/components/status-line/use-statusline-text.mjs +7 -1
  364. package/src/tui/components/user-message-skills.mjs +38 -0
  365. package/src/tui/dist/index.mjs +1323 -214
  366. package/src/tui/session/queue-helpers.mjs +7 -2
  367. package/src/tui/session/quota-resume.mjs +46 -14
  368. package/src/tui/session/session-api/commands.mjs +33 -24
  369. package/src/tui/session/session-api/extensions.mjs +26 -2
  370. package/src/tui/session/session-api/integrations.mjs +7 -0
  371. package/src/tui/session/session-api/lifecycle.mjs +5 -1
  372. package/src/tui/session/session-api/routes.mjs +71 -7
  373. package/src/tui/session/session-api/settings.mjs +12 -14
  374. package/src/tui/session/session-flow/drain.mjs +21 -5
  375. package/src/tui/session/session-flow/failed-turn-rewind.mjs +10 -4
  376. package/src/tui/session/tui-steering-persist.mjs +25 -9
  377. package/src/tui/session/turn.mjs +55 -15
  378. package/src/tui/statusline-ansi-bridge.mjs +1 -1
  379. package/src/ui/statusline-agents.mjs +15 -4
  380. package/src/ui/statusline.mjs +7 -3
  381. package/src/ui/streaming-markdown-heal.mjs +34 -3
  382. package/src/vendor/statusline/src/gateway/claude-current.mjs +14 -4
  383. package/src/runtime/effort-judge/previous-human-request.mjs +0 -162
  384. package/src/runtime/effort-judge/verified-context-input.mjs +0 -10
  385. package/src/runtime/memory/lib/memory-session-merge.mjs +0 -32
  386. package/src/runtime/shared/llm/usage-session-history.mjs +0 -266
@@ -1,7 +1,7 @@
1
1
  import { createHash } from 'node:crypto';
2
- import { existsSync, realpathSync } from 'node:fs';
2
+ import { existsSync, lstatSync, realpathSync } from 'node:fs';
3
3
  import { access, copyFile, lstat, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
4
- import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
4
+ import { basename, dirname, isAbsolute, join, relative, resolve } from 'node:path';
5
5
  import { PATCH_EDIT_MAX_BYTES } from './bounded-file-read.mjs';
6
6
  import { cleanString as clean } from './clean.mjs';
7
7
  import { runGitOffThread } from './git-runner.mjs';
@@ -44,8 +44,26 @@ function pathKey(value) {
44
44
 
45
45
  /** `resolve` plus symlinks of the nearest existing ancestor: git reports the
46
46
  * worktree root canonically (macOS /var -> /private/var), callers may not. */
47
- function canonicalPath(value) {
47
+ function canonicalPath(value, dirs = null) {
48
48
  const full = resolve(value);
49
+ if (dirs) {
50
+ // Same result as the walk below without repeating it per file: the parent
51
+ // directory is canonicalized once per call; the final component only
52
+ // matters when it is itself a link (or its state is unknown).
53
+ const parent = dirname(full);
54
+ if (parent !== full) {
55
+ let canonicalParent = dirs.get(parent);
56
+ if (canonicalParent === undefined) {
57
+ canonicalParent = canonicalPath(parent);
58
+ dirs.set(parent, canonicalParent);
59
+ }
60
+ try {
61
+ if (!lstatSync(full).isSymbolicLink()) return join(canonicalParent, basename(full));
62
+ } catch (error) {
63
+ if (error?.code === 'ENOENT' || error?.code === 'ENOTDIR') return join(canonicalParent, basename(full));
64
+ }
65
+ }
66
+ }
49
67
  let head = full;
50
68
  while (!existsSync(head)) {
51
69
  const parent = dirname(head);
@@ -139,8 +157,10 @@ function stateForRoot(root) {
139
157
  /** Report written paths (or `null`: anything may have changed). The next
140
158
  * review poll of an affected worktree captures instead of reusing. */
141
159
  export function noteTurnWorktreeChange(paths = null) {
160
+ if (states.size === 0) return;
142
161
  const list = Array.isArray(paths) ? paths.filter((value) => clean(value)) : [];
143
- const keys = list.map((value) => pathKey(canonicalPath(value)));
162
+ const dirs = new Map();
163
+ const keys = list.map((value) => pathKey(canonicalPath(value, dirs)));
144
164
  for (const state of states.values()) {
145
165
  const root = pathKey(state.root);
146
166
  if (keys.length === 0 || keys.some((key) => key === root || key.startsWith(`${root}/`))) {
@@ -149,8 +169,30 @@ export function noteTurnWorktreeChange(paths = null) {
149
169
  }
150
170
  }
151
171
 
152
- function withStateLock(state, task) {
153
- const next = state.lock.then(task, task);
172
+ // A lock hold (queue wait plus work) at least this long is logged, so a slow
173
+ // review bar can be told apart as waiting behind other captures or slow git.
174
+ const SLOW_LOCK_LOG_MS = 1_000;
175
+
176
+ function withStateLock(state, task, op = 'task') {
177
+ const queuedAt = Date.now();
178
+ const queuedBehind = state.lockDepth || 0;
179
+ state.lockDepth = queuedBehind + 1;
180
+ let startedAt = 0;
181
+ const timed = async () => {
182
+ startedAt = Date.now();
183
+ return await task();
184
+ };
185
+ const next = state.lock.then(timed, timed).finally(() => {
186
+ state.lockDepth -= 1;
187
+ const finishedAt = Date.now();
188
+ const waitMs = (startedAt || finishedAt) - queuedAt;
189
+ if (finishedAt - queuedAt >= SLOW_LOCK_LOG_MS) {
190
+ console.info(
191
+ `[turn-snapshot-lock] op=${op} waitMs=${waitMs} workMs=${finishedAt - (startedAt || finishedAt)} ` +
192
+ `queuedBehind=${queuedBehind} root=${state.root}`
193
+ );
194
+ }
195
+ });
154
196
  state.lock = next.then(
155
197
  () => undefined,
156
198
  () => undefined
@@ -318,6 +360,7 @@ async function ensureState(state) {
318
360
  }
319
361
 
320
362
  async function resetShadowIndex(state) {
363
+ state.lastCapture = null;
321
364
  state.sourceIndexPath ??= await sourceGitPath(state.root, 'index');
322
365
  const sourceIndex = state.sourceIndexPath;
323
366
  const target = join(state.gitDir, 'index');
@@ -380,10 +423,33 @@ async function changedWorktreePaths(state) {
380
423
  };
381
424
  }
382
425
 
383
- async function captureTreeUnlocked(state, toolPaths = []) {
426
+ /** The capture sequence a caller observes before queueing on the lock. */
427
+ function captureSequence(state) {
428
+ return state.captureSeq || 0;
429
+ }
430
+
431
+ /** `after` (a captureSequence() read before queueing): a capture of this root
432
+ * that began after that point, with no reported write since and covering
433
+ * every explicit path, already is a capture that began after the call. Many
434
+ * sessions of one repository queue reads together; they share that capture
435
+ * instead of each re-scanning the worktree in turn. */
436
+ async function captureTreeUnlocked(state, toolPaths = [], { after = -1 } = {}) {
384
437
  await ensureState(state);
385
- const { paths, excludedUntracked } = await changedWorktreePaths(state);
386
438
  const explicit = new Set(toolPaths);
439
+ const last = state.lastCapture;
440
+ if (
441
+ after >= 0 &&
442
+ last &&
443
+ last.seq > after &&
444
+ last.stamp === state.changeStamp &&
445
+ [...explicit].every((rel) => last.explicit.has(rel))
446
+ ) {
447
+ return last.tree;
448
+ }
449
+ state.captureSeq = captureSequence(state) + 1;
450
+ const capture = { seq: state.captureSeq, stamp: state.changeStamp, explicit, tree: '' };
451
+ state.lastCapture = null;
452
+ const { paths, excludedUntracked } = await changedWorktreePaths(state);
387
453
  for (const rel of explicit) {
388
454
  try {
389
455
  const stat = await lstat(resolve(state.root, rel));
@@ -412,7 +478,9 @@ async function captureTreeUnlocked(state, toolPaths = []) {
412
478
  });
413
479
  }
414
480
  const tree = await runGit(shadowArgs(state, ['write-tree']), { cwd: state.root });
415
- return clean(tree.stdout);
481
+ capture.tree = clean(tree.stdout);
482
+ state.lastCapture = capture;
483
+ return capture.tree;
416
484
  }
417
485
 
418
486
  /** Only explicit tool targets bypass ignore/size filters. Their first-before
@@ -781,7 +849,7 @@ function scheduleShadowGc(state) {
781
849
  async function removeShadowDir(gitDir, removed, kept) {
782
850
  if (!(await exists(gitDir))) return;
783
851
  await rm(gitDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 }).catch(() => {});
784
- (await exists(gitDir) ? kept : removed).push(gitDir);
852
+ ((await exists(gitDir)) ? kept : removed).push(gitDir);
785
853
  }
786
854
 
787
855
  /** Drop every trace of one worktree root: serialize behind its pending capture
@@ -855,39 +923,74 @@ export async function resumeTurnWorktreeSnapshot(
855
923
  const root = await repositoryRoot(worktree).catch(() => null);
856
924
  if (!root) return null;
857
925
  const state = stateForRoot(root);
858
- return await withStateLock(state, async () => {
859
- await ensureState(state);
860
- // A collected or never-written baseline restores nothing. Report that as
861
- // "no snapshot" instead of failing every later call made against it.
862
- const kind = await runGit(shadowArgs(state, ['cat-file', '-t', tree]), {
926
+ const after = captureSequence(state);
927
+ return await withStateLock(
928
+ state,
929
+ async () => {
930
+ await ensureState(state);
931
+ // A collected or never-written baseline restores nothing. Report that as
932
+ // "no snapshot" instead of failing every later call made against it.
933
+ const kind = await runGit(shadowArgs(state, ['cat-file', '-t', tree]), {
934
+ cwd: state.root,
935
+ allowFailure: true,
936
+ });
937
+ if (kind.code !== 0 || clean(kind.stdout) !== 'tree') return null;
938
+ const scopePaths = Array.isArray(paths) && paths.length > 0 ? [...new Set(paths)] : null;
939
+ const applied = await applyBaselineFilesUnlocked(state, tree, baselineFiles || []);
940
+ const snapshot = {
941
+ state,
942
+ root,
943
+ baselineTree: applied.tree,
944
+ currentTree: tree,
945
+ patch: '',
946
+ files: [],
947
+ patchTruncated: false,
948
+ scopePaths,
949
+ toolPaths: new Set(scopePaths || []),
950
+ pendingBaselines: new Map(),
951
+ baselineFiles: new Map(applied.files.map((entry) => [pathKey(entry.path), entry])),
952
+ };
953
+ const currentTree = await captureTreeUnlocked(state, snapshot.toolPaths, { after });
954
+ Object.assign(
955
+ snapshot,
956
+ withOmittedBaselines(
957
+ snapshot,
958
+ await diffTreesUnlocked(state, snapshot.baselineTree, currentTree, scopePaths, { omitPatch })
959
+ )
960
+ );
961
+ return snapshot;
962
+ },
963
+ 'resume'
964
+ );
965
+ }
966
+
967
+ // Git's well-known empty tree: what an unborn HEAD (no commit yet) compares as.
968
+ const EMPTY_TREE = '4b825dc642cb6eb9a060e54bf8d69288fbee4904';
969
+
970
+ /** The files among `paths` whose bytes in the snapshot's last capture still
971
+ * differ from the source repository's current HEAD (staged or not), with
972
+ * their counts against HEAD; no patch text. An ignored file that HEAD does
973
+ * not hold is no uncommitted change and is left out. */
974
+ export async function diffTurnWorktreeHead(snapshot, paths) {
975
+ const scope = [...new Set((paths || []).filter(Boolean))];
976
+ if (!snapshot?.state || !snapshot.currentTree || scope.length === 0) return [];
977
+ return await withStateLock(snapshot.state, async () => {
978
+ const { state } = snapshot;
979
+ const head = await runGit(['rev-parse', '--verify', '--quiet', 'HEAD^{tree}'], {
863
980
  cwd: state.root,
864
981
  allowFailure: true,
865
982
  });
866
- if (kind.code !== 0 || clean(kind.stdout) !== 'tree') return null;
867
- const scopePaths = Array.isArray(paths) && paths.length > 0 ? [...new Set(paths)] : null;
868
- const applied = await applyBaselineFilesUnlocked(state, tree, baselineFiles || []);
869
- const snapshot = {
870
- state,
871
- root,
872
- baselineTree: applied.tree,
873
- currentTree: tree,
874
- patch: '',
875
- files: [],
876
- patchTruncated: false,
877
- scopePaths,
878
- toolPaths: new Set(scopePaths || []),
879
- pendingBaselines: new Map(),
880
- baselineFiles: new Map(applied.files.map((entry) => [pathKey(entry.path), entry])),
881
- };
882
- const currentTree = await captureTreeUnlocked(state, snapshot.toolPaths);
883
- Object.assign(
884
- snapshot,
885
- withOmittedBaselines(
886
- snapshot,
887
- await diffTreesUnlocked(state, snapshot.baselineTree, currentTree, scopePaths, { omitPatch })
888
- )
889
- );
890
- return snapshot;
983
+ const headTree = head.code === 0 ? clean(head.stdout) : EMPTY_TREE;
984
+ const { files } = await diffTreesUnlocked(state, headTree, snapshot.currentTree, scope, { omitPatch: true });
985
+ const added = files.filter((file) => file.status === 'A').map((file) => file.path);
986
+ if (added.length === 0) return files;
987
+ const ignored = await runGit(['check-ignore', '-z', '--stdin'], {
988
+ cwd: state.root,
989
+ input: `${added.join('\0')}\0`,
990
+ allowFailure: true,
991
+ });
992
+ const skip = new Set(splitNulList(ignored.stdout).map(pathKey));
993
+ return files.filter((file) => file.status !== 'A' || !skip.has(pathKey(file.path)));
891
994
  });
892
995
  }
893
996
 
@@ -910,17 +1013,22 @@ function startRefresh(snapshot) {
910
1013
  const flight = { seq: (snapshot.refreshSeq || 0) + 1, promise: null };
911
1014
  snapshot.refreshSeq = flight.seq;
912
1015
  snapshot.refreshing = flight;
913
- flight.promise = withStateLock(snapshot.state, async () => {
914
- const mark = await captureMark(snapshot.state);
915
- await materializeToolBaselines(snapshot);
916
- const currentTree = await captureTreeUnlocked(snapshot.state, snapshot.toolPaths);
917
- if (currentTree !== snapshot.currentTree) {
918
- const review = await diffTreesUnlocked(snapshot.state, snapshot.baselineTree, currentTree, snapshot.scopePaths);
919
- Object.assign(snapshot, withOmittedBaselines(snapshot, review));
920
- }
921
- snapshot.captured = mark;
922
- return snapshot;
923
- }).finally(() => {
1016
+ const after = captureSequence(snapshot.state);
1017
+ flight.promise = withStateLock(
1018
+ snapshot.state,
1019
+ async () => {
1020
+ const mark = await captureMark(snapshot.state);
1021
+ await materializeToolBaselines(snapshot);
1022
+ const currentTree = await captureTreeUnlocked(snapshot.state, snapshot.toolPaths, { after });
1023
+ if (currentTree !== snapshot.currentTree) {
1024
+ const review = await diffTreesUnlocked(snapshot.state, snapshot.baselineTree, currentTree, snapshot.scopePaths);
1025
+ Object.assign(snapshot, withOmittedBaselines(snapshot, review));
1026
+ }
1027
+ snapshot.captured = mark;
1028
+ return snapshot;
1029
+ },
1030
+ 'refresh'
1031
+ ).finally(() => {
924
1032
  if (snapshot.refreshing === flight) snapshot.refreshing = null;
925
1033
  });
926
1034
  return flight.promise;
@@ -962,6 +1070,7 @@ function safeRelativePath(root, value) {
962
1070
  }
963
1071
 
964
1072
  async function restorePathFromTree(snapshot, rel) {
1073
+ snapshot.state.lastCapture = null;
965
1074
  const listed = await runGit(
966
1075
  shadowArgs(snapshot.state, ['ls-tree', '-z', '--name-only', snapshot.baselineTree, '--', rel]),
967
1076
  { cwd: snapshot.root }
@@ -14,8 +14,11 @@ import { parentPort, Worker } from 'node:worker_threads';
14
14
  * worker is detached before it is terminated, so no request is ever sent to
15
15
  * (or lost with) a worker that is shutting down.
16
16
  * `request.running()` reports whether a worker thread is currently attached.
17
+ *
18
+ * `onEvent` (optional): receives what the worker sends with postWorkerEvent
19
+ * (messages that answer no request).
17
20
  */
18
- export function createWorkerRequestClient(url, { idleExitMs = 0, ...workerOptions } = {}) {
21
+ export function createWorkerRequestClient(url, { idleExitMs = 0, onEvent = null, ...workerOptions } = {}) {
19
22
  let worker = null;
20
23
  let nextRequestId = 0;
21
24
  // Requests belong to the worker they were posted to: a replacement worker
@@ -50,7 +53,12 @@ export function createWorkerRequestClient(url, { idleExitMs = 0, ...workerOption
50
53
  owned.clear();
51
54
  for (const entry of entries) entry.reject(error);
52
55
  };
53
- created.on('message', ({ id, result, error }) => {
56
+ created.on('message', (message) => {
57
+ if (message && Object.hasOwn(message, 'event')) {
58
+ onEvent?.(message.event);
59
+ return;
60
+ }
61
+ const { id, result, error } = message;
54
62
  const entry = owned.get(id);
55
63
  if (!entry) return;
56
64
  owned.delete(id);
@@ -109,6 +117,11 @@ export function createWorkerRequestClient(url, { idleExitMs = 0, ...workerOption
109
117
  return request;
110
118
  }
111
119
 
120
+ /** Worker side: send `event` to the client's `onEvent`, outside any request. */
121
+ export function postWorkerEvent(event) {
122
+ parentPort.postMessage({ event });
123
+ }
124
+
112
125
  /** Worker side: answer every request with `handler(payload)` (sync or async). */
113
126
  export function serveWorkerRequests(handler) {
114
127
  parentPort.on('message', ({ id, payload }) => {
@@ -19,7 +19,6 @@ export const DEFAULT_WORKTREE_BRANCH_PREFIX = 'mixdog/';
19
19
 
20
20
  export const WORKSPACE_TRANSITION_STATES = Object.freeze(['idle', 'provisioning', 'switching', 'removing', 'recovery']);
21
21
  const WORKTREE_ENTRY_STATES = Object.freeze(['creating', 'ready', 'deleting', 'recovery']);
22
- const BRANCH_OCCUPANCY_KINDS = Object.freeze(['current', 'session', 'external', 'free']);
23
22
 
24
23
  /**
25
24
  * Stable error codes. Clients branch on `error.code`, never on message text.
@@ -302,29 +301,6 @@ export function normalizeWorktreeOwner(input) {
302
301
  * @typedef {{ branch: string, kind: BranchOccupancyKind, sessionId?: string, path?: string }} BranchOccupancy
303
302
  */
304
303
 
305
- /**
306
- * @param {unknown} input
307
- * @returns {BranchOccupancy}
308
- */
309
- export function normalizeBranchOccupancy(input) {
310
- const value = record(input, 'occupancy');
311
- const branch = text(value.branch, 'occupancy.branch');
312
- const kind = oneOf(value.kind, BRANCH_OCCUPANCY_KINDS, 'occupancy.kind');
313
- if (kind === 'free') {
314
- if (value.sessionId != null || value.path != null) {
315
- throw new TypeError('A free branch carries no sessionId or path.');
316
- }
317
- return { branch, kind };
318
- }
319
- if (kind !== 'session' && value.sessionId != null) {
320
- throw new TypeError('Only a session occupancy carries a sessionId.');
321
- }
322
- const occupancy = { branch, kind };
323
- if (kind === 'session') occupancy.sessionId = text(value.sessionId, 'occupancy.sessionId');
324
- if (value.path != null) occupancy.path = text(value.path, 'occupancy.path');
325
- return occupancy;
326
- }
327
-
328
304
  /**
329
305
  * The branch menu's rows (the desktop DesktopGitBranch shape) for the local
330
306
  * branch names the manager lists: `current` marks the branch checked out in
@@ -1,6 +1,6 @@
1
1
  // Temporary git repositories and data dirs for the worktree suites.
2
2
  import { execFileSync } from 'node:child_process';
3
- import { chmodSync, mkdtempSync, rmSync, writeFileSync, mkdirSync } from 'node:fs';
3
+ import { chmodSync, cpSync, existsSync, mkdtempSync, rmSync, writeFileSync, mkdirSync } from 'node:fs';
4
4
  import { tmpdir } from 'node:os';
5
5
  import { dirname, join } from 'node:path';
6
6
 
@@ -56,14 +56,29 @@ export function commitFile(cwd, rel, content, message = `edit ${rel}`) {
56
56
  return git(cwd, 'rev-parse', 'HEAD');
57
57
  }
58
58
 
59
- /** A repository on `main` with one commit. */
60
- export function makeRepo({ commit = true } = {}) {
61
- const dir = tempDir('mixdog-wt-repo-');
59
+ // Built once per process and copied per repository: the five or six git
60
+ // spawns behind one fresh repository cost ~0.8 s on win32, and the worktree
61
+ // suites make dozens. A non-bare repository holds no absolute paths, so a copy
62
+ // is the same repository at its new location.
63
+ const templates = new Map();
64
+
65
+ function repoTemplate(commit) {
66
+ const existing = templates.get(commit);
67
+ if (existing && existsSync(existing)) return existing;
68
+ const dir = tempDir('mixdog-wt-template-');
62
69
  git(dir, 'init', '-q', '-b', 'main');
63
70
  git(dir, 'config', 'user.name', 'Mixdog Test');
64
71
  git(dir, 'config', 'user.email', 'mixdog@example.com');
65
72
  git(dir, 'config', 'core.autocrlf', 'false');
66
73
  if (commit) commitFile(dir, 'README.md', 'hello\n', 'initial');
74
+ templates.set(commit, dir);
75
+ return dir;
76
+ }
77
+
78
+ /** A repository on `main` with one commit. */
79
+ export function makeRepo({ commit = true } = {}) {
80
+ const dir = tempDir('mixdog-wt-repo-');
81
+ cpSync(repoTemplate(commit), dir, { recursive: true });
67
82
  return dir;
68
83
  }
69
84
 
@@ -57,10 +57,16 @@ export function deliverAutomationCompletion(sessionId, text, meta = {}) {
57
57
  return true;
58
58
  }
59
59
 
60
+ // Ask options for a follow-up turn fed by agent results: the runtime reporting
61
+ // worker output, not the user — stored as a task notification and never a
62
+ // source of explicit skill selection.
63
+ export const AUTOMATION_RESULT_TURN_OPTS = Object.freeze({ promptSource: 'task-notification' });
64
+
60
65
  /**
61
66
  * Runs an automation conversation to its end: the first turn, then one more
62
67
  * turn per batch of agent results while agents it started are still out.
63
- * Resolves with the last turn's result.
68
+ * `nextTurn(text, askOpts)` receives AUTOMATION_RESULT_TURN_OPTS as the
69
+ * results' provenance. Resolves with the last turn's result.
64
70
  */
65
71
  export async function runAutomationTurns(sessionId, firstTurn, nextTurn, { waitMs = AGENT_RESULT_WAIT_MS } = {}) {
66
72
  const run = { pending: new Set(), ready: [], wake: null };
@@ -79,7 +85,7 @@ export async function runAutomationTurns(sessionId, firstTurn, nextTurn, { waitM
79
85
  run.wake = null;
80
86
  if (!arrived) break;
81
87
  }
82
- result = await nextTurn(run.ready.splice(0).join('\n\n'));
88
+ result = await nextTurn(run.ready.splice(0).join('\n\n'), { ...AUTOMATION_RESULT_TURN_OPTS });
83
89
  }
84
90
  return result;
85
91
  } finally {
@@ -60,6 +60,10 @@ import { createGoalFacadeApi } from '../goal-facade-api.mjs';
60
60
  import { createRuntimeReviewApi } from '../runtime-review-api.mjs';
61
61
  import { createWorkspaceTransitions, refreshWorkspacePrompt } from '../workspace/transitions.mjs';
62
62
  import { saveSession } from '../../runtime/agent/orchestrator/session/store.mjs';
63
+ import { modelToolSchemaAllowlist } from '../../runtime/agent/orchestrator/runtime-core/tool-profile.mjs';
64
+ import { sessionOptions } from '../session-create/session-options.mjs';
65
+ import { createConversationImportApi } from '../conversation-import/service.mjs';
66
+ import { importError } from '../conversation-import/discovery.mjs';
63
67
  import { bootProfile } from '../boot-profile.mjs';
64
68
  import { STANDALONE_DATA_DIR } from '../runtime-paths.mjs';
65
69
  import {
@@ -88,12 +92,22 @@ export function wireApis(boot) {
88
92
  });
89
93
  boot.providerAuthApi = providerAuthApiFor(boot);
90
94
  boot.mediaApi = createMediaApi();
95
+ boot.conversationImportApi = conversationImportApiFor(boot);
91
96
  boot.sessionTitles = createSessionTitleController({
92
97
  dataRoot: () => dataDirOf(boot.cfgMod),
93
98
  promoteGeneratedTitle: (sessionId, title, stage) => boot.mgr.updateSessionGeneratedTitle(sessionId, title, stage),
94
99
  });
95
100
  boot.lifecycleApi = lifecycleApiFor(boot);
96
101
  boot.resourceApi = resourceApiFor(boot);
102
+ // Plugin MCP servers are derived live; copies an earlier enable action wrote
103
+ // into mcpServers are adopted once (an injected headless config is left as is).
104
+ if (!boot.params.initialConfig || typeof boot.params.initialConfig !== 'object') {
105
+ try {
106
+ boot.resourceApi.migratePluginMcpCopies();
107
+ } catch (error) {
108
+ bootProfile('plugin-mcp:adopt-failed', { error: error?.message || String(error) });
109
+ }
110
+ }
97
111
  boot.disposeGlobalExtensionSubscription = () => {
98
112
  boot.disposeConfigSync?.();
99
113
  boot.resourceApi.disposeGlobalExtensionSubscription?.();
@@ -282,6 +296,45 @@ function settingsApiFor(boot) {
282
296
  });
283
297
  }
284
298
 
299
+ // Conversation import: imported sessions are built with the Lead options a new
300
+ // session of this runtime would get (current route, tools, workflow, memory),
301
+ // minus its reservation, profile ownership and desktop placement.
302
+ function conversationImportApiFor(boot) {
303
+ const { rt, mgr } = boot;
304
+ return createConversationImportApi({
305
+ // The async catalog (the daemon's SQLite catalog worker, else JSON),
306
+ // loaded on first use so boot does not pull in the cold reader.
307
+ listSessionSummaries: async () => {
308
+ const reader = await import('../../runtime/agent/orchestrator/session/store-summary-reader.mjs');
309
+ return reader.listStoredSessionSummariesAsync();
310
+ },
311
+ createSession: (options) => mgr.createSession(options),
312
+ prepareLeadOptions: async () => {
313
+ await boot.awaitKeychainPrewarm();
314
+ const route = rt.route?.provider && rt.route?.model ? rt.route : resolveRoute(rt.config, {});
315
+ if (!route?.provider || !route?.model) throw importError('noModelRoute');
316
+ await boot.ensureProvidersReady(rt.config.providers || {});
317
+ const coreMemoryContext = await boot.loadCoreMemoryContext();
318
+ const leadView = Object.assign(Object.create(rt), {
319
+ route,
320
+ reservedSessionId: null,
321
+ sessionProfile: null,
322
+ desktopSession: null,
323
+ });
324
+ return sessionOptions(
325
+ {
326
+ rt: leadView,
327
+ cfgMod: boot.cfgMod,
328
+ activeWorkflowContext: workflowHelpers.activeWorkflowContext,
329
+ schemaAllowedTools: modelToolSchemaAllowlist(rt.toolProfile),
330
+ featureDisallowedTools: boot.featureDisallowedTools,
331
+ },
332
+ coreMemoryContext
333
+ );
334
+ },
335
+ });
336
+ }
337
+
285
338
  function providerAuthApiFor(boot) {
286
339
  const { rt, cfgMod, reg, ensureProvidersReady } = boot;
287
340
  return createProviderAuthApi({
@@ -398,11 +451,19 @@ function resourceApiFor(boot) {
398
451
  STANDALONE_DATA_DIR,
399
452
  saveConfigAndAdopt: boot.saveConfigAndAdopt,
400
453
  connectConfiguredMcp: boot.connectConfiguredMcp,
454
+ cancelMcpLogin: boot.cancelMcpLogin,
455
+ completeMcpLogin: boot.completeMcpLogin,
456
+ clearMcpServerOAuth: (cfg) => boot.mcpClient.clearMcpServerOAuth(cfg),
457
+ getMcpServerOAuthState: (cfg) => boot.mcpClient.getMcpServerOAuthState(cfg),
458
+ probeMcpServerAuth: (cfg) => boot.mcpClient.probeMcpServerAuth(cfg),
401
459
  invalidatePreSessionToolSurface: boot.invalidatePreSessionToolSurface,
402
460
  refreshEmptySessionToolPolicy: boot.refreshEmptySessionToolPolicy,
403
461
  normalizeMcpServerInput: boot.normalizeMcpServerInput,
404
462
  mcpStatus: boot.mcpStatus,
405
463
  getMcpServerConfig: boot.getMcpServerConfig,
464
+ resolveMcpCatalog: boot.resolveMcpCatalog,
465
+ resolveConfiguredMcpCatalog: boot.resolveConfiguredMcpCatalog,
466
+ pluginMcpServer: boot.pluginMcpServer,
406
467
  skillsStatus: boot.skillsStatus,
407
468
  skillContent: boot.skillContent,
408
469
  addGlobalSkill: boot.addGlobalSkill,
@@ -87,7 +87,8 @@ function initRuntimeState(rt, params) {
87
87
  rt.activeTurnAbortControllers = new Set();
88
88
  rt.firstTurnCompleted = false;
89
89
  rt.codeGraphFirstTurnPrewarmDone = false;
90
- rt.startupProviderCatalogRefreshStarted = false;
90
+ // Last registry catalog-refresh promise this runtime attached handling to.
91
+ rt.startupProviderCatalogRefreshObserved = null;
91
92
  // True while the boot-time provider-catalog refresh is in flight: warming a
92
93
  // model cache it is about to invalidate only burns the load twice.
93
94
  rt.startupProviderCatalogRefreshPending = false;
@@ -106,7 +107,9 @@ function activeTurnControls(rt) {
106
107
  aborted = true;
107
108
  try {
108
109
  controller.abort(reason);
109
- } catch {}
110
+ } catch {
111
+ // Best-effort: one throwing abort listener must not stop the others.
112
+ }
110
113
  }
111
114
  return aborted;
112
115
  },
@@ -18,6 +18,7 @@ export function buildFacade(boot) {
18
18
  ...boot.providerAuthApi,
19
19
  ...boot.usageStatsApi,
20
20
  ...boot.mediaApi,
21
+ ...boot.conversationImportApi,
21
22
  ...boot.runtimeReviewApi,
22
23
  },
23
24
  goalApi: boot.goalFacadeApi,
@@ -7,11 +7,7 @@ import { createStandaloneHookBus } from '../services/hook-bus.mjs';
7
7
  import { updateCurrentCwdOverride, writeLastSessionCwd } from '../../runtime/shared/user-cwd.mjs';
8
8
  import { listRegisteredPlugins, pluginAdminStatus } from '../services/plugin-admin.mjs';
9
9
  import { clean } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
10
- import {
11
- countSkillFiles,
12
- pluginManifest,
13
- pluginMcpServerName,
14
- } from '../../runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs';
10
+ import { countSkillFiles, pluginManifest } from '../../runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs';
15
11
  import { bootProfile } from '../boot-profile.mjs';
16
12
  import { createMcpGlue } from '../mcp-glue.mjs';
17
13
  import { createSelfUpdateController } from '../self-update.mjs';
@@ -41,23 +37,39 @@ function wireMcp(boot) {
41
37
  mcpConnectGeneration: 0,
42
38
  mcpConnectInFlight: null,
43
39
  };
44
- const { mcpStatus, getMcpServerConfig, connectConfiguredMcp, awaitInitialMcpConnect, normalizeMcpServerInput } =
45
- createMcpGlue({
46
- mcpClient,
47
- getConfig: () => rt.config,
48
- getCurrentCwd: () => rt.currentCwd,
49
- getMcpScopeId: () => rt.mcpScopeId,
50
- getDesktopSession: () => rt.desktopSession,
51
- setDesktopSession: (v) => {
52
- rt.desktopSession = v;
53
- },
54
- state: mcpState,
55
- });
40
+ const {
41
+ mcpStatus,
42
+ resolveMcpCatalog,
43
+ resolveConfiguredMcpCatalog,
44
+ pluginMcpServer,
45
+ pluginMcpSummary,
46
+ getMcpServerConfig,
47
+ connectConfiguredMcp,
48
+ awaitInitialMcpConnect,
49
+ cancelMcpLogin,
50
+ completeMcpLogin,
51
+ normalizeMcpServerInput,
52
+ } = createMcpGlue({
53
+ mcpClient,
54
+ getConfig: () => rt.config,
55
+ getCurrentCwd: () => rt.currentCwd,
56
+ getMcpScopeId: () => rt.mcpScopeId,
57
+ // Plugin-declared servers are derived from the registry on every resolve.
58
+ getPluginDataDir: () => dataDirOf(boot.cfgMod),
59
+ listPlugins: () => listRegisteredPlugins({ dataDir: boot.cfgMod.getPluginData() }),
60
+ state: mcpState,
61
+ });
56
62
  Object.assign(boot, {
57
63
  mcpStatus,
64
+ resolveMcpCatalog,
65
+ resolveConfiguredMcpCatalog,
66
+ pluginMcpServer,
67
+ pluginMcpSummary,
58
68
  getMcpServerConfig,
59
69
  connectConfiguredMcp,
60
70
  awaitInitialMcpConnect,
71
+ cancelMcpLogin,
72
+ completeMcpLogin,
61
73
  normalizeMcpServerInput,
62
74
  });
63
75
  }
@@ -250,7 +262,12 @@ function wireCwdPlugins(boot) {
250
262
  listRegisteredPlugins,
251
263
  pluginAdminStatus,
252
264
  pluginManifest,
253
- pluginMcpServerName,
265
+ pluginMcpSummary: boot.pluginMcpSummary,
266
+ // Plugin MCP servers without a declared cwd follow this runtime's cwd:
267
+ // reconnect just those (and refresh advertised roots) in place.
268
+ onCwdChanged: () => {
269
+ void boot.connectConfiguredMcp({ reconcile: true }).catch(() => {});
270
+ },
254
271
  countSkillFiles,
255
272
  writeLastSessionCwd,
256
273
  updateCurrentCwdOverride,