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
@@ -0,0 +1,369 @@
1
+ /**
2
+ * "Import conversations from other apps": the runtime API (list / register /
3
+ * start / status / cancel) and the background import jobs behind it.
4
+ *
5
+ * Jobs are process-wide, so a job keeps running and stays observable after the
6
+ * dialog closes or the user switches conversations (each session runtime of
7
+ * the daemon reads the same registry). Every file passes one serialized
8
+ * admission lane shared by all jobs, where the "already imported" check is
9
+ * repeated right before the session is created, against the session catalog
10
+ * read once per batch and every commit of this process since.
11
+ * An import key only counts as imported while its session still exists in
12
+ * storage, so a deleted import can be imported again.
13
+ * Cancel stops admitting further files; committed imports stay.
14
+ */
15
+ import { randomUUID } from 'node:crypto';
16
+ import { access, stat } from 'node:fs/promises';
17
+ import {
18
+ buildImportOrigin,
19
+ createImportedSession,
20
+ importOriginKey,
21
+ } from '../../runtime/agent/orchestrator/session/conversation-import/imported-session.mjs';
22
+ import { IMPORT_WARNING_CWD_MISSING } from '../../runtime/agent/orchestrator/session/conversation-import/turns.mjs';
23
+ import { sessionPath } from '../../runtime/agent/orchestrator/session/store/paths-heartbeat.mjs';
24
+ import {
25
+ cachedMeta,
26
+ convertCandidateFile,
27
+ getCandidate,
28
+ importError,
29
+ importedKeyMap,
30
+ listConversations,
31
+ recordConversion,
32
+ registerFile,
33
+ resolveSourceCwd,
34
+ resolveViewSelection,
35
+ } from './discovery.mjs';
36
+
37
+ const MAX_FINISHED_JOBS = 20;
38
+ const MAX_REQUEST_ID_LENGTH = 200;
39
+
40
+ const jobs = new Map(); // jobId → job (insertion order = start order)
41
+ const jobsByRequest = new Map(); // requestId → jobId
42
+ const pendingStarts = new Map(); // requestId → Promise<job> while a selection resolves
43
+ // import key → session id, imports of this process (summaries can lag a save).
44
+ const committedKeys = new Map();
45
+ let admissionTail = Promise.resolve();
46
+
47
+ /** Test seam: forget every job and committed key. */
48
+ export function resetConversationImportJobs() {
49
+ jobs.clear();
50
+ jobsByRequest.clear();
51
+ pendingStarts.clear();
52
+ committedKeys.clear();
53
+ admissionTail = Promise.resolve();
54
+ }
55
+
56
+ const EXISTS_CONCURRENCY = 16;
57
+
58
+ const sessionFileExists = (id) =>
59
+ access(sessionPath(id)).then(
60
+ () => true,
61
+ () => false
62
+ );
63
+
64
+ /**
65
+ * `get(key)` → the id of an existing session imported under `key`, else null.
66
+ * Candidates come from this process's commits, then summaries; every candidate
67
+ * id's existence is checked (asynchronously) once, up front. Committed ids
68
+ * whose session no longer exists are dropped from the commit map.
69
+ */
70
+ async function importedLookup(summaries, sessionExists) {
71
+ const fromSummaries = importedKeyMap(summaries);
72
+ const committedAtStart = new Map(committedKeys);
73
+ const ids = [...new Set([...committedAtStart.values(), ...[...fromSummaries.values()].flat()])];
74
+ const existing = new Set();
75
+ let next = 0;
76
+ const worker = async () => {
77
+ while (next < ids.length) {
78
+ const id = ids[next++];
79
+ if (await sessionExists(id)) existing.add(id);
80
+ }
81
+ };
82
+ await Promise.all(Array.from({ length: Math.min(EXISTS_CONCURRENCY, ids.length) }, worker));
83
+ for (const [key, id] of committedAtStart) {
84
+ if (!existing.has(id) && committedKeys.get(key) === id) committedKeys.delete(key);
85
+ }
86
+ const get = (key) => {
87
+ const committed = committedAtStart.get(key);
88
+ if (committed && existing.has(committed)) return committed;
89
+ return (fromSummaries.get(key) || []).find((id) => existing.has(id)) || null;
90
+ };
91
+ return { get, has: (key) => get(key) !== null };
92
+ }
93
+
94
+ class FatalImportError extends Error {
95
+ constructor(code) {
96
+ super(code);
97
+ this.code = code;
98
+ }
99
+ }
100
+
101
+ function admit(task) {
102
+ const run = admissionTail.then(task, task);
103
+ admissionTail = run.then(
104
+ () => {},
105
+ () => {}
106
+ );
107
+ return run;
108
+ }
109
+
110
+ function snapshot(job) {
111
+ return {
112
+ jobId: job.jobId,
113
+ seq: job.seq,
114
+ state: job.state,
115
+ total: job.total,
116
+ processed: job.imported + job.skippedAlreadyImported + job.failed,
117
+ imported: job.imported,
118
+ skippedAlreadyImported: job.skippedAlreadyImported,
119
+ failed: job.failed,
120
+ byProject: [...job.byProject.entries()].map(([projectPath, counts]) => ({ projectPath, ...counts })),
121
+ results: job.results.map((result) => ({ ...result, warnings: [...result.warnings] })),
122
+ current: job.current ? { ...job.current } : null,
123
+ ...(job.errorCode ? { errorCode: job.errorCode } : {}),
124
+ };
125
+ }
126
+
127
+ function touch(job, patch = {}) {
128
+ Object.assign(job, patch);
129
+ job.seq += 1;
130
+ }
131
+
132
+ const COUNTER = { imported: 'imported', alreadyImported: 'skippedAlreadyImported', failed: 'failed' };
133
+
134
+ function record(job, { projectPath = null, ...result }) {
135
+ const counter = COUNTER[result.status];
136
+ job[counter] += 1;
137
+ const counts = job.byProject.get(projectPath) || { imported: 0, skippedAlreadyImported: 0, failed: 0 };
138
+ counts[counter] += 1;
139
+ job.byProject.set(projectPath, counts);
140
+ job.results.push(result);
141
+ touch(job);
142
+ }
143
+
144
+ function pruneFinishedJobs() {
145
+ const finished = [...jobs.values()].filter((job) => job.state !== 'running' && job.state !== 'stopping');
146
+ for (const job of finished.slice(0, Math.max(0, finished.length - MAX_FINISHED_JOBS))) {
147
+ jobs.delete(job.jobId);
148
+ if (jobsByRequest.get(job.requestId) === job.jobId) jobsByRequest.delete(job.requestId);
149
+ }
150
+ }
151
+
152
+ const failed = (candidateId, errorCode, projectPath = null, warnings = []) => ({
153
+ candidateId,
154
+ status: 'failed',
155
+ errorCode,
156
+ warnings,
157
+ projectPath,
158
+ });
159
+
160
+ async function importOne(candidateId, context) {
161
+ const candidate = getCandidate(candidateId);
162
+ if (!candidate) return failed(candidateId, 'unknownCandidate');
163
+ // The chat keeps the title the user picked it by: a long file's listing
164
+ // title comes from its prefix and can differ from the converted branch.
165
+ const listedTitle = cachedMeta(candidate)?.title ?? null;
166
+ let conversion;
167
+ try {
168
+ const info = await stat(candidate.path);
169
+ if (!info.isFile()) return failed(candidateId, 'unreadable');
170
+ candidate.size = info.size;
171
+ candidate.mtimeMs = info.mtimeMs;
172
+ conversion = await convertCandidateFile(candidate, context.limits);
173
+ } catch {
174
+ return failed(candidateId, 'unreadable');
175
+ }
176
+ if (listedTitle) conversion = { ...conversion, title: listedTitle };
177
+ recordConversion(candidate, conversion);
178
+ const cwdInfo = await resolveSourceCwd(conversion.cwd);
179
+ const projectPath = cwdInfo.exists ? cwdInfo.path : null;
180
+ if (conversion.errorCode) return failed(candidateId, conversion.errorCode, projectPath, conversion.warnings);
181
+ const warnings = [...conversion.warnings, ...(projectPath ? [] : [IMPORT_WARNING_CWD_MISSING])];
182
+ const key = importOriginKey(candidate.source, conversion.foreignSessionId, candidate.path);
183
+ // The batch's catalog snapshot, then commits made since (by any job; the
184
+ // admission lane serializes them).
185
+ let existing = context.imported.get(key);
186
+ const committed = committedKeys.get(key);
187
+ if (!existing && committed && (await context.sessionExists(committed))) existing = committed;
188
+ if (existing) return { candidateId, status: 'alreadyImported', sessionId: existing, warnings, projectPath };
189
+ if (!context.leadOptions) {
190
+ try {
191
+ context.leadOptions = await context.prepareLeadOptions();
192
+ } catch (error) {
193
+ throw new FatalImportError(error?.code || 'noModelRoute');
194
+ }
195
+ }
196
+ let session;
197
+ try {
198
+ session = await context.createImportedSession({
199
+ leadOptions: context.leadOptions,
200
+ conversation: conversion,
201
+ origin: buildImportOrigin({
202
+ source: candidate.source,
203
+ sourcePath: candidate.path,
204
+ foreignSessionId: conversion.foreignSessionId,
205
+ sourceCwd: conversion.cwd,
206
+ importedAt: Date.now(),
207
+ }),
208
+ cwd: projectPath,
209
+ desktopSession: projectPath
210
+ ? { classification: 'project', projectPath }
211
+ : { classification: 'task', projectPath: null },
212
+ });
213
+ } catch (error) {
214
+ return failed(candidateId, typeof error?.code === 'string' ? error.code : 'importFailed', projectPath, warnings);
215
+ }
216
+ committedKeys.set(key, session.id);
217
+ return { candidateId, status: 'imported', sessionId: session.id, warnings, projectPath };
218
+ }
219
+
220
+ async function runJob(job, candidateIds, context) {
221
+ try {
222
+ // One catalog read per batch; later commits are seen through committedKeys.
223
+ context.imported = await context.importedLookup();
224
+ for (const candidateId of candidateIds) {
225
+ if (job.cancelRequested) break;
226
+ let result;
227
+ try {
228
+ result = await admit(async () => {
229
+ // A cancel that arrived while queued behind other jobs wins here.
230
+ if (job.cancelRequested) return null;
231
+ const candidate = getCandidate(candidateId);
232
+ touch(job, { current: { candidateId, title: (candidate && cachedMeta(candidate)?.title) ?? null } });
233
+ try {
234
+ return await importOne(candidateId, context);
235
+ } finally {
236
+ job.current = null;
237
+ }
238
+ });
239
+ } catch (error) {
240
+ if (error instanceof FatalImportError) throw error;
241
+ result = failed(candidateId, 'importFailed');
242
+ }
243
+ if (result === null) break;
244
+ record(job, result);
245
+ }
246
+ touch(job, { state: job.cancelRequested ? 'stopped' : 'completed' });
247
+ } catch (error) {
248
+ touch(job, { state: 'failed', current: null, errorCode: error?.code || 'importFailed' });
249
+ }
250
+ pruneFinishedJobs();
251
+ }
252
+
253
+ /** Candidate ids of a selection; a `viewId` resolves the view's complete membership. */
254
+ async function selectionIds(selection, imported) {
255
+ if (!selection || typeof selection !== 'object') throw new TypeError('selection is required.');
256
+ if (Array.isArray(selection.ids)) {
257
+ if (!selection.ids.every((id) => typeof id === 'string' && id)) throw new TypeError('selection.ids must be ids.');
258
+ return [...new Set(selection.ids)];
259
+ }
260
+ if (typeof selection.viewId === 'string') {
261
+ if (selection.excludeIds !== undefined && !Array.isArray(selection.excludeIds)) {
262
+ throw new TypeError('selection.excludeIds must be ids.');
263
+ }
264
+ return resolveViewSelection(selection.viewId, selection.excludeIds, await imported());
265
+ }
266
+ throw new TypeError('selection must carry ids or a viewId.');
267
+ }
268
+
269
+ function requireParams(params) {
270
+ if (!params || typeof params !== 'object' || Array.isArray(params)) throw new TypeError('expected one object.');
271
+ return params;
272
+ }
273
+
274
+ /**
275
+ * Runtime API slice. deps:
276
+ * listSessionSummaries() async session summary rows (carrying `importKey`), read once per listing/batch
277
+ * prepareLeadOptions() async createSession options of a Lead session on the current route
278
+ * createSession(opts) the session manager's createSession
279
+ * sessionExists(id) async: whether a session is still in storage (default: its file exists)
280
+ * ingestSession(session) content-search ingestion of an imported session (default: ingestSessionTurn)
281
+ * limits convertCandidateFile limits (maxFileBytes, maxLineBytes, maxTranscriptChars, …)
282
+ */
283
+ export function createConversationImportApi({
284
+ listSessionSummaries,
285
+ prepareLeadOptions,
286
+ createSession,
287
+ env,
288
+ sessionExists = sessionFileExists,
289
+ ingestSession,
290
+ limits,
291
+ }) {
292
+ const summaries = async () => (await listSessionSummaries?.()) || [];
293
+ const imported = async () => importedLookup(await summaries(), sessionExists);
294
+ return {
295
+ async listImportableConversations(params) {
296
+ return listConversations(requireParams(params), { imported: await imported(), env });
297
+ },
298
+ async registerConversationImportFile(params) {
299
+ return registerFile(requireParams(params), { imported: await imported(), limits });
300
+ },
301
+ getConversationImportJobs(params = {}) {
302
+ const { jobId } = requireParams(params ?? {});
303
+ const list = [...jobs.values()].filter((job) => jobId === undefined || jobId === null || job.jobId === jobId);
304
+ return { jobs: list.map(snapshot) };
305
+ },
306
+ async startConversationImport(params) {
307
+ const { requestId, selection } = requireParams(params);
308
+ if (typeof requestId !== 'string' || !requestId.trim() || requestId.length > MAX_REQUEST_ID_LENGTH) {
309
+ throw new TypeError('requestId is required.');
310
+ }
311
+ const existing = jobs.get(jobsByRequest.get(requestId));
312
+ if (existing) return snapshot(existing);
313
+ let pending = pendingStarts.get(requestId);
314
+ if (!pending) {
315
+ pending = (async () => {
316
+ const candidateIds = await selectionIds(selection, imported);
317
+ const job = {
318
+ jobId: randomUUID(),
319
+ requestId,
320
+ seq: 0,
321
+ state: 'running',
322
+ total: candidateIds.length,
323
+ imported: 0,
324
+ skippedAlreadyImported: 0,
325
+ failed: 0,
326
+ byProject: new Map(),
327
+ results: [],
328
+ current: null,
329
+ errorCode: null,
330
+ cancelRequested: false,
331
+ };
332
+ jobs.set(job.jobId, job);
333
+ jobsByRequest.set(requestId, job.jobId);
334
+ void runJob(job, candidateIds, {
335
+ importedLookup: imported,
336
+ imported: null,
337
+ sessionExists,
338
+ limits,
339
+ prepareLeadOptions,
340
+ leadOptions: null,
341
+ createImportedSession: (args) =>
342
+ createImportedSession({
343
+ ...args,
344
+ ...(createSession ? { createSessionFn: createSession } : {}),
345
+ ...(ingestSession ? { ingestFn: ingestSession } : {}),
346
+ }),
347
+ });
348
+ return job;
349
+ })();
350
+ pendingStarts.set(requestId, pending);
351
+ pending.then(
352
+ () => pendingStarts.delete(requestId),
353
+ () => pendingStarts.delete(requestId)
354
+ );
355
+ }
356
+ return snapshot(await pending);
357
+ },
358
+ cancelConversationImport(params) {
359
+ const { jobId } = requireParams(params);
360
+ const job = typeof jobId === 'string' ? jobs.get(jobId) : null;
361
+ if (!job) throw importError('jobNotFound');
362
+ if (job.state === 'running') {
363
+ job.cancelRequested = true;
364
+ touch(job, { state: 'stopping' });
365
+ }
366
+ return snapshot(job);
367
+ },
368
+ };
369
+ }
@@ -20,6 +20,7 @@ export function createCwdApply({
20
20
  writeLastSessionCwd,
21
21
  updateCurrentCwdOverride,
22
22
  persistSession,
23
+ onCwdChanged,
23
24
  clean,
24
25
  resolve,
25
26
  statSync,
@@ -53,10 +54,11 @@ export function createCwdApply({
53
54
  }
54
55
  }
55
56
 
56
- // cwd changes NEVER recreate the session and never reload MCP or Skills
57
- // (extension settings are global): a mid-conversation cwd switch must
58
- // preserve the full message history. The live execution cwd is retargeted in
59
- // place; the BP3 session snapshot remains the session-start environment.
57
+ // cwd changes NEVER recreate the session and never reload Skills (extension
58
+ // settings are global): a mid-conversation cwd switch must preserve the full
59
+ // message history. The live execution cwd is retargeted in place; the BP3
60
+ // session snapshot remains the session-start environment. `onCwdChanged`
61
+ // lets MCP reconnect only the plugin servers that run in the session cwd.
60
62
  function applyResolvedCwd(nextCwd, { persistProjectSelection: persist = false } = {}) {
61
63
  const resolved = resolve(nextCwd);
62
64
  const stat = statSync(resolved);
@@ -84,7 +86,10 @@ export function createCwdApply({
84
86
  if (changed) {
85
87
  try {
86
88
  void hooks.dispatch('CwdChanged', hookCommonPayload({ cwd: currentCwd }));
87
- } catch {}
89
+ } catch {
90
+ // Hook dispatch is fire-and-forget; a synchronous throw must not block the cwd switch.
91
+ }
92
+ onCwdChanged?.(currentCwd);
88
93
  }
89
94
  return currentCwd;
90
95
  }
@@ -1,16 +1,15 @@
1
1
  // cwd-plugins/plugin-status.mjs — the registered-plugin status list with its
2
- // per-root caches (manifest keyed on mtime, MCP discovery and skill-file count
3
- // on a short TTL).
2
+ // manifest cache (keyed on mtime); skill count and MCP discovery are live.
4
3
  import { readFileSync } from 'node:fs';
5
4
  import { discoverPluginMcp } from '../../runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs';
5
+ import { PLUGIN_MANIFEST_FILES, pluginSkillsRoots } from '../../runtime/shared/plugin-manifest.mjs';
6
6
  import { pluginMetadata } from '../../runtime/shared/plugin-metadata.mjs';
7
7
 
8
8
  export function createPluginStatus({
9
- getConfig,
10
9
  listRegisteredPlugins,
11
10
  pluginAdminStatus,
12
11
  pluginManifest,
13
- pluginMcpServerName,
12
+ pluginMcpSummary = () => new Map(),
14
13
  countSkillFiles,
15
14
  clean,
16
15
  resolve,
@@ -18,16 +17,13 @@ export function createPluginStatus({
18
17
  existsSync,
19
18
  cfgMod,
20
19
  }) {
21
- // Per-plugin-root caches for pluginsStatus(): manifest + MCP discovery keyed
22
- // by root path + manifest mtime (invalidated on manifest edit); recursive
23
- // skill-file count keyed by root with a ~5s TTL fallback (the walk has no
24
- // single mtime to key on).
20
+ // Per-plugin-root manifest cache keyed by root path + manifest mtimes
21
+ // (invalidated on manifest edit). Skill count and MCP discovery are
22
+ // recomputed on every call so file add/remove shows up immediately.
25
23
  const pluginRootCache = new Map();
26
- const skillCountCache = new Map();
27
- const mcpDiscoveryCache = new Map();
28
24
  function manifestMtimeKey(root) {
29
25
  let key = '';
30
- for (const rel of ['.codex-plugin/plugin.json', 'plugin.json']) {
26
+ for (const rel of PLUGIN_MANIFEST_FILES) {
31
27
  try {
32
28
  key += `${statSync(resolve(root, rel)).mtimeMs}:`;
33
29
  } catch {
@@ -38,7 +34,7 @@ export function createPluginStatus({
38
34
  }
39
35
  // A manifest file that exists but is not a JSON object is breakage.
40
36
  function manifestInvalid(root) {
41
- for (const rel of ['.codex-plugin/plugin.json', 'plugin.json']) {
37
+ for (const rel of PLUGIN_MANIFEST_FILES) {
42
38
  const file = resolve(root, rel);
43
39
  if (!existsSync(file)) continue;
44
40
  try {
@@ -58,33 +54,20 @@ export function createPluginStatus({
58
54
  pluginRootCache.set(root, entry);
59
55
  return entry;
60
56
  }
61
- // MCP discovery probes candidate script files (.mcp.json, scripts/run-mcp.mjs,
62
- // ...) whose add/remove is NOT reflected in the manifest mtime, so key it on a
63
- // short TTL instead (~5s, like the skill-file count).
64
- function cachedMcpDiscovery(root) {
65
- const now = Date.now();
66
- const hit = mcpDiscoveryCache.get(root);
67
- if (hit && now - hit.at < 5000) return hit.mcp;
68
- const mcp = discoverPluginMcp(root);
69
- mcpDiscoveryCache.set(root, { at: now, mcp });
70
- return mcp;
71
- }
72
- function cachedSkillCount(root) {
73
- const now = Date.now();
74
- const hit = skillCountCache.get(root);
75
- if (hit && now - hit.at < 5000) return hit.count;
76
- const count = countSkillFiles(root);
77
- skillCountCache.set(root, { at: now, count });
78
- return count;
79
- }
80
57
 
81
- function registeredPlugin(entry, configuredMcp) {
58
+ function registeredPlugin(entry, mcpByPlugin) {
82
59
  const root = clean(entry.root);
83
60
  if (!root) return null;
84
- if (!existsSync(root)) return { missing: true, id: clean(entry.id) || clean(entry.name) || root, name: clean(entry.name) || clean(entry.id) || root.split(/[\\/]/).pop() || root };
61
+ if (!existsSync(root))
62
+ return {
63
+ missing: true,
64
+ id: clean(entry.id) || clean(entry.name) || root,
65
+ name: clean(entry.name) || clean(entry.id) || root.split(/[\\/]/).pop() || root,
66
+ };
85
67
  const { manifest, invalid } = cachedPluginData(root);
86
68
  const name = clean(manifest.name) || clean(manifest.id) || clean(entry.name) || root.split(/[\\/]/).pop() || root;
87
- const { mcpScript, mcpInline } = cachedMcpDiscovery(root);
69
+ const { mcpScript, mcpInline } = discoverPluginMcp(root);
70
+ const skillCount = countSkillFiles(root);
88
71
  const plugin = {
89
72
  id: clean(entry.id) || name,
90
73
  name,
@@ -101,26 +84,48 @@ export function createPluginStatus({
101
84
  root,
102
85
  installedAt: entry.installedAt || null,
103
86
  updatedAt: entry.updatedAt || null,
104
- skillCount: cachedSkillCount(root),
87
+ skillCount,
105
88
  mcpScript,
106
89
  mcpInline,
107
90
  };
108
91
  if (invalid) plugin.invalid = true;
109
- plugin.mcpServerName = pluginMcpServerName(plugin);
110
- plugin.mcpEnabled =
111
- Object.hasOwn(configuredMcp, plugin.mcpServerName) ||
112
- Object.keys(configuredMcp).some((k) => k.startsWith(`${plugin.mcpServerName}--`));
92
+ if (plugin.enabled && skillCount === 0 && !mcpScript && !mcpInline) {
93
+ plugin.searchedSkillRoots = pluginSkillsRoots(root);
94
+ plugin.warning = `No skills or MCP server found. Searched skill roots: ${plugin.searchedSkillRoots.join(', ')}`;
95
+ }
96
+ // Servers the plugin declares, live: name, declared key, intent/effective
97
+ // state, connection, and declaration problems.
98
+ const mcp = mcpByPlugin.get(plugin.id) || { format: 'none', errors: [], servers: [] };
99
+ plugin.mcpFormat = mcp.format;
100
+ plugin.mcpErrors = mcp.errors;
101
+ plugin.mcpServers = mcp.servers.filter(Boolean).map((row) => ({
102
+ name: row.name,
103
+ key: row.owner?.key || '',
104
+ enabled: row.enabled !== false,
105
+ serverEnabled: row.serverEnabled !== false,
106
+ connected: row.connected === true,
107
+ status: row.status,
108
+ error: row.error || null,
109
+ warnings: row.warnings || [],
110
+ cwdDependent: row.owner?.cwdDependent === true,
111
+ }));
112
+ plugin.mcpServerName = plugin.mcpServers[0]?.name || '';
113
+ plugin.mcpEnabled = plugin.mcpServers.some((server) => server.serverEnabled);
113
114
  return plugin;
114
115
  }
115
116
 
116
117
  function pluginsStatus() {
117
- const config = getConfig();
118
118
  const dataDir = cfgMod.getPluginData?.();
119
- const configuredMcp = config?.mcpServers && typeof config.mcpServers === 'object' ? config.mcpServers : {};
119
+ let mcpByPlugin = new Map();
120
+ try {
121
+ mcpByPlugin = pluginMcpSummary();
122
+ } catch {
123
+ /* status still lists plugins without their MCP rows */
124
+ }
120
125
  const plugins = [];
121
126
  const missing = [];
122
127
  for (const entry of listRegisteredPlugins({ dataDir })) {
123
- const plugin = registeredPlugin(entry, configuredMcp);
128
+ const plugin = registeredPlugin(entry, mcpByPlugin);
124
129
  if (plugin?.missing) missing.push({ id: plugin.id, name: plugin.name, enabled: entry.enabled !== false });
125
130
  else if (plugin) plugins.push(plugin);
126
131
  }
@@ -93,6 +93,8 @@ function leadSessionRow(s, heartbeatMtimes) {
93
93
  // sessions under the sidebar Automations section instead of Recent.
94
94
  sourceType: sourceType || null,
95
95
  sourceName: clean(s.sourceName || '') || null,
96
+ createdAt: Number(s.createdAt) || 0,
97
+ inheritedFromSessionId: clean(s.inheritedFromSessionId || '') || null,
96
98
  };
97
99
  }
98
100
 
@@ -178,7 +178,7 @@ export function createTeardown(deps, { ingestSessionIntoMemory, closeSurfaceSess
178
178
  if (scope.detach) await settleDetached(withTeardownDeadline, stops);
179
179
  else await settleAll(withTeardownDeadline, stops);
180
180
  // The session went away (idle eviction included): free its save baselines.
181
- if (scope.closingSessionId) forgetSessionSaveBaseline(scope.closingSessionId);
181
+ if (scope.closingSessionId) forgetSessionSaveBaseline(scope.closingSessionId, { releaseWriteControl: true });
182
182
  // Self-update stages in the background and swaps on the next clean launch
183
183
  // (see staged-update.mjs) — nothing installs at shutdown. On a real
184
184
  // process exit we just drop this session's live-refcount pid file so a