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
@@ -20,7 +20,7 @@ import { cwdWithinProjects } from '../../../shared/extension-scopes.mjs';
20
20
  import { resolvePluginData } from '../../../shared/plugin-paths.mjs';
21
21
  import { mixyIdentityDir, userDefinitionDir } from '../../../shared/agent-store.mjs';
22
22
  import { isSelfMcpToolName, parseMcpToolName } from '../../../shared/tool-primitives.mjs';
23
- import { connectorToolDefault } from '../../../shared/connector-catalog.mjs';
23
+ import { connectorById, connectorToolDefault } from '../../../shared/connector-catalog.mjs';
24
24
  import { MIXY_AGENT_ID } from '../session/agent-home.mjs';
25
25
  import { AGENT_MODE, sessionModeOf } from '../session/agent-mode.mjs';
26
26
  import { serverConnector } from './server-connectors.mjs';
@@ -214,12 +214,43 @@ export function connectorToolPermission(policy, toolOrName) {
214
214
  const mcp = parseMcpToolName(toolOrName);
215
215
  if (!mcp || isSelfMcpToolName(toolOrName)) return 'auto';
216
216
  if (policy && !connectorEnabled(policy, mcp.server)) return 'disabled';
217
- // An explicit per-tool entry decides; otherwise a catalog connector's
218
- // high-risk tools ask (connectorToolDefault) and any other tool is auto.
219
- return policy?.tools?.[mcp.server]?.[mcp.tool] || connectorToolDefault(serverConnector(mcp.server), mcp.tool);
217
+ // An explicit per-tool entry of the tool's connector decides; otherwise a
218
+ // catalog connector's high-risk tools ask (connectorToolDefault) and any
219
+ // other tool is auto.
220
+ const [own] = connectorToolEntries(policy?.tools, connectorGroupId(mcp.server), mcp.tool);
221
+ return own || connectorToolDefault(serverConnector(mcp.server), mcp.tool);
220
222
  }
221
223
 
222
224
  export const PERMISSION_RANK = Object.freeze({ disabled: 0, needs_approval: 1, auto: 2 });
225
+
226
+ /** The connector a server (account) belongs to: `gmail-2` → `gmail` for a
227
+ * catalog connector; any other id is its own. A tool's permission is stored
228
+ * once, under this id, and governs every account. */
229
+ export function connectorGroupId(serverId) {
230
+ const base = String(serverId).replace(/-\d+$/, '');
231
+ return base !== serverId && connectorById(base) ? base : serverId;
232
+ }
233
+
234
+ const accountNumber = (id) => Number(/-(\d+)$/.exec(id)?.[1] ?? 0);
235
+
236
+ /** The entries for `tool` under the connector `group` or any of its accounts,
237
+ * topmost first: the connector's own, then the lowest-numbered account. An
238
+ * account only holds one in data saved before permissions were kept per
239
+ * connector; the first entry is the one that applies. */
240
+ export function connectorToolEntries(tools, group, tool) {
241
+ return Object.entries(tools || {})
242
+ .filter(([id, own]) => connectorGroupId(id) === group && CONNECTOR_PERMISSIONS.includes(own?.[tool]))
243
+ .sort(([a], [b]) => accountNumber(a) - accountNumber(b))
244
+ .map(([, own]) => own[tool]);
245
+ }
246
+
247
+ const UNLISTED_ACCOUNT = '\0unlisted account';
248
+
249
+ /** Rank of `tool` on one account of connector `group`: 0 when that account is
250
+ * off, else the topmost entry for the connector, else `unset` (no override:
251
+ * at least asks / at most runs freely). */
252
+ const accountRank = (tools, off, account, group, tool, unset) =>
253
+ off.has(account) ? 0 : (PERMISSION_RANK[connectorToolEntries(tools, group, tool)[0]] ?? unset);
223
254
  export const NO_WIDEN_MESSAGE = 'This change would widen what the bot may do without asking; make it on the desktop.';
224
255
 
225
256
  /** Whether replacing the stored policy `before` (a stored value, a parsed
@@ -243,21 +274,100 @@ function connectorPolicyWidens(before, after) {
243
274
  for (const server of offBefore) if (!offAfter.has(server)) return true;
244
275
  const toolsBefore = old.tools || {};
245
276
  const toolsAfter = next.tools || {};
246
- for (const server of new Set([...Object.keys(toolsBefore), ...Object.keys(toolsAfter)])) {
247
- const tools = new Set([...Object.keys(toolsBefore[server] || {}), ...Object.keys(toolsAfter[server] || {})]);
248
- for (const tool of tools) {
249
- // No override: at least asks / at most runs freely.
250
- const was = offBefore.has(server) ? 0 : (PERMISSION_RANK[toolsBefore[server]?.[tool]] ?? 1);
251
- const now = offAfter.has(server) ? 0 : (PERMISSION_RANK[toolsAfter[server]?.[tool]] ?? 2);
277
+ // Judged per connector and tool: the accounts of one connector share them.
278
+ const pairs = new Set(
279
+ [toolsBefore, toolsAfter].flatMap((tools) =>
280
+ Object.entries(tools).flatMap(([id, own]) => Object.keys(own || {}).map((tool) => `${connectorGroupId(id)}\0${tool}`))
281
+ )
282
+ );
283
+ for (const pair of pairs) {
284
+ const [group, tool] = pair.split('\0');
285
+ // Each account on its own: one that is off does not hold back its siblings.
286
+ // Plus one account the policy never names: it is on in both policies, so it
287
+ // stands for every installed account that is not listed.
288
+ const accounts = new Set([
289
+ UNLISTED_ACCOUNT,
290
+ ...[group, ...offBefore, ...offAfter, ...Object.keys(toolsBefore), ...Object.keys(toolsAfter)].filter(
291
+ (id) => connectorGroupId(id) === group
292
+ ),
293
+ ]);
294
+ for (const account of accounts) {
295
+ const was = accountRank(toolsBefore, offBefore, account, group, tool, 1);
296
+ const now = accountRank(toolsAfter, offAfter, account, group, tool, 2);
252
297
  if (now > was) return true;
253
298
  }
254
299
  }
255
300
  return false;
256
301
  }
257
302
 
258
- /** Throws NO_WIDEN_MESSAGE when `after` could widen `before` (see connectorPolicyWidens). */
303
+ /** The `code` of the error a refused widening throws. */
304
+ export const CONNECTOR_POLICY_WIDENS = 'CONNECTOR_POLICY_WIDENS';
305
+ /** An approved tool change met a restriction newer than its request. */
306
+ export const CONNECTOR_TOOL_NARROWED = 'CONNECTOR_TOOL_NARROWED';
307
+ /** An approved tool change found the tool already at that level or more. */
308
+ export const CONNECTOR_TOOL_ALREADY = 'CONNECTOR_TOOL_ALREADY';
309
+
310
+ const codedError = (code, message) => Object.assign(new Error(message), { code });
311
+
312
+ // The one change a person approved on a card (setup-tool connector-widen-approval.mjs),
313
+ // set only for the synchronous stretch of its write.
314
+ let approvedToolChange = null;
315
+
316
+ /** Run `write` (a writer whose noWiden check runs synchronously, before it
317
+ * writes) with one approved change `{ server, tool, from, to }`: its check
318
+ * then lets exactly that tool go to `to`, judged on the stored policy it
319
+ * writes over, and nothing else widen. Returns what `write` returns. */
320
+ export function withApprovedConnectorToolChange(change, write) {
321
+ approvedToolChange = change;
322
+ try {
323
+ return write();
324
+ } finally {
325
+ approvedToolChange = null;
326
+ }
327
+ }
328
+
329
+ // The approved tool's entries (the connector's, and any account's) pinned to
330
+ // one value in both policies, so the comparison judges every other tool and switch.
331
+ const pinTool = (policy, group, tool) => {
332
+ const tools = Object.fromEntries(
333
+ Object.entries(policy?.tools || {}).map(([id, own]) => [
334
+ id,
335
+ connectorGroupId(id) === group ? { ...own, [tool]: 'disabled' } : own,
336
+ ])
337
+ );
338
+ tools[group] = { ...tools[group], [tool]: 'disabled' };
339
+ return { off: policy?.off || [], tools };
340
+ };
341
+
342
+ function assertApprovedToolChange(before, after, { server, tool, from, to }) {
343
+ let old;
344
+ try {
345
+ old = before == null ? { off: [], tools: {} } : parseConnectorPolicy(before);
346
+ } catch {
347
+ throw codedError(CONNECTOR_POLICY_WIDENS, NO_WIDEN_MESSAGE);
348
+ }
349
+ // The approved change is the connector's: its one entry goes to `to`.
350
+ const group = connectorGroupId(server);
351
+ if (old.allow || after?.tools?.[group]?.[tool] !== to) throw codedError(CONNECTOR_POLICY_WIDENS, NO_WIDEN_MESSAGE);
352
+ if (connectorPolicyWidens(pinTool(old, group, tool), pinTool(after, group, tool))) {
353
+ throw codedError(CONNECTOR_POLICY_WIDENS, NO_WIDEN_MESSAGE);
354
+ }
355
+ // No override: it asks at least (as connectorPolicyWidens reads it).
356
+ const was = accountRank(old.tools, new Set(old.off), server, group, tool, 1);
357
+ if (was < PERMISSION_RANK[from]) {
358
+ throw codedError(CONNECTOR_TOOL_NARROWED, 'A newer restriction on this tool landed meanwhile; it was kept.');
359
+ }
360
+ if (was >= PERMISSION_RANK[to]) {
361
+ throw codedError(CONNECTOR_TOOL_ALREADY, 'The tool already has this permission or more; nothing changed.');
362
+ }
363
+ }
364
+
365
+ /** Throws NO_WIDEN_MESSAGE (code CONNECTOR_POLICY_WIDENS) when `after` could
366
+ * widen `before` (see connectorPolicyWidens); inside
367
+ * withApprovedConnectorToolChange, only its approved change may. */
259
368
  export function assertConnectorPolicyNotWidened(before, after) {
260
- if (connectorPolicyWidens(before, after)) throw new Error(NO_WIDEN_MESSAGE);
369
+ if (approvedToolChange) return assertApprovedToolChange(before, after, approvedToolChange);
370
+ if (connectorPolicyWidens(before, after)) throw codedError(CONNECTOR_POLICY_WIDENS, NO_WIDEN_MESSAGE);
261
371
  }
262
372
 
263
373
  let toolPermissionWriter = null;
@@ -69,8 +69,10 @@ function run(cmd, args, { strict = false, timeoutMs = ENUMERATION_DEADLINE_MS }
69
69
  }
70
70
 
71
71
  // Snapshot the whole process table once: parent map + a per-pid identity
72
- // token (Windows CreationDate, POSIX lstart) so a later kill can prove a pid
73
- // number was not recycled onto a different process before signalling it.
72
+ // token (Windows CreationDate as a UTC FILETIME, POSIX lstart) so a later kill
73
+ // can prove a pid number was not recycled onto a different process before
74
+ // signalling it. The FILETIME form orders against the spawn server's root
75
+ // creation time and carries no local-time offset.
74
76
  // POSIX rows also carry the process group id (Windows has none).
75
77
  async function enumerate({ strict = false } = {}) {
76
78
  const childrenOf = new Map();
@@ -84,7 +86,7 @@ async function enumerate({ strict = false } = {}) {
84
86
  '-NoProfile',
85
87
  '-NonInteractive',
86
88
  '-Command',
87
- `${strict ? "$ErrorActionPreference = 'Stop'; " : ''}Get-CimInstance Win32_Process | ForEach-Object { $created = if ($_.CreationDate) { $_.CreationDate.ToString('o') } else { '' }; "$($_.ProcessId) $($_.ParentProcessId) $($_.Name) $created" }`,
89
+ `${strict ? "$ErrorActionPreference = 'Stop'; " : ''}Get-CimInstance Win32_Process | ForEach-Object { $created = if ($_.CreationDate) { $_.CreationDate.ToFileTimeUtc() } else { '' }; "$($_.ProcessId) $($_.ParentProcessId) $($_.Name) $created" }`,
88
90
  ],
89
91
  { strict, timeoutMs: ENUMERATION_DEADLINE_MS }
90
92
  )
@@ -307,14 +307,25 @@ export function resolveMcpTransportKind(cfg) {
307
307
  throw new Error(`Invalid config: need autoDetect, type (stdio/http/sse/ws), url (http), or command (stdio)`);
308
308
  }
309
309
 
310
- /** The exact URL OAuth credentials of an HTTP/SSE server config are stored
311
- * under; null for other transports. */
310
+ /** The exact URL an HTTP/SSE server config signs in at with OAuth; null for
311
+ * other transports. */
312
312
  export function resolveMcpOAuthUrl(cfg) {
313
313
  const kind = resolveMcpTransportKind(cfg);
314
314
  if (kind !== 'http' && kind !== 'sse') return null;
315
315
  return normalizeMcpTransportUrl(expandEnvVars(String(cfg.url ?? '')), kind);
316
316
  }
317
317
 
318
+ /** What the OAuth credentials of an HTTP/SSE server config are stored under:
319
+ * its exact URL, or a connector account's own `oauthKey` (another account
320
+ * of the connector signs in at the same URL), whatever the URL. Null for
321
+ * other transports. */
322
+ export function resolveMcpOAuthCredentialKey(cfg) {
323
+ const url = resolveMcpOAuthUrl(cfg);
324
+ if (!url) return null;
325
+ const own = typeof cfg.oauthKey === 'string' ? cfg.oauthKey.trim() : '';
326
+ return own ? `mcp-account:${own}` : url;
327
+ }
328
+
318
329
  // MCP per-tool-call timeout. Default 2min: a hung/unresponsive MCP server
319
330
  // (e.g. a busy editor) must not stall a tool call indefinitely. Genuinely
320
331
  // long-running tools can raise/disable it via MIXDOG_MCP_CALL_TIMEOUT_MS or a
@@ -8,24 +8,30 @@ import { createMcpOAuth } from './oauth.mjs';
8
8
  import { mcpOAuthClientFor, mcpOAuthDisplayName } from './oauth-client.mjs';
9
9
  import { googleMcpService, withGoogleMcp } from '../../../connectors/google-mcp.mjs';
10
10
  import { gmailAppPasswordService, withGmailAppPasswordMcp } from '../../../connectors/gmail-app-password.mjs';
11
- import { connectorAccountRequest, withConnectorAccountsMcp } from '../../../connectors/connector-accounts-mcp.mjs';
11
+ import { withConnectorAccountsMcp } from '../../../connectors/connector-accounts-mcp.mjs';
12
12
  import {
13
13
  connectorAccountsOf,
14
14
  connectorLiveAccountServers,
15
15
  gmailAppPasswordAccount,
16
16
  } from '../../../shared/connector-catalog.mjs';
17
- import { isMcpOAuthReauthenticationRequired } from './oauth.mjs';
17
+ import { runAbortable } from '../../../shared/abort-race.mjs';
18
+ import { isMcpOAuthReauthenticationRequired, storedMcpOAuthIdentity } from './oauth.mjs';
19
+ import { shutdownStdioChild } from './child-tree.mjs';
20
+ import { listAllMcpTools } from './client-handshake.mjs';
18
21
  import {
19
22
  AUTO_DETECT_PORTS,
23
+ MCP_MAX_TIMER_MS,
20
24
  expandEnvVars,
21
25
  findMissingMcpEnvVars,
22
26
  mcpUrlForLog,
23
27
  normalizeMcpTransportUrl,
24
28
  resolveMcpHttpHeaders,
29
+ resolveMcpOAuthCredentialKey,
25
30
  resolveMcpOAuthUrl,
26
31
  resolveMcpStdioEnvironment,
27
32
  resolveMcpTransportKind,
28
33
  scrubMcpConnectionMessage,
34
+ scrubMcpSecrets,
29
35
  } from './client-config.mjs';
30
36
 
31
37
  /**
@@ -76,57 +82,142 @@ export function hasConfiguredMcpAuth(cfg) {
76
82
  );
77
83
  }
78
84
 
79
- /** One account of a multi-account connector server (connector-catalog.mjs
80
- * connectorAccountServer) as connector-accounts-mcp.mjs calls it: through
81
- * the transport the account had as a server of its own — its app-password
82
- * header, or its own non-interactive OAuth sign-in (refreshed as needed; a
83
- * new sign-in goes through 계정 바꾸기, never through a tool call). */
84
- function connectorAccountClient(serverName, accountCfg, oauthOptions) {
85
- const base = { name: accountCfg.name, email: '', appPassword: false, close() {} };
86
- let url;
85
+ // A stdio account (Postgres) runs one process per account config, shared by
86
+ // the connector server of every scope like any stdio server (client.mjs):
87
+ // account config → its connection, which closes with its last holder.
88
+ const sharedAccountConnections = new Map();
89
+
90
+ /** Close one account connection: a stdio child with its whole process tree. */
91
+ async function closeAccountConnection(connection) {
92
+ connection.dead = true;
93
+ if (connection.transport?._process) await shutdownStdioChild(connection.transport).catch(() => {});
94
+ await connection.client.close().catch(() => {});
95
+ }
96
+
97
+ /** Whether a call failure is the account server's own JSON-RPC answer: its
98
+ * connection stays. */
99
+ const answeredError = (error) => error?.name === 'McpError' && error.code !== -32000;
100
+
101
+ /** The address a remote account is signed in as, '' when none is known. */
102
+ function signedInAs(accountCfg) {
87
103
  try {
88
- url = resolveMcpOAuthUrl(accountCfg);
89
- if (hasConfiguredMcpAuth(accountCfg)) {
90
- const email = gmailAppPasswordAccount(accountCfg);
91
- const fetchImpl = gmailAppPasswordService(url) ? withGmailAppPasswordMcp() : fetch;
92
- return {
93
- ...base,
94
- email,
95
- appPassword: Boolean(email),
96
- request: connectorAccountRequest(url, fetchImpl, resolveMcpHttpHeaders(accountCfg)),
97
- };
104
+ const url = resolveMcpOAuthUrl(accountCfg);
105
+ if (!url || hasConfiguredMcpAuth(accountCfg)) return '';
106
+ return storedMcpOAuthIdentity(url, resolveMcpOAuthCredentialKey(accountCfg))?.account || '';
107
+ } catch {
108
+ return '';
109
+ }
110
+ }
111
+
112
+ /** One account of a connector server (connector-catalog.mjs
113
+ * connectorAccountServer) as connector-accounts-mcp.mjs calls it: its own
114
+ * MCP connection through the transport it had as a server of its own — its
115
+ * key header, its app password, its local command, or its own
116
+ * non-interactive OAuth sign-in (refreshed as needed; a new sign-in goes
117
+ * through 계정 바꾸기, never through a tool call). Connected on first use and
118
+ * again after its connection failed. */
119
+ function connectorAccountClient(serverName, accountCfg, { sdk, log, oauthOptions }) {
120
+ const { interactive: _interactive, ...ownOAuth } = oauthOptions || {};
121
+ const address = gmailAppPasswordAccount(accountCfg);
122
+ const shareKey = resolveMcpTransportKind(accountCfg) === 'stdio' ? JSON.stringify(accountCfg) : null;
123
+ const secretsOf = (connection) => connection?.transport?.oauth?.secrets() || [];
124
+ const accountError = (error, connection) => {
125
+ if (isMcpOAuthReauthenticationRequired(error)) {
126
+ return Object.assign(new Error('Sign-in required; sign this account in again in Connectors.'), {
127
+ authRequired: true,
128
+ });
98
129
  }
99
- const oauth = createMcpOAuth({
100
- url,
101
- serverName: mcpOAuthDisplayName(accountCfg, serverName),
102
- oauthClient: mcpOAuthClientFor(accountCfg, url),
103
- ...(oauthOptions?.fetchImpl ? { fetchImpl: oauthOptions.fetchImpl } : {}),
104
- ...(googleMcpService(url) ? { fetchImpl: withGoogleMcp(oauthOptions?.fetchImpl) } : {}),
105
- });
106
- const send = connectorAccountRequest(url, (input, init) => oauth.fetch(input, init));
107
- return {
108
- ...base,
109
- async request(message, options) {
110
- try {
111
- return await send(message, options);
112
- } catch (error) {
113
- const reason = scrubMcpConnectionMessage(error?.message || String(error), accountCfg);
114
- const scrubbed = new Error(
115
- isMcpOAuthReauthenticationRequired(error)
116
- ? 'Sign-in required; sign this account in again in Connectors.'
117
- : reason
118
- );
119
- if (isMcpOAuthReauthenticationRequired(error)) scrubbed.authRequired = true;
120
- throw scrubbed;
121
- }
122
- },
123
- close: () => oauth.close(),
130
+ const reason = scrubMcpConnectionMessage(error?.message || String(error), accountCfg, secretsOf(connection));
131
+ return new Error(appendMcpStderrTail(reason, connection?.stderrTail?.() || ''));
132
+ };
133
+ const open = () => {
134
+ const connection = { users: 0, dead: false };
135
+ try {
136
+ const made = createMcpTransport({ name: accountCfg.name, cfg: accountCfg, sdk, log, oauthOptions: ownOAuth });
137
+ connection.transport = made.transport;
138
+ connection.stderrTail = made.stderrTail;
139
+ } catch (error) {
140
+ // A broken account (no sign-in client, an invalid address) stays listed with why.
141
+ connection.dead = true;
142
+ connection.client = { close: async () => {} };
143
+ connection.ready = Promise.reject(error);
144
+ connection.ready.catch(() => {});
145
+ return connection;
146
+ }
147
+ const client = new sdk.Client({ name: `mixdog-agent/${serverName}`, version: '1.0.0' });
148
+ connection.client = client;
149
+ const retire = () => {
150
+ connection.dead = true;
151
+ if (shareKey && sharedAccountConnections.get(shareKey) === connection) sharedAccountConnections.delete(shareKey);
124
152
  };
125
- } catch (error) {
126
- // A broken account (no sign-in client, an invalid address) stays listed with why.
127
- const failed = new Error(error?.message || String(error));
128
- return { ...base, request: async () => Promise.reject(failed) };
129
- }
153
+ client.onclose = retire;
154
+ connection.retire = retire;
155
+ connection.ready = (async () => {
156
+ await client.connect(connection.transport, { timeout: MCP_MAX_TIMER_MS });
157
+ const tools = client.getServerCapabilities()?.tools
158
+ ? (await listAllMcpTools(client, { timeout: MCP_MAX_TIMER_MS }))?.tools
159
+ : [];
160
+ if (!Array.isArray(tools)) throw new Error('The account listed its tools in an invalid shape');
161
+ return { instructions: client.getInstructions() || '', tools };
162
+ })();
163
+ connection.ready.catch(() => {
164
+ retire();
165
+ void closeAccountConnection(connection);
166
+ });
167
+ return connection;
168
+ };
169
+ let held = null;
170
+ const release = () => {
171
+ const connection = held;
172
+ held = null;
173
+ if (!connection || --connection.users > 0) return;
174
+ connection.retire?.();
175
+ void closeAccountConnection(connection);
176
+ };
177
+ const acquire = () => {
178
+ if (held && !held.dead) return held;
179
+ release();
180
+ let connection = shareKey ? sharedAccountConnections.get(shareKey) : null;
181
+ if (!connection || connection.dead) {
182
+ connection = open();
183
+ if (shareKey && !connection.dead) sharedAccountConnections.set(shareKey, connection);
184
+ }
185
+ connection.users += 1;
186
+ held = connection;
187
+ return connection;
188
+ };
189
+ return {
190
+ name: accountCfg.name,
191
+ email: address || signedInAs(accountCfg),
192
+ async connect(signal) {
193
+ const connection = acquire();
194
+ try {
195
+ return await runAbortable(signal, () => connection.ready);
196
+ } catch (error) {
197
+ throw accountError(error, connection);
198
+ }
199
+ },
200
+ async callTool(params, { signal, onprogress } = {}) {
201
+ const connection = acquire();
202
+ try {
203
+ await runAbortable(signal, () => connection.ready);
204
+ // The caller's deadline governs (it resets on the progress passed up).
205
+ const result = await connection.client.callTool(params, undefined, {
206
+ signal,
207
+ timeout: MCP_MAX_TIMER_MS,
208
+ ...(onprogress ? { onprogress } : {}),
209
+ });
210
+ return scrubMcpSecrets(result, secretsOf(connection));
211
+ } catch (error) {
212
+ const failed = accountError(error, connection);
213
+ if (answeredError(error)) return { content: [{ type: 'text', text: failed.message }], isError: true };
214
+ // A connection a call failed on is opened again for the next call.
215
+ if (!signal?.aborted) connection.retire?.();
216
+ throw failed;
217
+ }
218
+ },
219
+ close: release,
220
+ };
130
221
  }
131
222
 
132
223
  // The SDK's WebSocket transport opens its socket without a way to set request
@@ -177,13 +268,15 @@ function missingEnvWarnings(name, cfg, strictEnv, log) {
177
268
  return warnings;
178
269
  }
179
270
 
180
- // A multi-account connector: one server, every account behind it; closing the
181
- // transport closes every account client.
182
- function connectorAccountsTransport({ name, cfg, Transport, url, accountServers, oauthOptions, log }) {
183
- const accounts = accountServers.map((accountCfg) => connectorAccountClient(name, accountCfg, oauthOptions));
184
- const transport = new Transport(new URL(url), {
185
- fetch: withConnectorAccountsMcp({ connector: cfg.connector, accounts }),
186
- });
271
+ // A connector server: one server, every account behind it (its account choice
272
+ // on `connectorAccounts`); closing the transport closes every account client.
273
+ function connectorAccountsTransport({ name, cfg, Transport, url, accountServers, sdk, oauthOptions, log }) {
274
+ const accounts = accountServers.map((accountCfg) =>
275
+ connectorAccountClient(name, accountCfg, { sdk, log, oauthOptions })
276
+ );
277
+ const fetchImpl = withConnectorAccountsMcp({ connector: cfg.connector, accounts });
278
+ const transport = new Transport(new URL(url), { fetch: fetchImpl });
279
+ transport.connectorAccounts = fetchImpl.connectorAccounts;
187
280
  const close = transport.close.bind(transport);
188
281
  transport.close = async () => {
189
282
  for (const account of accounts) account.close();
@@ -204,6 +297,7 @@ function remoteHttpTransport({ name, cfg, kind, Transport, url, oauthOptions, lo
204
297
  if (!hasConfiguredMcpAuth(cfg)) {
205
298
  oauth = createMcpOAuth({
206
299
  url,
300
+ credentialKey: resolveMcpOAuthCredentialKey(cfg),
207
301
  serverName: mcpOAuthDisplayName(cfg, name),
208
302
  oauthClient: mcpOAuthClientFor(cfg, url),
209
303
  ...oauthOptions,
@@ -291,7 +385,7 @@ export function createMcpTransport({ name, cfg, sdk, log, strictEnv, oauthOption
291
385
  throw new Error(`Every account of "${name}" is disabled`);
292
386
  }
293
387
  const transport = accountServers.length
294
- ? connectorAccountsTransport({ name, cfg, Transport, url, accountServers, oauthOptions, log })
388
+ ? connectorAccountsTransport({ name, cfg, Transport, url, accountServers, sdk, oauthOptions, log })
295
389
  : remoteHttpTransport({ name, cfg, kind, Transport, url, oauthOptions, log });
296
390
  return { transport, autoDetectAdvert: null, stderrTail: noStderrTail, warnings };
297
391
  }
@@ -25,6 +25,7 @@ import { currentToolExecutionOwner } from '../../../shared/tool-execution-owner.
25
25
  import { positiveInt } from '../../../shared/numbers.mjs';
26
26
  import {
27
27
  providerSafeMcpToolName,
28
+ resolveMcpOAuthCredentialKey,
28
29
  resolveMcpOAuthUrl,
29
30
  resolveMcpRequestTimeouts,
30
31
  resolveMcpTransportKind,
@@ -39,6 +40,7 @@ import { mcpOAuthClientFor, mcpOAuthDisplayName } from './oauth-client.mjs';
39
40
  import { googleMcpService, withGoogleMcp } from '../../../connectors/google-mcp.mjs';
40
41
  import { gmailAppPasswordService } from '../../../connectors/gmail-app-password.mjs';
41
42
  import { connectorLiveAccountServers } from '../../../shared/connector-catalog.mjs';
43
+ import { CONVERSATION_META } from '../../../connectors/connector-accounts-mcp.mjs';
42
44
  import {
43
45
  clearMcpOAuth,
44
46
  commitMcpOAuthSwitch,
@@ -503,10 +505,15 @@ export async function executeMcpTool(name, args, options = {}) {
503
505
  const serverName = server.name;
504
506
  const toolName = definition?.mcpToolName || match[2];
505
507
  const callSignal = options?.signal || null;
508
+ // A connector server's call names the conversation it is made in: its
509
+ // account default, and the account it makes the last used.
510
+ const conversation = String(options?.conversation || '').trim();
511
+ const metaFor = (target) =>
512
+ conversation && target.transport?.connectorAccounts ? { [CONVERSATION_META]: conversation } : undefined;
506
513
  const dispatch = (target) =>
507
514
  definition?.mcpOperation
508
515
  ? _callMcpFeatureWithTimeout(target, definition.mcpOperation, args, callSignal)
509
- : _callToolWithTimeout(target, toolName, args, callSignal);
516
+ : _callToolWithTimeout(target, toolName, args, callSignal, metaFor(target));
510
517
  let retryServer = null;
511
518
  const secrets = () => oauthSecrets(server.transport, retryServer?.transport);
512
519
  const gate = callAdmissionFor(server);
@@ -999,7 +1006,7 @@ export function raceMcpAbort(promise, signal, message) {
999
1006
  });
1000
1007
  }
1001
1008
 
1002
- async function _callToolWithTimeout(server, toolName, args, signal = null) {
1009
+ async function _callToolWithTimeout(server, toolName, args, signal = null, meta = undefined) {
1003
1010
  // The request's signal goes INTO the SDK: an aborted caller (or our own
1004
1011
  // timeout) cancels the live JSON-RPC request (notifications/cancelled)
1005
1012
  // instead of merely walking away from it. The abort race additionally frees
@@ -1008,7 +1015,8 @@ async function _callToolWithTimeout(server, toolName, args, signal = null) {
1008
1015
  return runMcpRequest(
1009
1016
  server,
1010
1017
  signal,
1011
- (options) => server.client.callTool({ name: toolName, arguments: args }, undefined, options),
1018
+ (options) =>
1019
+ server.client.callTool({ name: toolName, arguments: args, ...(meta ? { _meta: meta } : {}) }, undefined, options),
1012
1020
  {
1013
1021
  abortMessage: `MCP tool call aborted (server="${server?.name}", tool="${toolName}")`,
1014
1022
  timeoutMessage: (ms) =>
@@ -1119,6 +1127,24 @@ export function isMcpTool(name) {
1119
1127
  export function isRegisteredMcpTool(name, scopeId = DEFAULT_MCP_SCOPE_ID) {
1120
1128
  return Boolean(resolveMcpToolTarget(scopeId, name)?.definition);
1121
1129
  }
1130
+ /**
1131
+ * The account (`{ name, label }`: its unique name, which binds a call to it,
1132
+ * and what it is shown as) a call of the prefixed tool `name` with `args`
1133
+ * would use on its connector server, made in `conversation`
1134
+ * (connectors/connector-accounts-mcp.mjs): the one `args.account` names, else
1135
+ * the one used last. Null when the tool's server holds no accounts. Throws the
1136
+ * refusal (usable accounts listed) when that account cannot be used.
1137
+ */
1138
+ export function resolveMcpToolAccount(name, args, { scopeId = DEFAULT_MCP_SCOPE_ID, conversation = '' } = {}) {
1139
+ const target = resolveMcpToolTarget(normalizeMcpScopeId(scopeId), name);
1140
+ const accounts = target?.server?.transport?.connectorAccounts;
1141
+ if (!accounts || target.definition?.mcpOperation) return null;
1142
+ return accounts.choose({
1143
+ tool: target.definition?.mcpToolName || MCP_TOOL_NAME_RE.exec(name)?.[2],
1144
+ wanted: args?.account,
1145
+ conversation: String(conversation || '').trim(),
1146
+ });
1147
+ }
1122
1148
  /** True when the prefixed name is `read_email` of a server whose every
1123
1149
  * endpoint Mixdog answers in-process (the Gmail app-password connector,
1124
1150
  * alone or behind the multi-account server): its result is a mail as the
@@ -1272,18 +1298,19 @@ export function isMcpLoginPending(name, options = {}) {
1272
1298
  * for servers that never use MCP OAuth.
1273
1299
  */
1274
1300
  export function getMcpServerOAuthState(cfg) {
1275
- let url;
1301
+ let key;
1276
1302
  try {
1277
- url = resolveMcpOAuthUrl(cfg);
1303
+ key = resolveMcpOAuthCredentialKey(cfg);
1278
1304
  } catch {
1279
1305
  return null;
1280
1306
  }
1281
- if (!url || hasConfiguredMcpAuth(cfg)) return null;
1282
- const signedIn = hasStoredMcpOAuthTokens(url);
1307
+ if (!key || hasConfiguredMcpAuth(cfg)) return null;
1308
+ const url = resolveMcpOAuthUrl(cfg);
1309
+ const signedIn = hasStoredMcpOAuthTokens(url, key);
1283
1310
  if (!signedIn) return { signedIn };
1284
1311
  // Who it is signed in as (never guessed: empty when the server named nobody)
1285
1312
  // and whether 계정 바꾸기 can make the provider show its account choice.
1286
- const who = storedMcpOAuthIdentity(url);
1313
+ const who = storedMcpOAuthIdentity(url, key);
1287
1314
  return {
1288
1315
  signedIn,
1289
1316
  account: typeof who?.account === 'string' ? who.account : '',
@@ -1298,32 +1325,38 @@ export function startMcpServerOAuthSwitch(cfg, launch, serverName) {
1298
1325
  if (!url) throw new Error(`MCP server "${serverName}" does not use OAuth sign-in`);
1299
1326
  return startMcpOAuthSwitch({
1300
1327
  url,
1328
+ credentialKey: resolveMcpOAuthCredentialKey(cfg),
1301
1329
  launch,
1302
1330
  serverName: mcpOAuthDisplayName(cfg, serverName),
1303
1331
  oauthClient: mcpOAuthClientFor(cfg, url),
1304
1332
  ...(googleMcpService(url) ? { fetchImpl: withGoogleMcp() } : {}),
1305
1333
  });
1306
1334
  }
1307
- /** Store a switch's sign-in for the server's URL; resolves its rollback. */
1335
+ /** Store a switch's sign-in as the server's (its URL's, or its own key's);
1336
+ * resolves its rollback. */
1308
1337
  export function commitMcpServerOAuthSwitch(cfg, signIn) {
1309
- return commitMcpOAuthSwitch({ url: resolveMcpOAuthUrl(cfg), signIn });
1338
+ return commitMcpOAuthSwitch({
1339
+ url: resolveMcpOAuthUrl(cfg),
1340
+ credentialKey: resolveMcpOAuthCredentialKey(cfg),
1341
+ signIn,
1342
+ });
1310
1343
  }
1311
1344
  /**
1312
- * Log out of an HTTP/SSE server URL: delete the OAuth tokens and client
1313
- * registration stored for it, shared by every server configured with that
1314
- * exact URL, and drop them from live connections. `options.revoke` also
1315
- * revokes the grant where the authorization server documents how; resolves
1316
- * `{ revoked }`.
1345
+ * Log out of an HTTP/SSE server: delete the OAuth tokens and client
1346
+ * registration stored for it (shared by every server configured with that
1347
+ * exact URL, unless it has its own credential key), and drop them from live
1348
+ * connections. `options.revoke` also revokes the grant where the
1349
+ * authorization server documents how; resolves `{ revoked }`.
1317
1350
  */
1318
1351
  export async function clearMcpServerOAuth(cfg, options = {}) {
1319
- let url;
1352
+ let key;
1320
1353
  try {
1321
- url = resolveMcpOAuthUrl(cfg);
1354
+ key = resolveMcpOAuthCredentialKey(cfg);
1322
1355
  } catch {
1323
1356
  // A config without a valid remote URL never connected, so never logged in.
1324
1357
  return { revoked: false };
1325
1358
  }
1326
- if (url) return clearMcpOAuth({ url, revoke: options.revoke === true });
1359
+ if (key) return clearMcpOAuth({ url: resolveMcpOAuthUrl(cfg), credentialKey: key, revoke: options.revoke === true });
1327
1360
  return { revoked: false };
1328
1361
  }
1329
1362
  /**