mixdog 1.2.5 → 1.3.0

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/LICENSES/simple-icons-CC0-1.0.txt +30 -0
  2. package/NOTICE.md +19 -0
  3. package/package.json +4 -1
  4. package/scripts/check-model-recommendations.mjs +89 -0
  5. package/scripts/gate-local.mjs +7 -0
  6. package/scripts/lib/gate-scheduler.mjs +2 -2
  7. package/scripts/lib/isolated-test-env.mjs +16 -0
  8. package/scripts/lib/run-node-tests.mjs +5 -14
  9. package/scripts/provider-pingpong-live.mjs +9 -9
  10. package/src/agents/advisor/AGENT.md +4 -4
  11. package/src/agents/advisor/agent.json +1 -1
  12. package/src/agents/reviewer/AGENT.md +5 -4
  13. package/src/agents/reviewer/agent.json +1 -1
  14. package/src/defaults/shipped-history.json +17 -0
  15. package/src/defaults/skills/browser-use/SKILL.md +7 -1
  16. package/src/defaults/skills/computer-use/SKILL.md +3 -1
  17. package/src/defaults/skills/memory-management/SKILL.md +4 -1
  18. package/src/defaults/skills/setup/references/actions.md +11 -4
  19. package/src/defaults/skills/setup-guide/SKILL.md +175 -0
  20. package/src/defaults/skills/setup-guide/model-recommendations.json +333 -0
  21. package/src/defaults/skills/visualize/SKILL.md +3 -2
  22. package/src/lib/rules-builder.cjs +56 -7
  23. package/src/output-styles/common.md +22 -6
  24. package/src/output-styles/detailed.md +7 -5
  25. package/src/output-styles/minimal.md +7 -3
  26. package/src/output-styles/simple.md +2 -3
  27. package/src/rules/agent/AGENT.md +7 -1
  28. package/src/rules/agent-mode/AGENT_MODE.md +24 -0
  29. package/src/rules/agent-mode/MIXY.md +21 -0
  30. package/src/rules/agent-mode/ROUTINE.md +6 -0
  31. package/src/rules/lead/LEAD.md +9 -4
  32. package/src/rules/shared/10-tool-workflow.md +2 -1
  33. package/src/rules/shared/60-verification.md +4 -5
  34. package/src/rules/shared/70-delivery.md +3 -0
  35. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -1
  36. package/src/runtime/agent/orchestrator/agent-runtime/room-supervisor-completion.mjs +86 -0
  37. package/src/runtime/agent/orchestrator/config.mjs +6 -0
  38. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +77 -14
  39. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +182 -13
  40. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +5 -3
  41. package/src/runtime/agent/orchestrator/internal-agents.mjs +10 -0
  42. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +215 -0
  43. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +58 -64
  44. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +10 -1
  45. package/src/runtime/agent/orchestrator/mcp/client.mjs +122 -15
  46. package/src/runtime/agent/orchestrator/mcp/oauth-account.mjs +97 -0
  47. package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +2 -3
  48. package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +49 -0
  49. package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +59 -9
  50. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +32 -7
  51. package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +45 -6
  52. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +195 -6
  53. package/src/runtime/agent/orchestrator/mcp/server-connectors.mjs +17 -0
  54. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +9 -2
  55. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  56. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +41 -19
  57. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +3 -2
  58. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-start.mjs +4 -1
  59. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events.mjs +2 -0
  60. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn.mjs +3 -0
  61. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +2 -1
  62. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +3 -1
  63. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +11 -5
  64. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +4 -0
  65. package/src/runtime/agent/orchestrator/providers/catalog-deadline.mjs +163 -0
  66. package/src/runtime/agent/orchestrator/providers/catalog-failures.mjs +34 -0
  67. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  68. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +38 -14
  69. package/src/runtime/agent/orchestrator/providers/lib/oauth-page.mjs +100 -26
  70. package/src/runtime/agent/orchestrator/providers/lib/send-callbacks.mjs +24 -0
  71. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +13 -0
  72. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +2 -1
  73. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
  74. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +4 -0
  75. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +3 -1
  76. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +2 -0
  77. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +49 -4
  78. package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +0 -7
  79. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +7 -1
  80. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
  81. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +2 -0
  82. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
  83. package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +17 -12
  84. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +5 -1
  85. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +11 -2
  86. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +1 -0
  87. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +45 -16
  88. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -0
  89. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +1 -0
  90. package/src/runtime/agent/orchestrator/providers/openai-ws-connect.mjs +196 -0
  91. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +74 -9
  92. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +14 -1
  93. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +2 -0
  94. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth-login.mjs +194 -0
  95. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth-tokens.mjs +353 -0
  96. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +293 -0
  97. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +33 -100
  98. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +23 -14
  99. package/src/runtime/agent/orchestrator/providers/registry.mjs +41 -27
  100. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +98 -4
  101. package/src/runtime/agent/orchestrator/runtime-core/context-status-shape.mjs +4 -0
  102. package/src/runtime/agent/orchestrator/runtime-core/extension-scopes.mjs +6 -4
  103. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +15 -8
  104. package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +1 -0
  105. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +12 -6
  106. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +32 -5
  107. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +10 -4
  108. package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +4 -1
  109. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +50 -20
  110. package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +2 -0
  111. package/src/runtime/agent/orchestrator/session/agent-home.d.mts +4 -0
  112. package/src/runtime/agent/orchestrator/session/agent-home.mjs +33 -0
  113. package/src/runtime/agent/orchestrator/session/agent-mode.mjs +62 -0
  114. package/src/runtime/agent/orchestrator/session/agent-pool-progress.mjs +15 -3
  115. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.d.mts +31 -0
  116. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.mjs +241 -0
  117. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +162 -0
  118. package/src/runtime/agent/orchestrator/session/agent-room.mjs +641 -0
  119. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +7 -1
  120. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +74 -13
  121. package/src/runtime/agent/orchestrator/session/compact/forked-summary.mjs +102 -0
  122. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +8 -2
  123. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +16 -0
  124. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +2 -0
  125. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +6 -2
  126. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +165 -0
  127. package/src/runtime/agent/orchestrator/session/human-gate/held-message.mjs +95 -0
  128. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +221 -0
  129. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +195 -0
  130. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +167 -0
  131. package/src/runtime/agent/orchestrator/session/lifecycle-scan.mjs +9 -0
  132. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +3 -1
  133. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +39 -2
  134. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +10 -0
  135. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +16 -1
  136. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +82 -33
  137. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +11 -4
  138. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +5 -4
  139. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +13 -3
  140. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +7 -0
  141. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +1 -0
  142. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +6 -1
  143. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
  144. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +2 -0
  145. package/src/runtime/agent/orchestrator/session/manager/ask-turn-steering-drain.mjs +13 -5
  146. package/src/runtime/agent/orchestrator/session/manager/pending-lifecycle-epoch.mjs +0 -8
  147. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +10 -7
  148. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +62 -22
  149. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +44 -16
  150. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +22 -5
  151. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +23 -1
  152. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +81 -34
  153. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +39 -4
  154. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +27 -5
  155. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +55 -8
  156. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +16 -0
  157. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +3 -2
  158. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +6 -0
  159. package/src/runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs +58 -0
  160. package/src/runtime/agent/orchestrator/session/manager.mjs +2 -2
  161. package/src/runtime/agent/orchestrator/session/mixy-name.mjs +24 -0
  162. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +32 -5
  163. package/src/runtime/agent/orchestrator/session/request-fork.mjs +47 -0
  164. package/src/runtime/agent/orchestrator/session/routine-result.d.mts +70 -0
  165. package/src/runtime/agent/orchestrator/session/routine-result.mjs +277 -0
  166. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +16 -1
  167. package/src/runtime/agent/orchestrator/session/side-question.mjs +5 -43
  168. package/src/runtime/agent/orchestrator/session/store/fs-probe.mjs +10 -0
  169. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +73 -6
  170. package/src/runtime/agent/orchestrator/session/store/load-session.mjs +40 -0
  171. package/src/runtime/agent/orchestrator/session/store/save-fault.mjs +15 -0
  172. package/src/runtime/agent/orchestrator/session/store/session-paths.mjs +12 -0
  173. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +8 -0
  174. package/src/runtime/agent/orchestrator/session/store-agent-worker-header.mjs +36 -0
  175. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +258 -43
  176. package/src/runtime/agent/orchestrator/session/store-record-strict-scan.mjs +325 -0
  177. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +84 -21
  178. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +44 -12
  179. package/src/runtime/agent/orchestrator/session/store-summary-last-message.mjs +46 -0
  180. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +46 -30
  181. package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +0 -4
  182. package/src/runtime/agent/orchestrator/session/store-transcript-cache.mjs +144 -45
  183. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +32 -13
  184. package/src/runtime/agent/orchestrator/session/store-transcript-worker.mjs +364 -38
  185. package/src/runtime/agent/orchestrator/session/store.mjs +9 -2
  186. package/src/runtime/agent/orchestrator/session/task-wait-control.mjs +43 -12
  187. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +23 -6
  188. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +44 -1
  189. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +12 -6
  190. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +13 -7
  191. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/bashism-rewrite.mjs +1 -1
  192. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/benign-exit.mjs +7 -1
  193. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +40 -7
  194. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +37 -34
  195. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/inline-hoist.mjs +1 -1
  196. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +0 -4
  197. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +6 -0
  198. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +29 -27
  199. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +1 -1
  200. package/src/runtime/agent/orchestrator/tools/builtin/find-files-tool.mjs +28 -11
  201. package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.mjs +12 -3
  202. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +203 -101
  203. package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.mjs +90 -41
  204. package/src/runtime/agent/orchestrator/tools/builtin/lib/list-helpers.mjs +17 -1
  205. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-delivery.mjs +199 -0
  206. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +12 -0
  207. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-task-output.mjs +165 -39
  208. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +25 -13
  209. package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +113 -5
  210. package/src/runtime/agent/orchestrator/tools/builtin/read-batch.mjs +7 -6
  211. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +19 -4
  212. package/src/runtime/agent/orchestrator/tools/builtin/read-line-collector.mjs +5 -3
  213. package/src/runtime/agent/orchestrator/tools/builtin/read-single-render.mjs +25 -7
  214. package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +2 -1
  215. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-render.mjs +3 -1
  216. package/src/runtime/agent/orchestrator/tools/builtin/search-glob-tool.mjs +12 -5
  217. package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +61 -22
  218. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +32 -8
  219. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +193 -19
  220. package/src/runtime/agent/orchestrator/tools/builtin/shell-direct-exe.mjs +57 -7
  221. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +123 -32
  222. package/src/runtime/agent/orchestrator/tools/builtin/shell-lossless-compact.mjs +26 -1
  223. package/src/runtime/agent/orchestrator/tools/builtin/shell-output.mjs +13 -28
  224. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +90 -11
  225. package/src/runtime/agent/orchestrator/tools/builtin/wait-budgets.mjs +23 -0
  226. package/src/runtime/agent/orchestrator/tools/builtin.mjs +54 -10
  227. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +22 -28
  228. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +11 -7
  229. package/src/runtime/agent/orchestrator/tools/destructive-warning.mjs +126 -9
  230. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
  231. package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +15 -6
  232. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +64 -2
  233. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +10 -2
  234. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promotion-results.mjs +13 -3
  235. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +8 -0
  236. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +6 -1
  237. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +1 -1
  238. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +104 -55
  239. package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +7 -2
  240. package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +39 -23
  241. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +6 -2
  242. package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +13 -13
  243. package/src/runtime/attachments/office-extract.mjs +11 -9
  244. package/src/runtime/browser-bridge/client.mjs +24 -0
  245. package/src/runtime/channels/lib/scheduler.mjs +40 -12
  246. package/src/runtime/channels/lib/schedules-db.mjs +71 -12
  247. package/src/runtime/channels/lib/webhook/session-dispatch.mjs +36 -16
  248. package/src/runtime/channels/lib/webhook.mjs +0 -1
  249. package/src/runtime/channels/lib/webhooks-db.mjs +100 -10
  250. package/src/runtime/channels/lib/worker-main.mjs +17 -20
  251. package/src/runtime/computer-bridge/client.mjs +28 -1
  252. package/src/runtime/connectors/gmail-app-password.mjs +690 -0
  253. package/src/runtime/connectors/google-mcp.mjs +446 -0
  254. package/src/runtime/memory/lib/core-memory-access.mjs +69 -0
  255. package/src/runtime/memory/lib/core-memory-file.mjs +16 -8
  256. package/src/runtime/memory/lib/core-memory-index.mjs +15 -9
  257. package/src/runtime/memory/lib/core-memory-management.mjs +10 -4
  258. package/src/runtime/memory/lib/core-memory-store.mjs +53 -30
  259. package/src/runtime/memory/lib/core-memory-uniqueness.mjs +10 -6
  260. package/src/runtime/memory/lib/embedding-tokenizer.mjs +32 -18
  261. package/src/runtime/memory/lib/http-router/session-routes.mjs +3 -2
  262. package/src/runtime/memory/lib/memory-action-handlers/core-actions.mjs +44 -14
  263. package/src/runtime/memory/lib/memory-schema/core-and-meta.mjs +11 -0
  264. package/src/runtime/memory/lib/memory.mjs +7 -1
  265. package/src/runtime/memory/lib/query-core-recall.mjs +9 -1
  266. package/src/runtime/memory/lib/query-hybrid-search.mjs +2 -0
  267. package/src/runtime/memory/lib/runtime-fetcher.mjs +1 -1
  268. package/src/runtime/memory/lib/tool-call-handler.mjs +9 -5
  269. package/src/runtime/memory/tool-defs.mjs +5 -0
  270. package/src/runtime/office/authoring/pptx-script-normalize.mjs +11 -8
  271. package/src/runtime/office/core/tabular.mjs +1 -1
  272. package/src/runtime/office/design/library/design-template-inspect.mjs +7 -4
  273. package/src/runtime/office/portable/docx-formatting.mjs +1 -1
  274. package/src/runtime/office/portable/docx-runs.mjs +1 -1
  275. package/src/runtime/office/portable/docx-tracked-edits.mjs +2 -2
  276. package/src/runtime/office/portable/portable-docx-edits.mjs +11 -10
  277. package/src/runtime/office/portable/portable-docx-operations.mjs +21 -17
  278. package/src/runtime/office/portable/portable-docx-parts.mjs +15 -9
  279. package/src/runtime/office/portable/portable-docx-styles.mjs +4 -1
  280. package/src/runtime/office/portable/portable-docx-xml.mjs +9 -9
  281. package/src/runtime/office/portable/portable-opc.mjs +27 -13
  282. package/src/runtime/office/portable/portable-pivot.mjs +3 -3
  283. package/src/runtime/office/portable/portable-pptx-chart.mjs +3 -3
  284. package/src/runtime/office/portable/portable-pptx-charts.mjs +24 -18
  285. package/src/runtime/office/portable/portable-pptx-core.mjs +5 -5
  286. package/src/runtime/office/portable/portable-pptx-deck.mjs +8 -4
  287. package/src/runtime/office/portable/portable-pptx-package.mjs +7 -4
  288. package/src/runtime/office/portable/portable-pptx-shapes.mjs +18 -9
  289. package/src/runtime/office/portable/portable-sheet-parts.mjs +9 -3
  290. package/src/runtime/office/portable/portable-sheet-xml.mjs +5 -5
  291. package/src/runtime/office/portable/portable-xlsx-charts.mjs +4 -1
  292. package/src/runtime/office/portable/portable-xlsx-drawings.mjs +10 -6
  293. package/src/runtime/office/portable/portable-xlsx-operations.mjs +8 -5
  294. package/src/runtime/office/portable/portable-xlsx-pivot-ops.mjs +1 -1
  295. package/src/runtime/office/portable/portable-xlsx-reference-shift.mjs +11 -7
  296. package/src/runtime/office/portable/portable-xlsx-sheet-edits.mjs +6 -3
  297. package/src/runtime/office/portable/portable-xml.mjs +1 -1
  298. package/src/runtime/shared/agent-avatar-sprites.mjs +445 -0
  299. package/src/runtime/shared/agent-avatar.d.mts +64 -0
  300. package/src/runtime/shared/agent-avatar.mjs +320 -0
  301. package/src/runtime/shared/agent-route-config.d.mts +3 -0
  302. package/src/runtime/shared/agent-route-config.mjs +16 -0
  303. package/src/runtime/shared/atomic-file.mjs +36 -14
  304. package/src/runtime/shared/background-tasks.mjs +60 -20
  305. package/src/runtime/shared/child-spawn-gate.mjs +26 -16
  306. package/src/runtime/shared/config.mjs +8 -11
  307. package/src/runtime/shared/connector-accounts.mjs +15 -0
  308. package/src/runtime/shared/connector-catalog.d.mts +52 -0
  309. package/src/runtime/shared/connector-catalog.mjs +230 -0
  310. package/src/runtime/shared/connector-triggers.d.mts +3 -0
  311. package/src/runtime/shared/connector-triggers.mjs +43 -0
  312. package/src/runtime/shared/file-lock.mjs +7 -1
  313. package/src/runtime/shared/file-permissions-worker.mjs +38 -0
  314. package/src/runtime/shared/file-permissions.d.mts +19 -4
  315. package/src/runtime/shared/file-permissions.mjs +310 -4
  316. package/src/runtime/shared/git-runner-worker.mjs +6 -78
  317. package/src/runtime/shared/google-oauth-client.mjs +24 -0
  318. package/src/runtime/shared/held-request-host.d.mts +12 -0
  319. package/src/runtime/shared/held-request-host.mjs +71 -0
  320. package/src/runtime/shared/llm/http-agent.mjs +7 -3
  321. package/src/runtime/shared/llm/provider-catalog-registry.mjs +10 -0
  322. package/src/runtime/shared/llm/usage-accounting.mjs +2 -0
  323. package/src/runtime/shared/llm/usage-ledger-import.mjs +32 -3
  324. package/src/runtime/shared/llm/usage-ledger-rollup.mjs +12 -5
  325. package/src/runtime/shared/llm/usage-ledger-worker.mjs +1 -1
  326. package/src/runtime/shared/llm/usage-ledger.mjs +20 -5
  327. package/src/runtime/shared/memory-snapshot.mjs +5 -1
  328. package/src/runtime/shared/notify-trace.mjs +8 -1
  329. package/src/runtime/shared/plain-preview.d.mts +1 -0
  330. package/src/runtime/shared/plain-preview.mjs +28 -0
  331. package/src/runtime/shared/pristine-execution-contract.json +5 -1
  332. package/src/runtime/shared/process-runner-worker.mjs +7 -0
  333. package/src/runtime/shared/process-runner.mjs +38 -0
  334. package/src/runtime/shared/provider-accounts.mjs +2 -1
  335. package/src/runtime/shared/provider-api-key.mjs +0 -1
  336. package/src/runtime/shared/read-row-numbers.mjs +1 -1
  337. package/src/runtime/shared/resource-admission.mjs +60 -18
  338. package/src/runtime/shared/routine-limits.d.mts +11 -0
  339. package/src/runtime/shared/routine-limits.mjs +21 -0
  340. package/src/runtime/shared/routine-trigger.d.mts +10 -0
  341. package/src/runtime/shared/routine-trigger.mjs +44 -0
  342. package/src/runtime/shared/run-child.mjs +84 -0
  343. package/src/runtime/shared/schedule-model-ref.mjs +9 -10
  344. package/src/runtime/shared/session-activity-signal.mjs +23 -0
  345. package/src/runtime/shared/shell-output-summary.mjs +23 -0
  346. package/src/runtime/shared/singleton-owner.mjs +126 -42
  347. package/src/runtime/shared/slow-span.mjs +4 -12
  348. package/src/runtime/shared/statusline/statusline-segments.mjs +13 -1
  349. package/src/runtime/shared/tool-output-limit.mjs +4 -2
  350. package/src/runtime/shared/tool-output-marker.mjs +58 -0
  351. package/src/runtime/shared/transcript-metadata.mjs +10 -0
  352. package/src/runtime/shared/turn-changes.mjs +0 -8
  353. package/src/runtime/shared/usage-readout-totals.mjs +56 -0
  354. package/src/runtime/shared/user-data-guard.mjs +131 -34
  355. package/src/runtime/shared/worktrees/activity-gate.mjs +33 -8
  356. package/src/runtime/shared/worktrees/git-head.mjs +20 -2
  357. package/src/runtime/shared/worktrees/lock.mjs +3 -2
  358. package/src/runtime/terminal-bridge/tool-defs.mjs +1 -1
  359. package/src/runtime/tidy/runners/biome.mjs +3 -1
  360. package/src/runtime/tidy/shadow.mjs +2 -1
  361. package/src/runtime/web-search/index.mjs +5 -7
  362. package/src/runtime/web-search/lib/formatter.mjs +42 -6
  363. package/src/runtime/web-search/tool-defs.mjs +6 -1
  364. package/src/session-runtime/agent-home-delivery.mjs +459 -0
  365. package/src/session-runtime/agent-profile-changes.mjs +21 -0
  366. package/src/session-runtime/agent-room-delivery.mjs +92 -0
  367. package/src/session-runtime/agent-task-sessions.mjs +200 -0
  368. package/src/session-runtime/boot/apis.mjs +50 -18
  369. package/src/session-runtime/boot/facade.mjs +5 -8
  370. package/src/session-runtime/boot/infrastructure.mjs +28 -17
  371. package/src/session-runtime/boot/tools.mjs +3 -0
  372. package/src/session-runtime/channel-config-api.mjs +53 -5
  373. package/src/session-runtime/cwd-plugins/core-memory-context.mjs +36 -20
  374. package/src/session-runtime/cwd-plugins/plugin-status.mjs +6 -1
  375. package/src/session-runtime/held-requests.mjs +228 -0
  376. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +121 -0
  377. package/src/session-runtime/internal-tool-executor/feature-tools.mjs +5 -1
  378. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +31 -2
  379. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +31 -2
  380. package/src/session-runtime/internal-tool-executor.mjs +2 -0
  381. package/src/session-runtime/lifecycle/session-catalog.mjs +9 -0
  382. package/src/session-runtime/lifecycle/session-switch/context-switch.mjs +4 -3
  383. package/src/session-runtime/lifecycle/session-switch/resume.mjs +11 -4
  384. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +3 -3
  385. package/src/session-runtime/lifecycle/shared.mjs +25 -2
  386. package/src/session-runtime/lifecycle/teardown/session-close.mjs +2 -0
  387. package/src/session-runtime/lifecycle/teardown.mjs +5 -2
  388. package/src/session-runtime/lifecycle-api.mjs +3 -1
  389. package/src/session-runtime/mcp-glue/server-catalog.mjs +8 -0
  390. package/src/session-runtime/mcp-glue/server-input.mjs +3 -0
  391. package/src/session-runtime/mcp-oauth-login.mjs +112 -15
  392. package/src/session-runtime/model-route/set-route.mjs +14 -3
  393. package/src/session-runtime/owned-automation-run.mjs +229 -0
  394. package/src/session-runtime/provider-auth/login.mjs +10 -6
  395. package/src/session-runtime/provider-auth/setup.mjs +19 -2
  396. package/src/session-runtime/provider-catalog-cache.mjs +101 -25
  397. package/src/session-runtime/provider-models/catalog-load.mjs +14 -3
  398. package/src/session-runtime/provider-models/row-hydration.mjs +4 -0
  399. package/src/session-runtime/provider-models.mjs +76 -10
  400. package/src/session-runtime/provider-readiness.mjs +4 -0
  401. package/src/session-runtime/resource-api.mjs +5 -0
  402. package/src/session-runtime/resource-mcp-api.mjs +107 -13
  403. package/src/session-runtime/resource-skills-api.mjs +12 -0
  404. package/src/session-runtime/room-member-gate.mjs +48 -0
  405. package/src/session-runtime/routine-home-turn.mjs +156 -0
  406. package/src/session-runtime/routine-owner.mjs +48 -0
  407. package/src/session-runtime/schedule-session-run.mjs +30 -64
  408. package/src/session-runtime/services/agent-tool/close-flow.mjs +2 -1
  409. package/src/session-runtime/services/agent-tool/helpers.mjs +10 -0
  410. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +10 -2
  411. package/src/session-runtime/services/agent-tool/job-views/session-progress.mjs +13 -4
  412. package/src/session-runtime/services/agent-tool/notify.mjs +15 -4
  413. package/src/session-runtime/services/agent-tool/restart-notify.mjs +134 -0
  414. package/src/session-runtime/services/agent-tool/send-flow.mjs +24 -1
  415. package/src/session-runtime/services/agent-tool/spawn-flow/deferred-spawn.mjs +2 -1
  416. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +4 -3
  417. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +19 -4
  418. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +2 -0
  419. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +5 -1
  420. package/src/session-runtime/services/agent-tool/tag-registry/tag-lookup.mjs +8 -3
  421. package/src/session-runtime/services/agent-tool/tag-registry.mjs +3 -0
  422. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +4 -0
  423. package/src/session-runtime/services/agent-tool/worker-index.mjs +44 -10
  424. package/src/session-runtime/services/agent-tool.mjs +20 -0
  425. package/src/session-runtime/services/channel-admin.mjs +93 -24
  426. package/src/session-runtime/services/memory-access.mjs +37 -0
  427. package/src/session-runtime/services/provider-admin.mjs +166 -59
  428. package/src/session-runtime/services/schedule-access.mjs +91 -0
  429. package/src/session-runtime/services/usage-dashboard-model.mjs +2 -2
  430. package/src/session-runtime/services/usage-dashboard.mjs +27 -69
  431. package/src/session-runtime/session-create/session-options.mjs +34 -7
  432. package/src/session-runtime/session-hooks.mjs +16 -8
  433. package/src/session-runtime/setup-tool/executor.mjs +68 -6
  434. package/src/session-runtime/setup-tool/extended-actions.mjs +72 -14
  435. package/src/session-runtime/setup-tool/settings-contract.mjs +8 -6
  436. package/src/session-runtime/setup-tool/tool-defs.mjs +8 -0
  437. package/src/session-runtime/skills-api.mjs +16 -1
  438. package/src/session-runtime/tool-policy-refresh.mjs +238 -29
  439. package/src/session-runtime/turn/session-ops.mjs +10 -3
  440. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +11 -2
  441. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +3 -0
  442. package/src/session-runtime/usage-stats-api.mjs +6 -1
  443. package/src/session-runtime/webhook-session-run.mjs +49 -67
  444. package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +239 -0
  445. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +556 -22
  446. package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +11 -1
  447. package/src/session-runtime/workflow-agents-api.mjs +4 -1
  448. package/src/standalone/channel-transport/http-routes.mjs +10 -9
  449. package/src/standalone/daemon-agent-control.mjs +1 -8
  450. package/src/standalone/daemon-desktop-runtime.mjs +20 -7
  451. package/src/standalone/daemon-owner.mjs +117 -25
  452. package/src/standalone/daemon-session-control.mjs +11 -2
  453. package/src/standalone/daemon.mjs +61 -17
  454. package/src/standalone/session-call-cache.mjs +6 -0
  455. package/src/standalone/session-protocol.mjs +28 -1
  456. package/src/standalone/session-service/retention/busy.mjs +7 -2
  457. package/src/standalone/session-service/session-calls/session-turns.mjs +9 -0
  458. package/src/standalone/session-service.mjs +19 -0
  459. package/src/standalone/session-transport/routes.mjs +7 -6
  460. package/src/tui/app/model-options.mjs +39 -2
  461. package/src/tui/app/model-picker.mjs +16 -2
  462. package/src/tui/app/provider-setup-picker.mjs +1 -2
  463. package/src/tui/app/slash-commands.mjs +20 -0
  464. package/src/tui/dist/index.mjs +487 -128
  465. package/src/tui/session/context-state.mjs +6 -1
  466. package/src/tui/session/notice-surface.mjs +35 -4
  467. package/src/tui/session/notice-text.mjs +0 -1
  468. package/src/tui/session/queue-helpers.mjs +2 -1
  469. package/src/tui/session/session-api/extensions.mjs +4 -0
  470. package/src/tui/session/session-api/intake/submission.mjs +12 -13
  471. package/src/tui/session/session-api/integrations.mjs +9 -5
  472. package/src/tui/session/session-api/lifecycle/session-reset.mjs +13 -3
  473. package/src/tui/session/session-api/lifecycle.mjs +17 -1
  474. package/src/tui/session/session-api/routes.mjs +48 -3
  475. package/src/tui/session/session-flow/drain.mjs +20 -3
  476. package/src/tui/session/session-flow/queue/entry-shape.mjs +2 -1
  477. package/src/tui/session/session-flow/queue/take-entries.mjs +3 -2
  478. package/src/tui/session/session-flow/steering.mjs +104 -24
  479. package/src/tui/session/session-flow.mjs +2 -0
  480. package/src/tui/session/tool-approval.mjs +26 -5
  481. package/src/tui/session/transcript-intake.mjs +6 -2
  482. package/src/tui/session/transcript-spill.mjs +5 -1
  483. package/src/tui/session/tui-steering-persist.mjs +65 -42
  484. package/src/tui/session/turn-live-call.mjs +56 -0
  485. package/src/tui/session/turn-stream.mjs +33 -0
  486. package/src/tui/session/turn.mjs +50 -7
  487. package/src/ui/statusline.mjs +1 -6
  488. package/src/ui/streaming-markdown-heal.mjs +24 -7
  489. package/src/vendor/statusline/bin/statusline-route.mjs +0 -1
  490. package/src/vendor/statusline/src/gateway/route-meta.mjs +0 -1
  491. package/src/workflows/default/WORKFLOW.md +9 -13
  492. package/src/session-runtime/automation-agents.mjs +0 -94
  493. package/src/session-runtime/automation-workflow.mjs +0 -59
@@ -3,6 +3,8 @@
3
3
  * lifecycle groups (teardown, session switching, inheritance).
4
4
  */
5
5
  import { hasUserConversationMessage } from '../../runtime/agent/orchestrator/session/manager/prompt-utils.mjs';
6
+ import { isAgentHomeSessionId } from '../../runtime/agent/orchestrator/session/agent-home.mjs';
7
+ import { isRoomThreadSessionId } from '../../runtime/agent/orchestrator/session/agent-room.mjs';
6
8
 
7
9
  export { SESSION_ID_PATTERN } from '../../runtime/agent/orchestrator/runtime-core/session-id.mjs';
8
10
 
@@ -19,13 +21,34 @@ export function isScratchConversation(messages, liveTurnMessages) {
19
21
  );
20
22
  }
21
23
 
24
+ /** An agent's home thread is never scratch: a blank one is tombstoned on no
25
+ * close, so its fixed id stays open for the next visit. Neither is a group
26
+ * room's log or member session (agent-room.mjs), whose ids are fixed too. */
22
27
  export function isScratchSession(session) {
28
+ if (isAgentHomeSessionId(session?.id) || isRoomThreadSessionId(session?.id)) return false;
23
29
  return isScratchConversation(session?.messages, session?.liveTurnMessages);
24
30
  }
25
31
 
26
- export function createSurfaceSessionCloser(mgr) {
32
+ /** `nonBlocking`: the closer returns the manager's closeSessionAsync promise
33
+ * (when it has one), whose barrier never sleeps the event loop. */
34
+ export function createSurfaceSessionCloser(mgr, nonBlocking = false) {
27
35
  return function closeSurfaceSession(session, reason, options) {
28
36
  if (!session?.id) return false;
29
- return mgr.closeSession(session.id, reason, options);
37
+ return closeManagedSession(mgr, session.id, reason, options, nonBlocking);
30
38
  };
31
39
  }
40
+
41
+ /** Await a switch path's close of the outgoing session. A close whose durable
42
+ * barrier failed leaves the session open, so the switch must not proceed. */
43
+ export async function closeOutgoingSession(closeSurfaceSession, session, reason, options) {
44
+ if ((await closeSurfaceSession(session, reason, options)) === false) {
45
+ throw new Error(`session close failed (reason=${reason}); the current session is kept`);
46
+ }
47
+ }
48
+
49
+ /** The manager's close: closeSessionAsync when asked for and available,
50
+ * else its closeSession, unchanged. */
51
+ export function closeManagedSession(mgr, id, reason, options, nonBlocking = true) {
52
+ if (nonBlocking && typeof mgr.closeSessionAsync === 'function') return mgr.closeSessionAsync(id, reason, options);
53
+ return mgr.closeSession(id, reason, options);
54
+ }
@@ -47,6 +47,8 @@ export function closeOwnSession(deps, closeSurfaceSession, reason) {
47
47
  clearRuntimeNotifications,
48
48
  notificationListeners,
49
49
  } = deps;
50
+ // The close's answer: a boolean, or the promise of one when the closer
51
+ // retries a refused barrier rename on timers (callers await it).
50
52
  let ok = false;
51
53
  const session = getSession();
52
54
  if (session?.id) {
@@ -226,13 +226,16 @@ export function createTeardown(deps, { ingestSessionIntoMemory, closeSurfaceSess
226
226
  };
227
227
  if (outstandingCloses.size > 0) void finish();
228
228
  else await finish();
229
- const { ok, stops } = await inOwnLoopTurn(() => {
229
+ const { ownClose, stops } = await inOwnLoopTurn(() => {
230
230
  const runtimeStops = startRuntimeStops(deps, reason, scope);
231
231
  const closed = closeOwnSession(deps, closeSurfaceSession, reason);
232
- return { ok: closed, stops: { channelStop, ...runtimeStops, ...startWorkStops(deps, reason, scope) } };
232
+ return { ownClose: closed, stops: { channelStop, ...runtimeStops, ...startWorkStops(deps, reason, scope) } };
233
233
  });
234
234
  if (scope.detach) await settleDetached(withTeardownDeadline, stops);
235
235
  else await settleAll(withTeardownDeadline, stops);
236
+ // Reported closed only once the barrier is on disk (closeSessionAsync),
237
+ // and its write control is released only after that.
238
+ const ok = await ownClose;
236
239
  // The session went away (idle eviction included): free its save baselines.
237
240
  if (scope.closingSessionId) forgetSessionSaveBaseline(scope.closingSessionId, { releaseWriteControl: true });
238
241
  // Self-update stages in the background and swaps on the next clean launch
@@ -20,7 +20,9 @@ export { resolveResumeCwd } from './lifecycle/session-switch.mjs';
20
20
  export function createLifecycleApi(deps) {
21
21
  const shared = {
22
22
  cancelBackgroundTasks: deps.cancelBackgroundTasks || cancelBackgroundTasks,
23
- closeSurfaceSession: createSurfaceSessionCloser(deps.mgr),
23
+ // Teardown and the switch paths await their close: a barrier rename the OS
24
+ // refuses is retried on timers instead of sleeping the daemon loop.
25
+ closeSurfaceSession: createSurfaceSessionCloser(deps.mgr, true),
24
26
  ingestSessionIntoMemory: createMemoryIngest(deps),
25
27
  };
26
28
  const teardown = createTeardown(deps, shared);
@@ -2,6 +2,7 @@
2
2
  // servers plus the servers registered plugins declare (resolved live, with
3
3
  // explicit owner metadata), per-server config reads, and the live/failed rollup.
4
4
  import { clean } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
5
+ import { connectorOfServer, gmailAppPasswordAccount } from '../../runtime/shared/connector-catalog.mjs';
5
6
  import { envFlag } from '../../runtime/shared/env.mjs';
6
7
  import { resolveMcpServerCatalog } from './plugin-servers.mjs';
7
8
 
@@ -142,9 +143,16 @@ export function createMcpServerCatalog({
142
143
  source: sources[name] || 'config',
143
144
  };
144
145
  if (authRequired) row.authRequired = true;
146
+ // The catalog service this server connects (connector-catalog.mjs):
147
+ // its marker.
148
+ const connector = entry.owner ? '' : connectorOfServer(cfg);
149
+ if (connector) row.connector = connector;
145
150
  const oauth = mcpClient.getMcpServerOAuthState?.(cfg);
146
151
  // Why the last sign-in did not complete (already credential-scrubbed).
147
152
  if (oauth) row.oauth = authRequired && fail?.msg ? { ...oauth, lastError: fail.msg } : oauth;
153
+ // Gmail connected with an app password: the account it runs as.
154
+ const appPasswordAccount = gmailAppPasswordAccount(cfg);
155
+ if (appPasswordAccount) row.appPassword = { account: appPasswordAccount };
148
156
  if (entry.owner) {
149
157
  row.owner = entry.owner;
150
158
  row.pluginEnabled = entry.pluginEnabled;
@@ -70,6 +70,9 @@ export function createMcpServerInput({ mcpClient, getCurrentCwd }) {
70
70
  if (bearerTokenEnvVar) config.bearer_token_env_var = bearerTokenEnvVar;
71
71
  const envHeaders = coerceStringRecord(input.env_http_headers || input.envHttpHeaders);
72
72
  if (envHeaders) config.env_http_headers = envHeaders;
73
+ // The user's own OAuth client (앱 번호); its secret never lands here.
74
+ const oauthClientId = clean(input.oauthClientId);
75
+ if (oauthClientId) config.oauthClientId = oauthClientId;
73
76
  return config;
74
77
  };
75
78
  const url = clean(input.url);
@@ -8,6 +8,15 @@ import { openInBrowser } from '../runtime/shared/open-url.mjs';
8
8
  // Settled flows stay readable long enough for a poller to observe the result.
9
9
  const SETTLED_FLOW_TTL_MS = 10 * 60_000;
10
10
  const NOT_COMPLETED = 'MCP OAuth sign-in was not completed';
11
+ export const MCP_OAUTH_LOGIN_TIMED_OUT = 'MCP OAuth sign-in timed out';
12
+ // A sign-in nobody finishes ends here, ahead of the OAuth layer's own 5-minute
13
+ // abort, so its failure is reported as the timeout it is.
14
+ const LOGIN_DEADLINE_MS = 5 * 60_000 - 10_000;
15
+ // The longest a status read waits for a change (local callers only).
16
+ const MAX_STATUS_WAIT_MS = 15_000;
17
+ // How a sign-in treats the account (mcp/oauth-account.mjs): a normal
18
+ // authorization, a repair of the same connection, or a transactional switch.
19
+ const MODES = new Set(['connect', 'reauth', 'switch']);
11
20
 
12
21
  const NOT_PENDING = 'MCP OAuth sign-in is no longer pending';
13
22
 
@@ -19,17 +28,36 @@ const NOT_PENDING = 'MCP OAuth sign-in is no longer pending';
19
28
  const flows = new Map();
20
29
 
21
30
  // A pending flow keeps its authorization URL readable: a client polling the
22
- // status (remote sign-in) still shows the page to open.
31
+ // status (remote sign-in) still shows the page to open. `step` names what a
32
+ // pending flow waits for: `browser` (the sign-in page), `connecting` (the
33
+ // callback arrived; tokens are exchanged), `tools` (signed in; the server
34
+ // connects and lists its tools).
23
35
  const view = (flow) => ({
24
36
  flowId: flow.flowId,
25
37
  state: flow.state,
38
+ ...(flow.state === 'pending' ? { step: flow.step } : {}),
26
39
  ...(flow.state === 'pending' && flow.url ? { url: flow.url } : {}),
27
40
  ...(flow.error ? { error: flow.error } : {}),
41
+ ...(flow.reason ? { reason: flow.reason } : {}),
28
42
  });
43
+ const versionOf = (flow) => `${flow.state}:${flow.step}`;
44
+ // Wake every status read waiting on this flow.
45
+ function changed(flow) {
46
+ const waiters = flow.waiters;
47
+ flow.waiters = [];
48
+ for (const wake of waiters) wake();
49
+ }
50
+ function setStep(flow, step) {
51
+ if (flow.state !== 'pending' || flow.step === step) return;
52
+ flow.step = step;
53
+ changed(flow);
54
+ }
29
55
  function settle(flow, state, error = null) {
30
56
  if (flow.state !== 'pending') return;
31
57
  flow.state = state;
32
58
  if (state === 'failed') flow.error = error || NOT_COMPLETED;
59
+ clearTimeout(flow.deadline);
60
+ changed(flow);
33
61
  setTimeout(() => flows.delete(flow.flowId), SETTLED_FLOW_TTL_MS).unref?.();
34
62
  }
35
63
  function flowOf(flowId) {
@@ -40,19 +68,23 @@ function flowOf(flowId) {
40
68
  async function cancelFlow(flow) {
41
69
  if (flow.state === 'pending') {
42
70
  flow.cancelled = true;
43
- flow.owner.cancel(flow.name);
71
+ flow.abort();
44
72
  await flow.done;
45
73
  }
46
74
  return view(flow);
47
75
  }
48
76
 
49
77
  /**
50
- * `connect(name, openBrowser)` reconnects the server interactively, calling
51
- * `openBrowser(url)` when a login starts, and resolves `{ connected, error }`.
78
+ * `connect(name, launch)` reconnects the server interactively, calling
79
+ * `launch(url)` when a login starts, and resolves `{ connected, error }`.
52
80
  * `cancel(name)` aborts that server's pending login. `completeCallback(name,
53
81
  * callbackUrl)` answers it with a pasted redirect URL (throws a fixed message
54
- * for an invalid one). `onComplete(name)` runs after a successful sign-in,
55
- * before the flow reports complete.
82
+ * for an invalid one). `switchAccount(name, launch, attempt)` runs an account
83
+ * switch instead (mode `switch`): it registers `attempt.abort` / `attempt.answer`
84
+ * and resolves `{ connected, error }` only after committing (or rolling back)
85
+ * the new sign-in. `launch` carries `accountMode` and the progress hooks
86
+ * `onCallback` / `onAuthorized` the OAuth layer calls. `onComplete(name)` runs
87
+ * after a successful sign-in, once the flow already reports complete.
56
88
  */
57
89
  export function createMcpOAuthLogins({
58
90
  connect,
@@ -60,8 +92,12 @@ export function createMcpOAuthLogins({
60
92
  completeCallback = () => {
61
93
  throw new Error(NOT_PENDING);
62
94
  },
95
+ switchAccount = async () => {
96
+ throw new Error('Switching the account is not supported here');
97
+ },
63
98
  onComplete = async () => {},
64
99
  openBrowser = openInBrowser,
100
+ loginTimeoutMs = LOGIN_DEADLINE_MS,
65
101
  }) {
66
102
  // name -> tail of that server's serialized begin/cancel/sign-out steps, so
67
103
  // replacing a pending flow is atomic and exactly one can be pending.
@@ -89,10 +125,11 @@ export function createMcpOAuthLogins({
89
125
  Promise.all([...flows.values()].filter((flow) => flow.owner === owner && flow.name === name).map(cancelFlow));
90
126
 
91
127
  async function begin(name, options = {}) {
92
- const { openBrowser: hostBrowser } = options || {};
128
+ const { openBrowser: hostBrowser, mode = 'connect' } = options || {};
93
129
  if (hostBrowser !== undefined && typeof hostBrowser !== 'boolean') {
94
130
  throw new TypeError('openBrowser must be a boolean');
95
131
  }
132
+ if (!MODES.has(mode)) throw new TypeError('mode must be connect, reauth or switch');
96
133
  let announceUrl;
97
134
  const urlReady = new Promise((resolve) => {
98
135
  announceUrl = resolve;
@@ -105,10 +142,17 @@ export function createMcpOAuthLogins({
105
142
  flowId: `mcp_oauth_${randomUUID()}`,
106
143
  name,
107
144
  owner,
145
+ mode,
108
146
  state: 'pending',
147
+ step: 'starting',
109
148
  error: null,
149
+ reason: null,
110
150
  cancelled: false,
151
+ timedOut: false,
111
152
  url: null,
153
+ waiters: [],
154
+ abort: () => owner.cancel(name),
155
+ answer: (callbackUrl) => owner.completeCallback(name, callbackUrl),
112
156
  };
113
157
  flows.set(flow.flowId, flow);
114
158
  const launch = async (url) => {
@@ -117,22 +161,51 @@ export function createMcpOAuthLogins({
117
161
  if (flow.cancelled) throw new Error('MCP OAuth login cancelled');
118
162
  flow.awaitingCallback = true;
119
163
  flow.url = url;
164
+ setStep(flow, 'browser');
120
165
  announceUrl(url);
121
166
  if (hostBrowser !== false) openBrowser(url);
167
+ // Nobody finished the sign-in page in time.
168
+ flow.deadline = setTimeout(() => {
169
+ if (flow.state !== 'pending' || flow.step !== 'browser') return;
170
+ flow.timedOut = flow.cancelled = true;
171
+ flow.abort();
172
+ }, loginTimeoutMs);
173
+ flow.deadline.unref?.();
122
174
  };
175
+ launch.accountMode = mode;
176
+ launch.onCallback = () => setStep(flow, 'connecting');
177
+ launch.onAuthorized = () => setStep(flow, 'tools');
123
178
  flow.done = (async () => {
124
179
  try {
125
- const outcome = await connect(name, launch);
126
- if (flow.cancelled) settle(flow, 'cancelled');
180
+ const outcome =
181
+ mode === 'switch'
182
+ ? await switchAccount(name, launch, {
183
+ set abort(fn) {
184
+ flow.abort = fn;
185
+ },
186
+ set answer(fn) {
187
+ flow.answer = fn;
188
+ },
189
+ })
190
+ : await connect(name, launch);
191
+ if (flow.timedOut) {
192
+ flow.reason = 'timeout';
193
+ settle(flow, 'failed', MCP_OAUTH_LOGIN_TIMED_OUT);
194
+ } else if (flow.cancelled) settle(flow, 'cancelled');
127
195
  else if (outcome?.connected) {
128
- // The sign-in itself succeeded; a failed follow-up never undoes it.
129
- await Promise.resolve()
196
+ settle(flow, 'complete');
197
+ // The other runtimes reconnect after the flow already reports
198
+ // complete: the caller never waits for every peer's reconnect, and
199
+ // a failed follow-up never undoes the sign-in.
200
+ void Promise.resolve()
130
201
  .then(() => onComplete(name))
131
202
  .catch(() => {});
132
- settle(flow, 'complete');
133
203
  } else settle(flow, 'failed', outcome?.error);
134
204
  } catch (error) {
135
- settle(flow, flow.cancelled ? 'cancelled' : 'failed', error?.message || String(error));
205
+ if (flow.timedOut) {
206
+ flow.reason = 'timeout';
207
+ settle(flow, 'failed', MCP_OAUTH_LOGIN_TIMED_OUT);
208
+ } else settle(flow, flow.cancelled ? 'cancelled' : 'failed', error?.message || String(error));
136
209
  }
137
210
  })();
138
211
  return flow;
@@ -147,7 +220,31 @@ export function createMcpOAuthLogins({
147
220
 
148
221
  return {
149
222
  begin,
150
- status: (flowId) => view(flowOf(flowId)),
223
+ /** The flow's view. With `{ since, waitMs }` (the `state:step` a caller
224
+ * last saw) the read resolves on the flow's next change, or after at most
225
+ * `waitMs` unchanged: the caller follows each step as it happens. */
226
+ status(flowId, options = {}) {
227
+ const flow = flowOf(flowId);
228
+ const { since, waitMs } = options || {};
229
+ if (since === undefined) return view(flow);
230
+ if (typeof since !== 'string') throw new TypeError('since must be a string');
231
+ if (!Number.isFinite(waitMs) || waitMs < 0) throw new TypeError('waitMs must be a non-negative number');
232
+ if (since !== versionOf(flow) || flow.state !== 'pending' || waitMs === 0) return view(flow);
233
+ return new Promise((resolve) => {
234
+ const wake = () => {
235
+ clearTimeout(timer);
236
+ resolve(view(flow));
237
+ };
238
+ const timer = setTimeout(
239
+ () => {
240
+ flow.waiters = flow.waiters.filter((waiter) => waiter !== wake);
241
+ resolve(view(flow));
242
+ },
243
+ Math.min(waitMs, MAX_STATUS_WAIT_MS)
244
+ );
245
+ flow.waiters.push(wake);
246
+ });
247
+ },
151
248
  cancel(flowId) {
152
249
  const flow = flowOf(flowId);
153
250
  return flow.owner.exclusive(flow.name, () => cancelFlow(flow));
@@ -158,7 +255,7 @@ export function createMcpOAuthLogins({
158
255
  async complete(flowId, callbackUrl) {
159
256
  const flow = flowOf(flowId);
160
257
  if (flow.state !== 'pending' || flow.cancelled || !flow.awaitingCallback) throw new Error(NOT_PENDING);
161
- flow.owner.completeCallback(flow.name, callbackUrl);
258
+ flow.answer(callbackUrl);
162
259
  await flow.done;
163
260
  return view(flow);
164
261
  },
@@ -140,15 +140,26 @@ function modelSwitchAnchorIndex(messages) {
140
140
  * (like an inheritance boundary): the transcript shows it live and after a
141
141
  * restore, and the model conversation gains no synthetic message. */
142
142
  export function recordModelSwitchBoundary(session, boundary) {
143
+ return recordTranscriptBoundary(
144
+ session,
145
+ 'modelSwitches',
146
+ boundary,
147
+ (entry) => entry?.at === boundary.at && entry?.modelId === boundary.modelId
148
+ );
149
+ }
150
+
151
+ /** A display-only boundary appended to `meta[field]` of the switch anchor
152
+ * message (model and workflow switches); `same` finds an already recorded one. */
153
+ export function recordTranscriptBoundary(session, field, boundary, same) {
143
154
  const messages = Array.isArray(session?.messages) ? session.messages : [];
144
155
  const index = modelSwitchAnchorIndex(messages);
145
156
  if (index < 0) return false;
146
157
  const anchor = messages[index];
147
- const switches = Array.isArray(anchor.meta?.modelSwitches) ? anchor.meta.modelSwitches : [];
148
- if (switches.some((entry) => entry?.at === boundary.at && entry?.modelId === boundary.modelId)) return true;
158
+ const switches = Array.isArray(anchor.meta?.[field]) ? anchor.meta[field] : [];
159
+ if (switches.some(same)) return true;
149
160
  // Replace the message object: the session save worker detects changed
150
161
  // messages by reference identity, so an in-place meta edit is never saved.
151
- messages[index] = { ...anchor, meta: { ...anchor.meta, modelSwitches: [...switches, boundary] } };
162
+ messages[index] = { ...anchor, meta: { ...anchor.meta, [field]: [...switches, boundary] } };
152
163
  return true;
153
164
  }
154
165
 
@@ -0,0 +1,229 @@
1
+ /**
2
+ * One run of an owned automation — a schedule or a webhook endpoint. The run
3
+ * itself is a turn in the owner bot's home thread (routine-home-turn.mjs);
4
+ * the bot's reply there is its result, so nothing is posted besides it. A run
5
+ * whose owner bot does not exist (routine-owner.mjs) never fires: it is
6
+ * skipped as paused, never run elsewhere. A second run of the same automation
7
+ * while one is in flight is skipped (and recorded).
8
+ *
9
+ * Unattended runs (timed fires, received webhooks; not the app's or a chat's
10
+ * "Run now") also back off after consecutive failures (ROUTINE_LIMITS).
11
+ * Runs have no concurrency cap or deadline (user decision).
12
+ *
13
+ * Routine results from before routines ran in the bot's thread may still sit
14
+ * on a routine's record (`result_delivery`): redeliverRoutine and the daemon's
15
+ * recovery queue them into the owner's thread (agent-home-delivery.mjs),
16
+ * never running the routine again.
17
+ */
18
+ import { announceRoutineRun, deliverScheduleResult } from './agent-home-delivery.mjs';
19
+ import { ROUTINE_LIMITS, routineBackoffRemaining } from '../runtime/shared/routine-limits.mjs';
20
+ import { ROUTINE_OWNER_PROBLEMS, routineOwnerProblem } from './routine-owner.mjs';
21
+
22
+ const DELIVERY_RETRY_MS = 60_000;
23
+ const errorText = (error) => error?.message || String(error);
24
+ const defaultLog = (line) => process.stderr.write(`mixdog routines: ${line}\n`);
25
+
26
+ // `${kind}:${name}` of every run in flight in this process: timed fires,
27
+ // received webhooks and the app's "Run now" all come through here.
28
+ const activeRuns = new Set();
29
+
30
+ // The PG record of each kind, imported on first use.
31
+ const RECORDS = {
32
+ schedule: () =>
33
+ import('../runtime/channels/lib/schedules-db.mjs').then((db) => ({
34
+ state: (name) => db.getSchedule(name),
35
+ list: () => db.listSchedules(),
36
+ recordDelivery: db.recordScheduleDelivery,
37
+ })),
38
+ webhook: () =>
39
+ import('../runtime/channels/lib/webhooks-db.mjs').then((db) => ({
40
+ state: (name) => db.loadEndpointConfig(name),
41
+ list: () => db.listEndpoints(),
42
+ recordDelivery: db.recordEndpointDelivery,
43
+ })),
44
+ };
45
+ /** `deps` over the kind's PG record for whatever `deps` leaves out. */
46
+ async function withRecord(kind, deps) {
47
+ if (deps.state && deps.list && deps.recordDelivery) return deps;
48
+ const record = await RECORDS[kind]();
49
+ return {
50
+ ...deps,
51
+ state: deps.state || record.state,
52
+ list: deps.list || record.list,
53
+ recordDelivery: deps.recordDelivery || record.recordDelivery,
54
+ };
55
+ }
56
+
57
+ let retryArmed = false;
58
+ /** One timer for the whole host: re-queue every record's undelivered result. */
59
+ function armDeliveryRetry() {
60
+ if (retryArmed) return;
61
+ retryArmed = true;
62
+ setTimeout(() => {
63
+ retryArmed = false;
64
+ recoverRoutineDeliveries().catch((error) => defaultLog(`delivery retry failed: ${errorText(error)}`));
65
+ }, DELIVERY_RETRY_MS).unref?.();
66
+ }
67
+
68
+ /** Show `run`'s live-only row in the owner's home; a failure only logs. */
69
+ async function announceRun(announce, row, run, log) {
70
+ try {
71
+ await announce(row, run);
72
+ } catch (error) {
73
+ log(`${run.kind} "${row.name}": run row not shown: ${errorText(error)}`);
74
+ }
75
+ }
76
+
77
+ /** Queue `item` for `row`'s owner; clear the record on success, mark it
78
+ * 'failed' (and arm the retry) when the queue refuses it. */
79
+ async function queueResult(row, item, entry, { deliver, recordDelivery, onDeliveryFailed, announce, log }) {
80
+ try {
81
+ await deliver(row, item);
82
+ } catch (error) {
83
+ const message = errorText(error);
84
+ log(`${item.kind} "${row.name}": result could not be queued (${message}); retrying later`);
85
+ await recordDelivery(row.name, item.runId, { ...entry, state: 'failed', error: message }).catch((recordError) =>
86
+ log(`${item.kind} "${row.name}": delivery failure not recorded: ${errorText(recordError)}`)
87
+ );
88
+ // The home's run row turns into "result not delivered" (retry: retryRoutineDelivery).
89
+ await announceRun(announce, row, { ...item, state: 'undelivered' }, log);
90
+ onDeliveryFailed();
91
+ return false;
92
+ }
93
+ // A clear that fails leaves the record 'pending': the next recovery re-queues
94
+ // it, which the queue's run-id key turns into a no-op.
95
+ await recordDelivery(row.name, item.runId, null).catch((error) =>
96
+ log(`${item.kind} "${row.name}": delivered result not cleared: ${errorText(error)}`)
97
+ );
98
+ return true;
99
+ }
100
+
101
+ /**
102
+ * `row` is `{ name, ownerAgentId }`; `run({ signal })` resolves `{ sessionId,
103
+ * result }` (`signal` is the caller's cancellation, if any). Resolves the
104
+ * outcome, or `{ skipped: true, reason: 'owner' | 'overlap' | 'backoff' }`
105
+ * (an owner skip also names `ownerProblem` and its `message`); a failed run
106
+ * rejects.
107
+ */
108
+ export async function runOwnedAutomation(kind, row, run, deps = {}) {
109
+ if (!row?.name) throw new Error(`${kind} run: a ${kind} row is required`);
110
+ const {
111
+ recordSkip = async () => {},
112
+ active = activeRuns,
113
+ limits = ROUTINE_LIMITS,
114
+ attended = false,
115
+ signal = null,
116
+ now = Date.now,
117
+ ownerProblem = routineOwnerProblem,
118
+ log = defaultLog,
119
+ } = deps;
120
+ const name = row.name;
121
+ const key = `${kind}:${name}`;
122
+ const skip = async (reason, extra = {}) => {
123
+ await Promise.resolve()
124
+ .then(() => recordSkip(name, new Date(now())))
125
+ .catch((error) => log(`${kind} "${name}": skip record failed: ${errorText(error)}`));
126
+ return { name, skipped: true, reason, sessionId: null, result: '', ...extra };
127
+ };
128
+ // A routine runs only in its own bot's thread: without that bot it waits.
129
+ const problem = await Promise.resolve()
130
+ .then(() => ownerProblem(row.ownerAgentId))
131
+ .catch(() => '');
132
+ if (problem) {
133
+ log(`${kind} "${name}": paused, ${ROUTINE_OWNER_PROBLEMS[problem]}`);
134
+ return skip('owner', { ownerProblem: problem, message: ROUTINE_OWNER_PROBLEMS[problem] });
135
+ }
136
+ if (active.has(key)) return skip('overlap');
137
+ active.add(key);
138
+ try {
139
+ if (!attended) {
140
+ const record = await withRecord(kind, deps);
141
+ const state = await Promise.resolve()
142
+ .then(() => record.state(name))
143
+ .catch(() => null);
144
+ const wait = routineBackoffRemaining(state, now(), limits);
145
+ if (wait > 0) {
146
+ log(`${kind} "${name}": skipped, backing off ${Math.ceil(wait / 1000)}s after failures in a row`);
147
+ return await skip('backoff');
148
+ }
149
+ }
150
+ return await run({ signal });
151
+ } finally {
152
+ active.delete(key);
153
+ }
154
+ }
155
+
156
+ /** Re-queue every result `row`'s record still holds, oldest first; resolves
157
+ * the errors of the ones the queue still refuses. Never reruns the routine. */
158
+ export async function redeliverRoutine(kind, row, deps = {}) {
159
+ const {
160
+ deliver = deliverScheduleResult,
161
+ onDeliveryFailed = armDeliveryRetry,
162
+ announce = announceRoutineRun,
163
+ log = defaultLog,
164
+ } = deps;
165
+ const record = await withRecord(kind, deps);
166
+ const entries = Object.entries(row?.resultDelivery || {}).sort(([, a], [, b]) =>
167
+ String(a?.at).localeCompare(String(b?.at))
168
+ );
169
+ const errors = [];
170
+ for (const [runId, entry] of entries) {
171
+ const result = entry?.result || {};
172
+ const item = {
173
+ runId,
174
+ kind,
175
+ ok: result.ok === true,
176
+ text: result.text || '',
177
+ runSessionId: result.runSessionId,
178
+ reason: result.reason,
179
+ paused: result.paused,
180
+ };
181
+ await queueResult(row, item, entry, {
182
+ ...record,
183
+ deliver,
184
+ announce,
185
+ log,
186
+ onDeliveryFailed: () => errors.push(runId),
187
+ });
188
+ }
189
+ if (errors.length) onDeliveryFailed();
190
+ return errors;
191
+ }
192
+
193
+ /** Daemon start (and the retry timer): re-queue every undelivered result of
194
+ * every schedule and webhook. Resolves how many runs still wait. Without a
195
+ * PostgreSQL cluster no routine exists, and none is started for this. */
196
+ export async function recoverRoutineDeliveries(depsByKind = null) {
197
+ if (!depsByKind) {
198
+ const [{ pgClusterExists }, { resolvePluginData }] = await Promise.all([
199
+ import('../runtime/memory/lib/pg/adapter.mjs'),
200
+ import('../runtime/shared/plugin-paths.mjs'),
201
+ ]);
202
+ if (!pgClusterExists(resolvePluginData())) return 0;
203
+ }
204
+ let waiting = 0;
205
+ for (const kind of ['schedule', 'webhook']) {
206
+ const deps = await withRecord(kind, depsByKind?.[kind] || {});
207
+ for (const row of await deps.list()) {
208
+ if (row?.resultDelivery && Object.keys(row.resultDelivery).length) {
209
+ waiting += (await redeliverRoutine(kind, row, deps)).length;
210
+ }
211
+ }
212
+ }
213
+ return waiting;
214
+ }
215
+
216
+ /** The app's "retry" on one routine: rejects with the error when still refused. */
217
+ export async function retryRoutineDelivery(kind, name, deps = {}) {
218
+ if (kind !== 'schedule' && kind !== 'webhook') throw new Error(`unknown routine kind "${kind}"`);
219
+ const record = await withRecord(kind, deps);
220
+ const row = await record.state(name);
221
+ if (!row) throw new Error(`${kind} "${name}" does not exist`);
222
+ const failed = await redeliverRoutine(kind, row, { ...deps, ...record });
223
+ if (failed.length) {
224
+ const latest = await record.state(name);
225
+ const error = latest?.resultDelivery?.[failed.at(-1)]?.error;
226
+ throw new Error(`"${name}": the result still could not be delivered${error ? ` (${error})` : ''}`);
227
+ }
228
+ return { name, delivered: Object.keys(row.resultDelivery || {}).length };
229
+ }
@@ -6,11 +6,11 @@ import {
6
6
  forgetProviderAuth,
7
7
  loginOAuthProvider,
8
8
  saveOpenAIUsageSessionKey,
9
- saveOpenCodeGoUsageAuth,
10
9
  saveProviderApiKey,
11
10
  saveCustomProvider,
12
11
  removeCustomProvider,
13
12
  updateCustomProviderModel,
13
+ saveModelPreferences,
14
14
  testCustomProvider,
15
15
  checkCustomProviderKeyEnv,
16
16
  discoverCustomProviderModels,
@@ -70,11 +70,6 @@ export function createLoginApi({ cfgMod, awaitKeychainPrewarm, reloadFullConfig,
70
70
  refresh.afterUsageAuthChange();
71
71
  return result;
72
72
  },
73
- async saveOpenCodeGoUsageAuth(opts) {
74
- const result = await saveOpenCodeGoUsageAuth(cfgMod, opts);
75
- refresh.afterUsageAuthChange();
76
- return result;
77
- },
78
73
  async saveCustomProvider(input) {
79
74
  await awaitKeychainPrewarm();
80
75
  const result = await saveCustomProvider(cfgMod, input);
@@ -86,6 +81,15 @@ export function createLoginApi({ cfgMod, awaitKeychainPrewarm, reloadFullConfig,
86
81
  refresh.afterCredentialChange();
87
82
  return result;
88
83
  },
84
+ /** Model management: /model visibility and favorites of one provider.
85
+ * Catalog rows read modelSettings on every answer, so only a custom
86
+ * provider's catalog (its hidden overrides) needs the refresh chain. */
87
+ setModelPreferences(providerId, changes) {
88
+ const result = saveModelPreferences(cfgMod, providerId, changes);
89
+ if (result.customCatalogChanged) refresh.afterCredentialChange();
90
+ else reloadFullConfig();
91
+ return result;
92
+ },
89
93
  async removeCustomProvider(providerId) {
90
94
  await awaitKeychainPrewarm();
91
95
  const result = await removeCustomProvider(cfgMod, providerId);