mixdog 1.4.0 → 1.4.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 (493) hide show
  1. package/package.json +3 -2
  2. package/scripts/build-shipped-history.mjs +115 -33
  3. package/scripts/check-voice-runtime-inputs.mjs +25 -0
  4. package/scripts/gate-local.mjs +183 -63
  5. package/scripts/latest-guard.mjs +21 -9
  6. package/scripts/lib/gate-scheduler.mjs +138 -32
  7. package/scripts/lib/gate-timing-reporter.mjs +16 -0
  8. package/scripts/lib/isolated-test-env.mjs +10 -5
  9. package/scripts/lib/run-node-tests.mjs +91 -66
  10. package/scripts/lib/scoped-temp.d.mts +16 -0
  11. package/scripts/lib/scoped-temp.mjs +124 -0
  12. package/scripts/lib/stale-temp.mjs +44 -0
  13. package/scripts/lib/test-failure-records.mjs +14 -1
  14. package/scripts/lib/test-failure-summary.mjs +16 -4
  15. package/scripts/lib/test-spawn-ledger.mjs +12 -3
  16. package/scripts/post-release-check.mjs +1 -6
  17. package/scripts/pptx-kit-index.mjs +17 -5
  18. package/scripts/suite-health.mjs +115 -24
  19. package/scripts/test-data-dir.mjs +10 -7
  20. package/scripts/test.mjs +9 -14
  21. package/src/agents/advisor/AGENT.md +4 -4
  22. package/src/agents/advisor/agent.json +1 -1
  23. package/src/agents/explore/AGENT.md +22 -0
  24. package/src/agents/explore/agent.json +6 -0
  25. package/src/app.mjs +4 -0
  26. package/src/cli.mjs +7 -0
  27. package/src/defaults/agents.json +7 -0
  28. package/src/defaults/shipped-history.json +23 -2
  29. package/src/defaults/skills/browser-use/SKILL.md +10 -5
  30. package/src/defaults/skills/code-tidy/SKILL.md +71 -102
  31. package/src/defaults/skills/code-tidy/references/agent-cleanup.md +66 -114
  32. package/src/defaults/skills/code-tidy/references/cleanup-report.md +19 -35
  33. package/src/defaults/skills/code-tidy/references/dead-code.md +3 -9
  34. package/src/defaults/skills/code-tidy/references/test-suite-slop.md +16 -27
  35. package/src/defaults/skills/computer-use/SKILL.md +16 -4
  36. package/src/defaults/skills/goal-management/SKILL.md +1 -1
  37. package/src/defaults/skills/setup/references/actions.md +12 -6
  38. package/src/defaults/skills/setup/references/surfaces.md +2 -2
  39. package/src/defaults/skills/setup-guide/SKILL.md +5 -2
  40. package/src/defaults/skills/setup-guide/model-recommendations.json +13 -0
  41. package/src/defaults/skills/skill-creator/SKILL.md +85 -190
  42. package/src/defaults/skills/skill-creator/references/design.md +70 -162
  43. package/src/defaults/skills/skill-creator/references/evaluation.md +33 -92
  44. package/src/defaults/skills/test-suite/SKILL.md +194 -0
  45. package/src/defaults/skills/test-suite/references/case-management.md +51 -0
  46. package/src/defaults/skills/test-suite/references/flaky-policy.md +39 -0
  47. package/src/defaults/skills/test-suite/references/reliability.md +49 -0
  48. package/src/defaults/skills/test-suite/references/suite-design.md +78 -0
  49. package/src/defaults/skills/test-suite/scripts/inventory.mjs +335 -0
  50. package/src/defaults/skills/visualize/SKILL.md +6 -2
  51. package/src/lib/rules-builder.cjs +35 -42
  52. package/src/output-styles/common.md +2 -2
  53. package/src/output-styles/simple.md +1 -1
  54. package/src/rules/agent/AGENT.md +5 -4
  55. package/src/rules/lead/LEAD.md +12 -7
  56. package/src/rules/shared/10-tool-workflow.md +3 -1
  57. package/src/rules/shared/20-research.md +3 -6
  58. package/src/rules/shared/40-editing.md +1 -0
  59. package/src/rules/shared/45-destructive.md +0 -2
  60. package/src/rules/shared/50-execution.md +2 -0
  61. package/src/rules/shared/60-verification.md +10 -9
  62. package/src/rules/shared/70-delivery.md +8 -9
  63. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/abort-link.mjs +5 -1
  64. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +10 -1
  65. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +156 -24
  66. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +13 -0
  67. package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +4 -1
  68. package/src/runtime/agent/orchestrator/cache-break-trace.mjs +2 -0
  69. package/src/runtime/agent/orchestrator/config.mjs +12 -7
  70. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +8 -8
  71. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +5 -26
  72. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +3 -1
  73. package/src/runtime/agent/orchestrator/internal-agents.mjs +3 -14
  74. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +122 -12
  75. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +5 -3
  76. package/src/runtime/agent/orchestrator/mcp/client-config.mjs +13 -2
  77. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +152 -58
  78. package/src/runtime/agent/orchestrator/mcp/client.mjs +51 -18
  79. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +88 -54
  80. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +42 -26
  81. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +54 -8
  82. package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +1 -1
  83. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +37 -31
  84. package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +8 -2
  85. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +6 -2
  86. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -2
  87. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  88. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +31 -21
  89. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +3 -1
  90. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +14 -3
  91. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +6 -0
  92. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -2
  93. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +3 -1
  94. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +2 -2
  95. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
  96. package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +1 -10
  97. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +1 -1
  98. package/src/runtime/agent/orchestrator/providers/cursor.mjs +3 -0
  99. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +40 -0
  100. package/src/runtime/agent/orchestrator/providers/custom-provider-keys.mjs +34 -0
  101. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +3 -0
  102. package/src/runtime/agent/orchestrator/providers/devin-auth.mjs +14 -7
  103. package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +3 -2
  104. package/src/runtime/agent/orchestrator/providers/devin.mjs +3 -0
  105. package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +2 -2
  106. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +4 -1
  107. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +248 -41
  108. package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -11
  109. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +45 -6
  110. package/src/runtime/agent/orchestrator/providers/lib/first-output-window.mjs +58 -0
  111. package/src/runtime/agent/orchestrator/providers/lib/listen-on.mjs +14 -0
  112. package/src/runtime/agent/orchestrator/providers/lib/oauth-page.mjs +0 -18
  113. package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +138 -6
  114. package/src/runtime/agent/orchestrator/providers/mimo-oauth-login.mjs +1 -10
  115. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +1 -1
  116. package/src/runtime/agent/orchestrator/providers/model-cache.mjs +100 -33
  117. package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +1 -1
  118. package/src/runtime/agent/orchestrator/providers/oauth-secret-fields.fixtures.mjs +45 -0
  119. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
  120. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +9 -2
  121. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +7 -0
  122. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +14 -3
  123. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +4 -2
  124. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
  125. package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +5 -3
  126. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +16 -3
  127. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
  128. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +5 -2
  129. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +13 -3
  130. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +13 -2
  131. package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +81 -42
  132. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +8 -0
  133. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +160 -31
  134. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +22 -12
  135. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +7 -1
  136. package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +49 -22
  137. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +1 -1
  138. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +3 -1
  139. package/src/runtime/agent/orchestrator/providers/registry.mjs +45 -2
  140. package/src/runtime/agent/orchestrator/providers/vertex-ai-auth.mjs +622 -0
  141. package/src/runtime/agent/orchestrator/providers/vertex-ai-maas.mjs +185 -0
  142. package/src/runtime/agent/orchestrator/providers/vertex-ai-models.mjs +445 -0
  143. package/src/runtime/agent/orchestrator/providers/vertex-ai.mjs +567 -0
  144. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +41 -13
  145. package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +2 -0
  146. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +8 -6
  147. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +7 -3
  148. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
  149. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +6 -1
  150. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +14 -5
  151. package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +1 -0
  152. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +5 -2
  153. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +30 -5
  154. package/src/runtime/agent/orchestrator/session/agent-room.mjs +171 -37
  155. package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +1 -3
  156. package/src/runtime/agent/orchestrator/session/cache-warm.mjs +102 -12
  157. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
  158. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -8
  159. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +23 -0
  160. package/src/runtime/agent/orchestrator/session/compact/observation-supersession.mjs +103 -23
  161. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +160 -36
  162. package/src/runtime/agent/orchestrator/session/context-utils.mjs +13 -1
  163. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +164 -23
  164. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +139 -19
  165. package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +38 -7
  166. package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +3 -0
  167. package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +100 -22
  168. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +4 -3
  169. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -6
  170. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +33 -3
  171. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +9 -2
  172. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -1
  173. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +13 -5
  174. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +14 -0
  175. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +13 -1
  176. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +23 -4
  177. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +2 -0
  178. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +23 -1
  179. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +39 -21
  180. package/src/runtime/agent/orchestrator/session/loop/turn-limits.mjs +13 -8
  181. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +50 -28
  182. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +3 -3
  183. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +3 -1
  184. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +2 -2
  185. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +1 -0
  186. package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +2 -1
  187. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +30 -13
  188. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +63 -6
  189. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +7 -0
  190. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +15 -0
  191. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +35 -13
  192. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +24 -14
  193. package/src/runtime/agent/orchestrator/session/manager/session-scratch-environment.mjs +70 -0
  194. package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +0 -3
  195. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +10 -6
  196. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +4 -0
  197. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +3 -0
  198. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +56 -29
  199. package/src/runtime/agent/orchestrator/session/manager/turn-reap.mjs +125 -0
  200. package/src/runtime/agent/orchestrator/session/mixy-name.mjs +11 -5
  201. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +5 -1
  202. package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +30 -7
  203. package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.mjs +154 -0
  204. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +7 -0
  205. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +4 -0
  206. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +15 -0
  207. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +37 -8
  208. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +252 -39
  209. package/src/runtime/agent/orchestrator/session/store-transcript-cache.mjs +163 -64
  210. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +164 -4
  211. package/src/runtime/agent/orchestrator/session/store-transcript-worker.mjs +142 -18
  212. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +61 -2
  213. package/src/runtime/agent/orchestrator/stall-policy.mjs +23 -0
  214. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +6 -2
  215. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +2 -2
  216. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -2
  217. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +6 -1
  218. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +46 -20
  219. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +7 -1
  220. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +67 -31
  221. package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +25 -13
  222. package/src/runtime/agent/orchestrator/tools/builtin/lib/scoped-search-flight.mjs +9 -5
  223. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +58 -20
  224. package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +7 -1
  225. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +6 -3
  226. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +1 -1
  227. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +23 -12
  228. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +6 -1
  229. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +1 -8
  230. package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +20 -15
  231. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +96 -20
  232. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +59 -10
  233. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/federation.mjs +3 -1
  234. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -1
  235. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
  236. package/src/runtime/agent/orchestrator/tools/lib/isolated-test-env.mjs +4 -3
  237. package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.mjs +99 -2
  238. package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +2 -0
  239. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +103 -13
  240. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +3 -1
  241. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +49 -15
  242. package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +45 -27
  243. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +34 -10
  244. package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +28 -4
  245. package/src/runtime/agent/orchestrator/tools/lib/wait-until-fixture.mjs +26 -0
  246. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -2
  247. package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +13 -13
  248. package/src/runtime/channels/lib/webhook/signature.mjs +4 -1
  249. package/src/runtime/channels/lib/whisper-port.mjs +29 -11
  250. package/src/runtime/channels/lib/whisper-server.mjs +10 -1
  251. package/src/runtime/computer-bridge/action-schema.mjs +2 -2
  252. package/src/runtime/computer-bridge/core-actions.mjs +2 -2
  253. package/src/runtime/computer-bridge/error-recovery.mjs +5 -4
  254. package/src/runtime/computer-bridge/linux-action-capabilities.mjs +24 -5
  255. package/src/runtime/computer-bridge/linux-readiness.d.mts +7 -2
  256. package/src/runtime/connectors/connector-accounts-mcp.mjs +236 -103
  257. package/src/runtime/connectors/gmail-app-password.mjs +3 -3
  258. package/src/runtime/explore/explorer.mjs +93 -0
  259. package/src/runtime/explore/tool-defs.mjs +35 -0
  260. package/src/runtime/explore/tool.mjs +123 -0
  261. package/src/runtime/local-provider/server-process.mjs +2 -1
  262. package/src/runtime/media/adapters/gemini-video.mjs +4 -1
  263. package/src/runtime/media/adapters/xai-media.mjs +2 -1
  264. package/src/runtime/memory/index.mjs +4 -0
  265. package/src/runtime/memory/lib/embedding-provider.mjs +4 -1
  266. package/src/runtime/memory/lib/embedding-worker.mjs +10 -3
  267. package/src/runtime/memory/lib/memory-action-handlers/cycle-backfill-action.mjs +1 -7
  268. package/src/runtime/memory/lib/memory-chunk-quality.mjs +1 -6
  269. package/src/runtime/office/authoring/pdf-author-action.mjs +1 -6
  270. package/src/runtime/office/design/design-art-direction.mjs +29 -27
  271. package/src/runtime/office/pdf/pdf-writer.mjs +6 -1
  272. package/src/runtime/office/portable/portable-snapshot-docx.mjs +7 -1
  273. package/src/runtime/office/portable/portable-snapshot-shared.mjs +6 -1
  274. package/src/runtime/office/quality/assurance-rendered.mjs +9 -2
  275. package/src/runtime/office/quality/assurance-structure-docx.mjs +4 -2
  276. package/src/runtime/office/quality/design-review.mjs +1 -5
  277. package/src/runtime/office/quality/document-brief.mjs +1 -5
  278. package/src/runtime/office/quality/forced-break-pages.mjs +9 -6
  279. package/src/runtime/office/quality/quality-pipeline.mjs +1 -1
  280. package/src/runtime/shared/agent-route-config.mjs +17 -2
  281. package/src/runtime/shared/atomic-file.mjs +103 -2
  282. package/src/runtime/shared/background-tasks.mjs +15 -6
  283. package/src/runtime/shared/bot-owned-work.mjs +56 -0
  284. package/src/runtime/shared/connector-accounts.mjs +5 -5
  285. package/src/runtime/shared/connector-catalog.d.mts +11 -4
  286. package/src/runtime/shared/connector-catalog.mjs +60 -52
  287. package/src/runtime/shared/custom-provider-keys.mjs +20 -0
  288. package/src/runtime/shared/defaults-separation.mjs +1 -4
  289. package/src/runtime/shared/fetch-safe-listen.d.mts +16 -0
  290. package/src/runtime/shared/fetch-safe-listen.mjs +50 -0
  291. package/src/runtime/shared/file-has-data.mjs +26 -2
  292. package/src/runtime/shared/file-lock.mjs +13 -15
  293. package/src/runtime/shared/git-launch.d.mts +9 -0
  294. package/src/runtime/shared/git-launch.mjs +213 -0
  295. package/src/runtime/shared/git-runner-worker.mjs +2 -1
  296. package/src/runtime/shared/handoff-artifacts.d.mts +2 -1
  297. package/src/runtime/shared/handoff-artifacts.mjs +18 -8
  298. package/src/runtime/shared/idle-auto-clear-gate.mjs +29 -7
  299. package/src/runtime/shared/llm/cost.mjs +11 -2
  300. package/src/runtime/shared/llm/http-agent.mjs +313 -29
  301. package/src/runtime/shared/llm/model-catalog-projection.mjs +3 -2
  302. package/src/runtime/shared/llm/model-catalog.mjs +46 -6
  303. package/src/runtime/shared/llm/provider-catalog-registry.mjs +3 -0
  304. package/src/runtime/shared/llm/system-proxy.mjs +452 -0
  305. package/src/runtime/shared/loopback-listener.mjs +25 -2
  306. package/src/runtime/shared/model-route-chain.d.mts +7 -2
  307. package/src/runtime/shared/model-route-chain.mjs +22 -4
  308. package/src/runtime/shared/open-url.mjs +10 -0
  309. package/src/runtime/shared/provider-accounts.mjs +52 -2
  310. package/src/runtime/shared/session-activity-signal.mjs +14 -0
  311. package/src/runtime/shared/session-scratch.mjs +37 -0
  312. package/src/runtime/shared/staged-install-worker.mjs +5 -0
  313. package/src/runtime/shared/staged-update.mjs +47 -2
  314. package/src/runtime/shared/tool-card-model/agent-surface.mjs +1 -3
  315. package/src/runtime/shared/tool-card-model/card-status.mjs +15 -2
  316. package/src/runtime/shared/tool-result-summary.mjs +1 -1
  317. package/src/runtime/shared/tool-surface.mjs +113 -36
  318. package/src/runtime/shared/tool-work-units.mjs +6 -0
  319. package/src/runtime/shared/turn-snapshot-store.mjs +13 -6
  320. package/src/runtime/shared/turn-worktree-snapshot.mjs +414 -54
  321. package/src/runtime/shared/worktrees/activity-gate.mjs +215 -318
  322. package/src/runtime/shared/worktrees/activity-ledger-worker.mjs +56 -0
  323. package/src/runtime/shared/worktrees/activity-ledger.mjs +391 -0
  324. package/src/runtime/shared/worktrees/branch-management.mjs +9 -3
  325. package/src/runtime/shared/worktrees/contract.mjs +4 -1
  326. package/src/runtime/shared/worktrees/git.mjs +72 -0
  327. package/src/runtime/shared/worktrees/manager.mjs +285 -168
  328. package/src/runtime/shared/worktrees/test-support.mjs +6 -10
  329. package/src/runtime/tidy/install.mjs +95 -15
  330. package/src/runtime/tidy/languages.mjs +17 -15
  331. package/src/runtime/tidy/process.mjs +7 -4
  332. package/src/runtime/tidy/resolve.mjs +19 -16
  333. package/src/runtime/tidy/runners/biome.mjs +3 -3
  334. package/src/runtime/tidy/tool.mjs +1 -1
  335. package/src/runtime/tidy/worktree.mjs +10 -6
  336. package/src/runtime/visualize-bridge/client.mjs +6 -1
  337. package/src/runtime/visualize-bridge/tool-defs.mjs +7 -2
  338. package/src/runtime/web-search/index.mjs +1 -1
  339. package/src/session-runtime/agent-home-delivery.mjs +41 -0
  340. package/src/session-runtime/agent-task-sessions.mjs +458 -312
  341. package/src/session-runtime/agent-tool-routing.mjs +8 -41
  342. package/src/session-runtime/boot/tools.mjs +7 -1
  343. package/src/session-runtime/bot-status.mjs +107 -101
  344. package/src/session-runtime/bot-target.mjs +38 -0
  345. package/src/session-runtime/channel-config-api.mjs +6 -1
  346. package/src/session-runtime/connector-merge-migration.mjs +18 -15
  347. package/src/session-runtime/conversation-import/codex-titles.mjs +88 -0
  348. package/src/session-runtime/conversation-import/discovery.mjs +401 -36
  349. package/src/session-runtime/conversation-import/service.mjs +35 -8
  350. package/src/session-runtime/handoff-attachments.mjs +82 -31
  351. package/src/session-runtime/held-requests.mjs +3 -1
  352. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +12 -13
  353. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +1 -0
  354. package/src/session-runtime/internal-tool-executor/feature-tools.mjs +2 -0
  355. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +8 -0
  356. package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +4 -1
  357. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +145 -112
  358. package/src/session-runtime/lifecycle/cold-heir.mjs +11 -6
  359. package/src/session-runtime/lifecycle/session-catalog.mjs +9 -1
  360. package/src/session-runtime/lifecycle/teardown/session-close.mjs +6 -1
  361. package/src/session-runtime/lifecycle/teardown.mjs +1 -1
  362. package/src/session-runtime/mcp-glue/server-catalog.mjs +16 -10
  363. package/src/session-runtime/mcp-glue/server-input.mjs +4 -1
  364. package/src/session-runtime/mixy-heartbeat.mjs +1 -1
  365. package/src/session-runtime/model-route/set-route.mjs +11 -2
  366. package/src/session-runtime/provider-auth/login.mjs +2 -2
  367. package/src/session-runtime/provider-catalog-cache.mjs +82 -36
  368. package/src/session-runtime/provider-models/catalog-load.mjs +23 -6
  369. package/src/session-runtime/provider-models.mjs +4 -2
  370. package/src/session-runtime/resource-mcp-api.mjs +98 -35
  371. package/src/session-runtime/resource-mcp-connector-accounts.mjs +44 -31
  372. package/src/session-runtime/resource-mcp-secrets.mjs +28 -11
  373. package/src/session-runtime/resource-mcp-servers.mjs +5 -4
  374. package/src/session-runtime/resource-recall-api.mjs +6 -1
  375. package/src/session-runtime/runtime-feature-gates.mjs +5 -1
  376. package/src/session-runtime/services/agent-tool/execute.mjs +14 -9
  377. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +20 -4
  378. package/src/session-runtime/services/agent-tool/model-select.mjs +48 -7
  379. package/src/session-runtime/services/agent-tool/render.mjs +71 -17
  380. package/src/session-runtime/services/agent-tool/send-flow.mjs +10 -2
  381. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +8 -3
  382. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +4 -1
  383. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +6 -2
  384. package/src/session-runtime/services/agent-tool/tool-def.mjs +9 -9
  385. package/src/session-runtime/services/agent-watchdog-registry.mjs +32 -4
  386. package/src/session-runtime/services/channel-worker/daemon-spawn.mjs +2 -0
  387. package/src/session-runtime/services/daemon-candidate.mjs +4 -1
  388. package/src/session-runtime/services/memory-runtime-proxy/daemon-spawn.mjs +4 -0
  389. package/src/session-runtime/services/plugin-admin.mjs +10 -6
  390. package/src/session-runtime/services/provider-admin.mjs +283 -11
  391. package/src/session-runtime/session-turn-api.mjs +2 -0
  392. package/src/session-runtime/settings-builtin-tools-api.mjs +4 -1
  393. package/src/session-runtime/settings-system-api.mjs +1 -1
  394. package/src/session-runtime/setup-tool/agent-settings-actions.mjs +13 -3
  395. package/src/session-runtime/setup-tool/connector-widen-approval.mjs +96 -0
  396. package/src/session-runtime/setup-tool/executor.mjs +6 -2
  397. package/src/session-runtime/setup-tool/tool-defs.mjs +2 -1
  398. package/src/session-runtime/tool-policy/policy-stage.mjs +1 -0
  399. package/src/session-runtime/turn/agent-ops.mjs +23 -0
  400. package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +49 -11
  401. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +5 -1
  402. package/src/session-runtime/workflow-agents-api/agent-route.mjs +25 -5
  403. package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -3
  404. package/src/session-runtime/workspace/activity-guards.mjs +176 -0
  405. package/src/session-runtime/workspace/session-view.mjs +149 -0
  406. package/src/session-runtime/workspace/side-checkouts.mjs +203 -0
  407. package/src/session-runtime/workspace/transition-runner.mjs +188 -0
  408. package/src/session-runtime/workspace/transitions.mjs +108 -1041
  409. package/src/session-runtime/workspace/workspace-merge.mjs +132 -0
  410. package/src/session-runtime/workspace/worktree-intents.mjs +242 -0
  411. package/src/session-runtime/workspace/worktree-maintenance.mjs +80 -0
  412. package/src/session-runtime/workspace/worktree-setup.mjs +85 -0
  413. package/src/session-runtime/workspace/worktree-tool.mjs +73 -56
  414. package/src/standalone/daemon-desktop-runtime.mjs +2 -0
  415. package/src/standalone/daemon-session-control.mjs +169 -138
  416. package/src/standalone/daemon.mjs +7 -0
  417. package/src/standalone/desktop-service-registry.mjs +9 -0
  418. package/src/standalone/local-session-runtime.mjs +5 -1
  419. package/src/standalone/session-client/daemon-unavailable.mjs +3 -0
  420. package/src/standalone/session-client.mjs +5 -1
  421. package/src/standalone/session-frame-stream.mjs +83 -11
  422. package/src/standalone/session-protocol.mjs +4 -0
  423. package/src/standalone/session-service/agent-tree/agent-turns.mjs +16 -12
  424. package/src/standalone/session-service/agent-tree.mjs +45 -9
  425. package/src/standalone/session-service/projection/transcript-window.mjs +700 -64
  426. package/src/standalone/session-service/session-calls/session-action.mjs +10 -3
  427. package/src/standalone/session-service/session-calls/session-views.mjs +160 -104
  428. package/src/standalone/session-service/stored-reader.mjs +127 -40
  429. package/src/standalone/session-transport/client-registry/client-grace.mjs +12 -9
  430. package/src/tui/App.jsx +154 -164
  431. package/src/tui/app/app-view/floating-panel.jsx +10 -4
  432. package/src/tui/app/app-view/text-entry-labels.mjs +1 -0
  433. package/src/tui/app/clipboard.mjs +21 -10
  434. package/src/tui/app/model-picker/route-selection/fast-parameters.mjs +1 -3
  435. package/src/tui/app/model-picker/route-selection.mjs +5 -6
  436. package/src/tui/app/prompt-handlers/use-prompt-history-nav.mjs +1 -1
  437. package/src/tui/app/prompt-handlers/use-prompt-interrupt.mjs +3 -0
  438. package/src/tui/app/prompt-submit/provider-prompts.mjs +3 -1
  439. package/src/tui/app/provider-setup/api-key-actions.mjs +31 -1
  440. package/src/tui/app/provider-setup/oauth-actions.mjs +58 -21
  441. package/src/tui/app/provider-setup/provider-items.mjs +8 -2
  442. package/src/tui/app/provider-setup-picker.mjs +29 -18
  443. package/src/tui/app/shell-layout/row-budgets.mjs +12 -10
  444. package/src/tui/app/slash-commands.mjs +1 -0
  445. package/src/tui/app/slash-dispatch/session-commands.mjs +46 -0
  446. package/src/tui/app/slash-dispatch.mjs +1 -1
  447. package/src/tui/app/use-btw-panel.mjs +29 -0
  448. package/src/tui/app/use-context-panel-refresh.mjs +31 -0
  449. package/src/tui/app/use-model-catalog-cache.mjs +13 -10
  450. package/src/tui/app/use-mouse-input.mjs +11 -0
  451. package/src/tui/app/use-onboarding-start.mjs +8 -3
  452. package/src/tui/app/use-panel-transition.mjs +10 -1
  453. package/src/tui/app/use-pasted-buffers.mjs +28 -20
  454. package/src/tui/app/use-prompt-draft-flow.mjs +7 -0
  455. package/src/tui/app/use-prompt-layout-rows.mjs +3 -3
  456. package/src/tui/app/use-prompt-queue-history.mjs +3 -2
  457. package/src/tui/app/use-selection-focus.mjs +11 -2
  458. package/src/tui/app/use-selection-geometry.mjs +18 -9
  459. package/src/tui/app/use-selection-paint.mjs +4 -4
  460. package/src/tui/app/use-selection-stitch.mjs +3 -3
  461. package/src/tui/app/use-slash-palette.mjs +62 -0
  462. package/src/tui/app/use-terminal-chrome.mjs +2 -2
  463. package/src/tui/app/use-transcript-activity.mjs +47 -32
  464. package/src/tui/app/use-transcript-follow.mjs +12 -4
  465. package/src/tui/app/use-transcript-scroll.mjs +46 -17
  466. package/src/tui/app/use-transcript-swap-reset.mjs +27 -0
  467. package/src/tui/app/use-transcript-window/geometry.mjs +19 -5
  468. package/src/tui/app/use-transcript-window/instance-state.mjs +4 -0
  469. package/src/tui/app/use-transcript-window/selection-shift.mjs +5 -1
  470. package/src/tui/app/use-transcript-window.mjs +21 -11
  471. package/src/tui/app/use-tui-ready.mjs +18 -0
  472. package/src/tui/app/use-welcome-prompt-hint.mjs +5 -0
  473. package/src/tui/app/use-workflow-tab-cycle.mjs +5 -3
  474. package/src/tui/components/Picker.jsx +27 -3
  475. package/src/tui/components/QuestionPanel.jsx +13 -4
  476. package/src/tui/components/SignInUrl.jsx +68 -0
  477. package/src/tui/components/TextEntryPanel.jsx +76 -46
  478. package/src/tui/components/sign-in-url-layout.mjs +24 -0
  479. package/src/tui/dist/index.mjs +1963 -1361
  480. package/src/tui/session/goal-turn-state.mjs +6 -2
  481. package/src/tui/session/session-api/controls.mjs +4 -0
  482. package/src/tui/session/session-api/extensions.mjs +8 -0
  483. package/src/tui/session/session-api/intake/abort.mjs +2 -1
  484. package/src/tui/session/session-api/intake.mjs +2 -0
  485. package/src/tui/session/session-api/integrations.mjs +2 -2
  486. package/src/tui/session/session-api/routes-agent-definitions.mjs +1 -0
  487. package/src/tui/session/session-api/settings.mjs +2 -2
  488. package/src/tui/session/session-flow/queue/take-entries.mjs +11 -0
  489. package/src/tui/session/transcript-spill/spill-writer.mjs +15 -0
  490. package/src/tui/session/transcript-spill.mjs +92 -4
  491. package/src/tui/session/transcript-store.mjs +2 -0
  492. package/src/ui/statusline-agents.mjs +1 -3
  493. package/src/defaults/skills/skill-creator/references/source-review.md +0 -94
@@ -17,6 +17,7 @@ import { scheduleCallerScope } from '../services/schedule-access.mjs';
17
17
  import { worktreeCallerScope } from '../services/worktree-access.mjs';
18
18
  import { resolveHandoffAttachments, sessionRoots } from '../handoff-attachments.mjs';
19
19
  import { delegationChainOf } from '../bot-chain.mjs';
20
+ import { BOT_TARGET_ACTIONS, resolveBotTarget } from '../bot-target.mjs';
20
21
 
21
22
  const USER_TITLE_ERROR = 'session: the user already titled this session; the title was not changed';
22
23
 
@@ -41,6 +42,140 @@ function callerAttachmentParts(rt, args, callerCwd, surface) {
41
42
  }).parts;
42
43
  }
43
44
 
45
+ /** `session` actions that act on another session through session control
46
+ * (list, bots, read, send, cancel, delete, create, status of a target). */
47
+ async function runSessionControl(
48
+ { rt, executeSessionControl, listBots, getSessionManager },
49
+ args,
50
+ { action, target, callerSessionId, callerCwd }
51
+ ) {
52
+ // `callerAgentId`: the bot whose conversation gets results reported
53
+ // back; `callerBotId`: the bot the caller acts as, attended or not,
54
+ // for what only a bot's own sessions allow (an effort change).
55
+ const callerAgentId = attendedAgentOf(rt.session);
56
+ const acting = resolveSessionMode({ session: rt.session });
57
+ const callerBotId = acting.mode === AGENT_MODE ? acting.agentId : '';
58
+ if (action === 'create' && !callerAgentId) {
59
+ throw new Error(
60
+ 'session: create belongs to a bot conversation; delegated workers, agent task sessions and schedule/webhook runs cannot create agent task sessions'
61
+ );
62
+ }
63
+ // A bot id names that bot's home chat; every check below and in the
64
+ // control runs on the chat it resolves to.
65
+ const sessionTarget = BOT_TARGET_ACTIONS.has(action) && target ? await resolveBotTarget(target, listBots) : target;
66
+ // A task session reaches its agent only through its reported result.
67
+ const origin = rt.session?.taskOrigin;
68
+ if (
69
+ action === 'send' &&
70
+ origin &&
71
+ (sessionTarget === origin.sessionId || agentIdOfHomeSession(sessionTarget) === origin.agentId)
72
+ ) {
73
+ throw new Error('session: a task session reports to its agent only through its result');
74
+ }
75
+ if (typeof executeSessionControl !== 'function') {
76
+ throw new Error('session: cross-session control is unavailable in this runtime');
77
+ }
78
+ const { attachments, ...rest } = args || {};
79
+ if (attachments !== undefined && action !== 'send' && action !== 'create') {
80
+ throw new Error(`session: attachments apply to send and create, not action=${action}`);
81
+ }
82
+ const attachmentParts = callerAttachmentParts(rt, args, callerCwd, 'session');
83
+ return JSON.stringify(
84
+ await executeSessionControl(
85
+ { ...rest, action, ...(sessionTarget !== target ? { session_id: sessionTarget } : {}) },
86
+ {
87
+ callerSessionId,
88
+ callerAgentId,
89
+ callerBotId,
90
+ callerCwd,
91
+ ...(action === 'send' || action === 'create'
92
+ ? {
93
+ callerHop: turnHopOf(rt.session),
94
+ // The bot chain this turn acts in (bot-chain.mjs), checked
95
+ // against the target and carried to the turn it starts —
96
+ // only when the sender is a bot conversation (a bot's chat,
97
+ // room seat or run); any other session hands work to any
98
+ // bot as before, whatever started its turn (the hop limit
99
+ // still bounds a loop).
100
+ callerBotChain: callerBotId
101
+ ? delegationChainOf(callerSessionId, (id) =>
102
+ id === rt.session?.id ? rt.session : getSessionManager()?.getSession?.(id) || null
103
+ )
104
+ : [],
105
+ }
106
+ : {}),
107
+ // Whether a create may provision a worktree (worktree-access.mjs).
108
+ ...(action === 'create' ? { callerScope: worktreeCallerScope(rt.session) } : {}),
109
+ // Which bots and sessions it may see (bot-status.mjs).
110
+ ...(action === 'bots' || action === 'list' ? { callerScope: scheduleCallerScope(rt.session) } : {}),
111
+ ...(attachmentParts.length ? { attachmentParts } : {}),
112
+ }
113
+ )
114
+ );
115
+ }
116
+
117
+ /** `session action=rename`: the AI title of the Lead session, mirrored onto the live session. */
118
+ async function renameSession({ rt, getSessionManager }, action, sessionId, args) {
119
+ if (rt.config?.aiSessionTitles === false) throw new Error('session: AI session titles are disabled in settings');
120
+ const title = clean(args?.title);
121
+ if (!title) throw new Error('session: title is required for action=rename');
122
+ const result = await getSessionManager()?.updateSessionAiTitle(sessionId, title);
123
+ if (!result?.ok) {
124
+ throw new Error(
125
+ result?.reason === 'user-title' ? USER_TITLE_ERROR : `session: rename failed (${result?.reason || 'unavailable'})`
126
+ );
127
+ }
128
+ // Mirror the store's truth onto the live session, never the AI title
129
+ // itself: a manual rename that landed while the save was awaited wins.
130
+ const stored = getSessionManager()?.getSession?.(sessionId);
131
+ if (stored && rt.session?.id === sessionId && stored !== rt.session) {
132
+ rt.session.title = stored.title;
133
+ rt.session.titleLocked = stored.titleLocked;
134
+ rt.session.titleSource = stored.titleSource;
135
+ rt.session.titleUpdatedAt = stored.titleUpdatedAt;
136
+ }
137
+ if (stored && stored.titleSource === 'user') throw new Error(USER_TITLE_ERROR);
138
+ return JSON.stringify({ action, sessionId, title: result.title }, null, 2);
139
+ }
140
+
141
+ /** `session action=fork`: inherit the Lead session into a new one. */
142
+ async function forkSession(getLifecycleApi, action, sessionId, args) {
143
+ const lifecycle = getLifecycleApi();
144
+ if (typeof lifecycle?.inheritToNewSession !== 'function') {
145
+ throw new Error('session: fork is unavailable in this runtime');
146
+ }
147
+ const result = await lifecycle.inheritToNewSession(sessionId, {
148
+ compact: args?.compact === true,
149
+ titleSuffix: ' (Fork)',
150
+ });
151
+ return JSON.stringify(
152
+ { action, sourceSessionId: sessionId, sessionId: result.sessionId, compacted: result.compacted === true },
153
+ null,
154
+ 2
155
+ );
156
+ }
157
+
158
+ /** `session action=status`: the current session's identity, folder and route. */
159
+ function sessionStatusJson(rt, callerCtx, currentCwd) {
160
+ const current = rt.session;
161
+ return JSON.stringify(
162
+ {
163
+ sessionId: callerCtx?.callerSessionId || current?.id || null,
164
+ title: current?.title || null,
165
+ titleSource: current?.titleSource || null,
166
+ cwd: currentCwd,
167
+ project: current?.workspace?.projectPath || current?.desktopSession?.projectPath || currentCwd,
168
+ provider: current?.provider || null,
169
+ model: current?.model || null,
170
+ effort: current?.effort || null,
171
+ autoEffort: current?.autoEffort !== false,
172
+ fast: current?.fast === true,
173
+ },
174
+ null,
175
+ 2
176
+ );
177
+ }
178
+
44
179
  export function createWorkspaceToolHandlers({
45
180
  rt,
46
181
  goalRuntime,
@@ -50,6 +185,7 @@ export function createWorkspaceToolHandlers({
50
185
  getSessionManager = () => null,
51
186
  getLifecycleApi = () => null,
52
187
  executeSessionControl = null,
188
+ listBots,
53
189
  }) {
54
190
  const assertLead = (action, callerCtx) => {
55
191
  const callerSessionId = callerCtx?.callerSessionId || rt.session?.id || null;
@@ -99,66 +235,12 @@ export function createWorkspaceToolHandlers({
99
235
  (action === 'status' && target)
100
236
  ) {
101
237
  const callerSessionId = assertLead(action, callerCtx);
102
- // `callerAgentId`: the bot whose conversation gets results reported
103
- // back; `callerBotId`: the bot the caller acts as, attended or not,
104
- // for what only a bot's own sessions allow (an effort change).
105
- const callerAgentId = attendedAgentOf(rt.session);
106
- const acting = resolveSessionMode({ session: rt.session });
107
- const callerBotId = acting.mode === AGENT_MODE ? acting.agentId : '';
108
- if (action === 'create' && !callerAgentId) {
109
- throw new Error(
110
- 'session: create belongs to a bot conversation; delegated workers, agent task sessions and schedule/webhook runs cannot create agent task sessions'
111
- );
112
- }
113
- // A task session reaches its agent only through its reported result.
114
- const origin = rt.session?.taskOrigin;
115
- if (
116
- action === 'send' &&
117
- origin &&
118
- (target === origin.sessionId || agentIdOfHomeSession(target) === origin.agentId)
119
- ) {
120
- throw new Error('session: a task session reports to its agent only through its result');
121
- }
122
- if (typeof executeSessionControl !== 'function') {
123
- throw new Error('session: cross-session control is unavailable in this runtime');
124
- }
125
- const { attachments, ...rest } = args || {};
126
- if (attachments !== undefined && action !== 'send' && action !== 'create') {
127
- throw new Error(`session: attachments apply to send and create, not action=${action}`);
128
- }
129
- const attachmentParts = callerAttachmentParts(rt, args, callerCwd, 'session');
130
- return JSON.stringify(
131
- await executeSessionControl(
132
- { ...rest, action },
133
- {
134
- callerSessionId,
135
- callerAgentId,
136
- callerBotId,
137
- callerCwd,
138
- ...(action === 'send' || action === 'create'
139
- ? {
140
- callerHop: turnHopOf(rt.session),
141
- // The bot chain this turn acts in (bot-chain.mjs), checked
142
- // against the target and carried to the turn it starts —
143
- // only when the sender is a bot conversation (a bot's chat,
144
- // room seat or run); any other session hands work to any
145
- // bot as before, whatever started its turn (the hop limit
146
- // still bounds a loop).
147
- callerBotChain: callerBotId
148
- ? delegationChainOf(callerSessionId, (id) =>
149
- id === rt.session?.id ? rt.session : getSessionManager()?.getSession?.(id) || null
150
- )
151
- : [],
152
- }
153
- : {}),
154
- // Whether a create may provision a worktree (worktree-access.mjs).
155
- ...(action === 'create' ? { callerScope: worktreeCallerScope(rt.session) } : {}),
156
- // Which bots and sessions it may see (bot-status.mjs).
157
- ...(action === 'bots' || action === 'list' ? { callerScope: scheduleCallerScope(rt.session) } : {}),
158
- ...(attachmentParts.length ? { attachmentParts } : {}),
159
- }
160
- )
161
- );
238
+ return runSessionControl({ rt, executeSessionControl, listBots, getSessionManager }, args, {
239
+ action,
240
+ target,
241
+ callerSessionId,
242
+ callerCwd,
243
+ });
162
244
  }
163
245
  // Mixy's proposals to the user (mixy-heartbeat.mjs): kept so none repeats.
164
246
  if (action === 'suggest') {
@@ -178,28 +260,7 @@ export function createWorkspaceToolHandlers({
178
260
  if (action === 'status' || action === 'list-projects' || action === 'set-project') assertLead(action, callerCtx);
179
261
  if (action === 'rename') {
180
262
  const sessionId = assertLead(action, callerCtx);
181
- if (rt.config?.aiSessionTitles === false) throw new Error('session: AI session titles are disabled in settings');
182
- const title = clean(args?.title);
183
- if (!title) throw new Error('session: title is required for action=rename');
184
- const result = await getSessionManager()?.updateSessionAiTitle(sessionId, title);
185
- if (!result?.ok) {
186
- throw new Error(
187
- result?.reason === 'user-title'
188
- ? USER_TITLE_ERROR
189
- : `session: rename failed (${result?.reason || 'unavailable'})`
190
- );
191
- }
192
- // Mirror the store's truth onto the live session, never the AI title
193
- // itself: a manual rename that landed while the save was awaited wins.
194
- const stored = getSessionManager()?.getSession?.(sessionId);
195
- if (stored && rt.session?.id === sessionId && stored !== rt.session) {
196
- rt.session.title = stored.title;
197
- rt.session.titleLocked = stored.titleLocked;
198
- rt.session.titleSource = stored.titleSource;
199
- rt.session.titleUpdatedAt = stored.titleUpdatedAt;
200
- }
201
- if (stored && stored.titleSource === 'user') throw new Error(USER_TITLE_ERROR);
202
- return JSON.stringify({ action, sessionId, title: result.title }, null, 2);
263
+ return renameSession({ rt, getSessionManager }, action, sessionId, args);
203
264
  }
204
265
  if (action === 'compact') {
205
266
  const sessionId = assertLead(action, callerCtx);
@@ -213,38 +274,10 @@ export function createWorkspaceToolHandlers({
213
274
  }
214
275
  if (action === 'fork') {
215
276
  const sessionId = assertLead(action, callerCtx);
216
- const lifecycle = getLifecycleApi();
217
- if (typeof lifecycle?.inheritToNewSession !== 'function') {
218
- throw new Error('session: fork is unavailable in this runtime');
219
- }
220
- const result = await lifecycle.inheritToNewSession(sessionId, {
221
- compact: args?.compact === true,
222
- titleSuffix: ' (Fork)',
223
- });
224
- return JSON.stringify(
225
- { action, sourceSessionId: sessionId, sessionId: result.sessionId, compacted: result.compacted === true },
226
- null,
227
- 2
228
- );
277
+ return forkSession(getLifecycleApi, action, sessionId, args);
229
278
  }
230
279
  if (action === 'status') {
231
- const current = rt.session;
232
- return JSON.stringify(
233
- {
234
- sessionId: callerCtx?.callerSessionId || current?.id || null,
235
- title: current?.title || null,
236
- titleSource: current?.titleSource || null,
237
- cwd: currentCwd,
238
- project: current?.workspace?.projectPath || current?.desktopSession?.projectPath || currentCwd,
239
- provider: current?.provider || null,
240
- model: current?.model || null,
241
- effort: current?.effort || null,
242
- autoEffort: current?.autoEffort !== false,
243
- fast: current?.fast === true,
244
- },
245
- null,
246
- 2
247
- );
280
+ return sessionStatusJson(rt, callerCtx, currentCwd);
248
281
  }
249
282
  if (action === 'list-projects') {
250
283
  return JSON.stringify(
@@ -150,14 +150,19 @@ export function buildColdHeir(source, now = Date.now()) {
150
150
  // earlier folder) is the source's own: the heir's prompt is built for the
151
151
  // folder it actually starts in. Project memory follows the Project, which a
152
152
  // worktree shares, so the source's current memory slice carries over.
153
+ // In the same folder only the environment facts are rebuilt: they name the
154
+ // heir's own scratch directory, never the source's, while the inherited
155
+ // instruction files and memory stay as the source presented them.
153
156
  const pending = source.workspaceReroot;
154
157
  delete heir.workspaceReroot;
155
- if ((projectFolder || pending) && heir.cwd) {
156
- refreshSessionWorkspaceContext(heir, {
157
- cwd: heir.cwd,
158
- ...buildWorkspacePromptContext(heir, heir.cwd),
159
- coreMemoryContext: pending?.target?.coreMemoryContext,
160
- });
158
+ if (heir.cwd) {
159
+ const context = buildWorkspacePromptContext(heir, heir.cwd);
160
+ refreshSessionWorkspaceContext(
161
+ heir,
162
+ projectFolder || pending
163
+ ? { cwd: heir.cwd, ...context, coreMemoryContext: pending?.target?.coreMemoryContext }
164
+ : { cwd: heir.cwd, environmentContext: context.environmentContext }
165
+ );
161
166
  }
162
167
  invalidateContextUsageSnapshot(heir);
163
168
  invalidateProviderContextBaseline(heir);
@@ -21,7 +21,11 @@ import {
21
21
  wantsIncomingTail,
22
22
  } from '../../runtime/agent/orchestrator/session/store-summary-last-message.mjs';
23
23
  import { isRealTurnMessage } from '../../runtime/agent/orchestrator/session/compact/user-message-kind.mjs';
24
- import { realTurnCount as realTurnCountOf } from '../../runtime/agent/orchestrator/session/store-summary-fields.mjs';
24
+ import {
25
+ compactCount as compactCountOf,
26
+ compactCountField,
27
+ realTurnCount as realTurnCountOf,
28
+ } from '../../runtime/agent/orchestrator/session/store-summary-fields.mjs';
25
29
 
26
30
  const VISIBLE_OWNERS = ['cli', 'user', 'mixdog'];
27
31
 
@@ -107,6 +111,10 @@ export function leadSessionRow(s, working = false) {
107
111
  // sit among the counted ones: a bot row's unread pill counts those past
108
112
  // the read cursor. Absent when unknown; clients then show one.
109
113
  ...(incomingTail ? { incomingTail } : {}),
114
+ // The numbering epoch of messageCount: each compaction restarts it. A
115
+ // summary row carries it at the top; a stored session in its compaction
116
+ // record. Unknown stays absent.
117
+ ...compactCountField(compactCountOf(s) === null ? s.compaction : s),
110
118
  title: cleanSessionPreview(s.title || '', 100),
111
119
  preview,
112
120
  // The latest user or assistant line (an agent contact's row shows it).
@@ -4,6 +4,7 @@
4
4
  * tombstone barrier and the turn abort.
5
5
  */
6
6
  import { SessionClosedError } from '../../../runtime/agent/orchestrator/session/manager/session-errors.mjs';
7
+ import { armTurnTimeout } from '../../../runtime/agent/orchestrator/session/manager/turn-reap.mjs';
7
8
  import { isScratchSession } from '../shared.mjs';
8
9
 
9
10
  // SessionEnd reason mapped to standard values ('clear'/'exit' where
@@ -88,11 +89,15 @@ export function closeCanonicalSession(deps, reason = 'canonical-session-close',
88
89
  return true;
89
90
  }
90
91
 
91
- export function abortRuntime(deps, reason = 'cli-abort') {
92
+ /** `options.timeout` is the progress watchdog's explicit "this abort is a
93
+ * timeout": the reap is armed before either abort runs, while the turn is
94
+ * still un-aborted (the outer abort cascades into the manager's controller). */
95
+ export function abortRuntime(deps, reason = 'cli-abort', { timeout = false } = {}) {
92
96
  const { getSession, getReservedSessionId, abortActiveTurns, mgr } = deps;
93
97
  const session = getSession();
94
98
  const sessionId = session?.id || getReservedSessionId?.() || 'pending';
95
99
  const abortError = new SessionClosedError(sessionId, `runtime abort (reason=${reason})`, reason);
100
+ if (timeout && session?.id) armTurnTimeout(session.id);
96
101
  let outerAborted = false;
97
102
  try {
98
103
  outerAborted = abortActiveTurns?.(abortError) === true;
@@ -281,6 +281,6 @@ export function createTeardown(deps, { ingestSessionIntoMemory, closeSurfaceSess
281
281
  return {
282
282
  closeCanonicalSession: (reason, options) => closeCanonicalSession(deps, reason, options),
283
283
  close,
284
- abort: (reason) => abortRuntime(deps, reason),
284
+ abort: (reason, options) => abortRuntime(deps, reason, options),
285
285
  };
286
286
  }
@@ -9,7 +9,7 @@ import {
9
9
  connectorOfServer,
10
10
  gmailAppPasswordAccount,
11
11
  } from '../../runtime/shared/connector-catalog.mjs';
12
- import { connectorAccountHealth } from '../../runtime/connectors/connector-accounts-mcp.mjs';
12
+ import { connectorAccountHealth, connectorAccountInUse } from '../../runtime/connectors/connector-accounts-mcp.mjs';
13
13
  import { googleOAuthClient } from '../../runtime/shared/google-oauth-client.mjs';
14
14
  import { envFlag } from '../../runtime/shared/env.mjs';
15
15
  import { resolveMcpServerCatalog } from './plugin-servers.mjs';
@@ -119,10 +119,12 @@ export function createMcpServerCatalog({
119
119
  };
120
120
  }
121
121
 
122
- /** One account of a multi-account connector server, as its row lists it:
123
- * `{ name, email, enabled?, appPassword?, oauth?, ownClient?, error?, authRequired? }`
124
- * (`enabled: false` only for an account that is switched off). */
125
- function accountRow(accountCfg, enabled) {
122
+ /** One account of a connector server, as its row lists it:
123
+ * `{ name, email, enabled?, inUse?, appPassword?, oauth?, ownClient?, error?, authRequired? }`
124
+ * (`enabled: false` only for an account that is switched off; `inUse:
125
+ * true` only for the connector's last-used account, the one a call
126
+ * without a conversation history uses). */
127
+ function accountRow(accountCfg, enabled, inUse) {
126
128
  const address = gmailAppPasswordAccount(accountCfg);
127
129
  const oauth = address ? null : mcpClient.getMcpServerOAuthState?.(accountCfg);
128
130
  const state = connectorAccountHealth(accountCfg.name);
@@ -130,6 +132,7 @@ export function createMcpServerCatalog({
130
132
  return {
131
133
  name: accountCfg.name,
132
134
  ...(enabled ? {} : { enabled: false }),
135
+ ...(inUse ? { inUse: true } : {}),
133
136
  email: address || oauth?.account || state?.email || '',
134
137
  ...(address ? { appPassword: { account: address } } : {}),
135
138
  ...(oauth ? { oauth } : {}),
@@ -152,11 +155,14 @@ export function createMcpServerCatalog({
152
155
  failures.get(name) || (dead ? { msg: live.error || live.lastError || 'MCP server is not alive' } : undefined);
153
156
  const entry = entries[name] || {};
154
157
  const loginPending = !(live && !dead) && mcpClient.isMcpLoginPending?.(name, getMcpScopeId()) === true;
155
- const accounts = entry.owner
156
- ? []
157
- : connectorAccountsOf(cfg).map((account) =>
158
- accountRow(connectorAccountServer(cfg, account), connectorAccountEnabled(account))
159
- );
158
+ const stored = entry.owner ? [] : connectorAccountsOf(cfg);
159
+ // The connector's last-used account (none when that one was removed),
160
+ // else its topmost switched-on one.
161
+ const recorded = stored.length ? connectorAccountInUse(connectorOfServer(cfg)) : '';
162
+ const inUse = recorded || stored.find(connectorAccountEnabled)?.name;
163
+ const accounts = stored.map((account) =>
164
+ accountRow(connectorAccountServer(cfg, account), connectorAccountEnabled(account), account.name === inUse)
165
+ );
160
166
  // A connector server none of whose accounts is signed in needs a sign-in, not a repair.
161
167
  const accountsSignIn = accounts.length > 0 && accounts.every((account) => account.authRequired === true);
162
168
  const status = mcpServerStatus(
@@ -79,8 +79,11 @@ export function createMcpServerInput({ mcpClient, getCurrentCwd }) {
79
79
  const type = clean(input.type).toLowerCase();
80
80
  if (url) {
81
81
  const kind = URL_TRANSPORT_TYPES.get(type) || (/^wss?:\/\//i.test(url) ? 'ws' : 'http');
82
- const secureUrl =
82
+ const normalizedUrl =
83
83
  typeof mcpClient.normalizeMcpTransportUrl === 'function' ? mcpClient.normalizeMcpTransportUrl(url, kind) : url;
84
+ // URL parsing adds a "/" to a bare origin (`https://mcp.stripe.com`):
85
+ // keep the address as it was given, as the catalog and legacy entries do.
86
+ const secureUrl = normalizedUrl === `${url}/` ? url : normalizedUrl;
84
87
  return { name, config: withOptionalHeaders({ type: kind, url: secureUrl }) };
85
88
  }
86
89
  const command = clean(input.command);
@@ -635,7 +635,7 @@ export function createMixyHeartbeat({
635
635
  now = Date.now,
636
636
  path = defaultStatePath(),
637
637
  isEnabled = async () =>
638
- (await import('../runtime/agent/orchestrator/config.mjs')).loadConfig({ secrets: false }).heartbeat !== false,
638
+ (await import('../runtime/agent/orchestrator/config.mjs')).loadConfig({ secrets: false }).heartbeat === true,
639
639
  // Whether the representative bot exists (mixy-presence.mjs, the rule the
640
640
  // desktop and her routines follow): without it no check runs at all.
641
641
  mixyExists = async () => (await import('./routine-owner.mjs')).readMixyExists(),
@@ -217,11 +217,20 @@ function switchSessionInPlace(deps, session) {
217
217
 
218
218
  /** The Lead's fallback models after this selection, checked like an agent's
219
219
  * chain (leaf routes, distinct providers): an explicit list replaces the
220
- * stored one ([] clears it), a selection without one keeps it. */
220
+ * stored one ([] clears it), a selection without one keeps it as stored — a
221
+ * fallback on the new main model's provider stays saved and is inactive
222
+ * (executionFallbacks). */
221
223
  async function leadFallbackChain(deps, next, primary) {
222
224
  const storedPreset = findPreset(deps.getConfig(), workflowPresetId('lead'));
223
- const requested = hasOwn(next || {}, 'fallbacks') ? next.fallbacks : storedPreset?.fallbacks;
225
+ const explicit = hasOwn(next || {}, 'fallbacks');
226
+ const requested = explicit ? next.fallbacks : storedPreset?.fallbacks;
224
227
  if (!Array.isArray(requested) || !requested.length) return [];
228
+ if (!explicit) {
229
+ return (
230
+ normalizeWorkflowRoute({ provider: primary.provider, model: primary.model, fallbacks: requested })?.fallbacks ||
231
+ []
232
+ );
233
+ }
225
234
  const chain = routeChain({ provider: primary.provider, model: primary.model, fallbacks: requested });
226
235
  validateRouteChainProviders(chain);
227
236
  const storedChain = routeChain(storedPreset || {});
@@ -61,8 +61,8 @@ export function createLoginApi({ cfgMod, awaitKeychainPrewarm, reloadFullConfig,
61
61
  reloadFullConfig();
62
62
  return completeOAuthLogin(result);
63
63
  },
64
- async saveProviderApiKey(providerId, secret) {
65
- const result = await saveProviderApiKey(cfgMod, providerId, secret);
64
+ async saveProviderApiKey(providerId, secret, options) {
65
+ const result = await saveProviderApiKey(cfgMod, providerId, secret, options);
66
66
  refresh.afterCredentialChange();
67
67
  return result;
68
68
  },
@@ -37,6 +37,19 @@ let sharedPartialEntries = null;
37
37
  let sharedCatalogPromise = null;
38
38
  let sharedCatalogPromiseKey = null;
39
39
  let sharedCatalogPromiseForced = false;
40
+ // Every read that publishes to the shared state (a full load or a scoped
41
+ // refresh) runs through this queue, one at a time, so the last write is always
42
+ // the newest successful answer.
43
+ // A catalog state that moved on (new revision or account epoch) starts a fresh
44
+ // queue: reads of the old state never publish, so nothing waits for them.
45
+ let publishing = { key: null, tail: Promise.resolve() };
46
+ function serial(key, run) {
47
+ if (publishing.key !== key) publishing = { key, tail: Promise.resolve() };
48
+ const queue = publishing;
49
+ const result = queue.tail.then(run);
50
+ queue.tail = result.catch(() => {});
51
+ return result;
52
+ }
40
53
 
41
54
  export function catalogRevision(registry) {
42
55
  const value = Number(registry?.providerCatalogRevision?.());
@@ -60,6 +73,24 @@ export function invalidateSharedProviderCatalog() {
60
73
 
61
74
  const answered = (entry) => !Object.hasOwn(entry, 'error');
62
75
 
76
+ /** Folds a scoped refresh into the shared state of `key`: only the providers it
77
+ * answered are replaced; a failed entry replaces nothing. */
78
+ function mergeScopedEntries(key, scoped) {
79
+ const fresh = new Map(scoped.filter(answered).map((entry) => [entry.name, entry]));
80
+ const merge = (current) => current.map((entry) => fresh.get(entry.name) ?? entry);
81
+ if (sharedCatalogKey === key) {
82
+ sharedCatalogEntries = merge(sharedCatalogEntries);
83
+ } else if (sharedPartialKey === key) {
84
+ const merged = merge(sharedPartialEntries);
85
+ if (merged.every(answered)) {
86
+ // The refresh answered the last missing provider: the catalog is whole.
87
+ [sharedCatalogKey, sharedCatalogEntries, sharedPartialKey, sharedPartialEntries] = [key, merged, null, null];
88
+ } else {
89
+ sharedPartialEntries = merged;
90
+ }
91
+ }
92
+ }
93
+
63
94
  async function providerEntry(name, provider, timeoutMs, epoch) {
64
95
  if (typeof provider?.listModels !== 'function') return { name, models: [], ms: 0 };
65
96
  const startedAt = performance.now();
@@ -78,6 +109,23 @@ async function providerEntry(name, provider, timeoutMs, epoch) {
78
109
  return { name, models: Array.isArray(outcome.value) ? outcome.value : [], ms };
79
110
  }
80
111
 
112
+ /** Fresh entries for just the named providers (an account-pool provider is one
113
+ * entry, however many accounts it holds), merged into the shared catalog. */
114
+ export function scopedProviderCatalog(registry, names, { timeoutMs = PROVIDER_CATALOG_TIMEOUT_MS } = {}) {
115
+ const key = catalogKey(registry);
116
+ const wanted = new Set(names);
117
+ return serial(key, async () => {
118
+ const entries = await Promise.all(
119
+ [...registry.getAllProviders()]
120
+ .filter(([name]) => wanted.has(name))
121
+ .map(([name, provider]) => providerEntry(name, provider, timeoutMs, key))
122
+ );
123
+ // A catalog state that moved on while this read ran is not written to.
124
+ if (catalogKey(registry) === key) mergeScopedEntries(key, entries);
125
+ return entries;
126
+ });
127
+ }
128
+
81
129
  export async function sharedProviderCatalog(registry, { force = false, timeoutMs = PROVIDER_CATALOG_TIMEOUT_MS } = {}) {
82
130
  const key = catalogKey(registry);
83
131
  if (!force && sharedCatalogKey === key && Array.isArray(sharedCatalogEntries)) {
@@ -86,45 +134,43 @@ export async function sharedProviderCatalog(registry, { force = false, timeoutMs
86
134
  if (sharedCatalogPromise && sharedCatalogPromiseKey === key && (sharedCatalogPromiseForced || !force)) {
87
135
  return await sharedCatalogPromise;
88
136
  }
89
- const kept = new Map();
90
- if (!force && sharedPartialKey === key) {
91
- const now = Date.now();
92
- for (const entry of sharedPartialEntries) {
93
- if (answered(entry) || now < entry.retryAt) kept.set(entry.name, entry);
94
- }
95
- }
96
- const providers = [...registry.getAllProviders()];
97
- // Every missing provider is still inside its backoff: the partial answer is
98
- // the answer, without asking anyone.
99
- if (providers.length > 0 && providers.every(([name]) => kept.has(name))) {
100
- return providers.map(([name]) => kept.get(name));
101
- }
102
- // A pending list from an older catalog state (revision or account epoch) is
103
- // not joined: its answer may describe the previous catalog.
104
- const epoch = key;
105
137
  sharedCatalogPromiseKey = key;
106
138
  sharedCatalogPromiseForced = force;
107
- const request = Promise.all(
108
- providers.map(([name, provider]) => kept.get(name) || providerEntry(name, provider, timeoutMs, epoch))
109
- )
110
- .then((entries) => {
111
- if (sharedCatalogPromise === request && catalogKey(registry) === key) {
112
- const complete = entries.every(answered);
113
- const partial = !complete && entries.some(answered);
114
- sharedCatalogKey = complete ? key : null;
115
- sharedCatalogEntries = complete ? entries : null;
116
- sharedPartialKey = partial ? key : null;
117
- sharedPartialEntries = partial ? entries : null;
139
+ const request = serial(key, async () => {
140
+ const kept = new Map();
141
+ if (!force && sharedPartialKey === key) {
142
+ const now = Date.now();
143
+ for (const entry of sharedPartialEntries) {
144
+ if (answered(entry) || now < entry.retryAt) kept.set(entry.name, entry);
118
145
  }
119
- return entries;
120
- })
121
- .finally(() => {
122
- if (sharedCatalogPromise === request) {
123
- sharedCatalogPromise = null;
124
- sharedCatalogPromiseKey = null;
125
- sharedCatalogPromiseForced = false;
126
- }
127
- });
146
+ }
147
+ const providers = [...registry.getAllProviders()];
148
+ // Every missing provider is still inside its backoff: the partial answer is
149
+ // the answer, without asking anyone.
150
+ if (providers.length > 0 && providers.every(([name]) => kept.has(name))) {
151
+ return providers.map(([name]) => kept.get(name));
152
+ }
153
+ // A pending list from an older catalog state (revision or account epoch) is
154
+ // not joined: its answer may describe the previous catalog.
155
+ const entries = await Promise.all(
156
+ providers.map(([name, provider]) => kept.get(name) || providerEntry(name, provider, timeoutMs, key))
157
+ );
158
+ if (sharedCatalogPromise === request && catalogKey(registry) === key) {
159
+ const complete = entries.every(answered);
160
+ const partial = !complete && entries.some(answered);
161
+ sharedCatalogKey = complete ? key : null;
162
+ sharedCatalogEntries = complete ? entries : null;
163
+ sharedPartialKey = partial ? key : null;
164
+ sharedPartialEntries = partial ? entries : null;
165
+ }
166
+ return entries;
167
+ }).finally(() => {
168
+ if (sharedCatalogPromise === request) {
169
+ sharedCatalogPromise = null;
170
+ sharedCatalogPromiseKey = null;
171
+ sharedCatalogPromiseForced = false;
172
+ }
173
+ });
128
174
  sharedCatalogPromise = request;
129
175
  return await request;
130
176
  }