mixdog 1.0.11 → 1.1.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 (471) hide show
  1. package/NOTICE.md +20 -0
  2. package/README.md +75 -181
  3. package/package.json +9 -4
  4. package/scripts/build-shipped-history.mjs +131 -0
  5. package/scripts/check-changelog-locales.mjs +203 -0
  6. package/scripts/computer-task-cost-report.mjs +0 -4
  7. package/scripts/lib/run-node-tests.mjs +235 -13
  8. package/scripts/lib/test-failure-records.mjs +5 -0
  9. package/scripts/lib/test-failure-summary.mjs +9 -3
  10. package/scripts/lib/test-spawn-ledger.mjs +19 -0
  11. package/scripts/prune-embedding-runtime.mjs +9 -5
  12. package/scripts/sync-client-version-floors.mjs +100 -0
  13. package/scripts/test-direct.mjs +16 -1
  14. package/src/agents/advisor/AGENT.md +38 -0
  15. package/src/agents/advisor/agent.json +6 -0
  16. package/src/agents/reviewer/AGENT.md +5 -2
  17. package/src/agents/reviewer/agent.json +1 -1
  18. package/src/defaults/shipped-history.json +381 -0
  19. package/src/defaults/skills/docx/SKILL.md +7 -5
  20. package/src/defaults/skills/docx/references/html.md +9 -8
  21. package/src/defaults/skills/docx/references/native-authoring.md +28 -18
  22. package/src/defaults/skills/goal-management/SKILL.md +11 -5
  23. package/src/defaults/skills/local-provider/SKILL.md +2 -1
  24. package/src/defaults/skills/pdf/SKILL.md +9 -6
  25. package/src/defaults/skills/pdf/references/html.md +17 -12
  26. package/src/defaults/skills/pptx/SKILL.md +8 -6
  27. package/src/defaults/skills/pptx/references/composition.md +4 -4
  28. package/src/defaults/skills/pptx/references/concepts.md +168 -0
  29. package/src/defaults/skills/pptx/references/direction.md +5 -4
  30. package/src/defaults/skills/pptx/references/html.md +6 -29
  31. package/src/defaults/skills/pptx/references/kit-index.md +11 -3
  32. package/src/defaults/skills/pptx/references/kit.md +27 -5
  33. package/src/defaults/skills/pptx/references/review.md +6 -5
  34. package/src/defaults/skills/setup/SKILL.md +5 -8
  35. package/src/defaults/skills/setup/references/actions.md +61 -31
  36. package/src/defaults/skills/setup/references/surfaces.md +12 -10
  37. package/src/defaults/skills/xlsx/SKILL.md +8 -6
  38. package/src/defaults/skills/xlsx/references/html.md +8 -4
  39. package/src/defaults/skills/xlsx/references/model-conventions.md +2 -0
  40. package/src/defaults/skills/xlsx/references/report-design.md +32 -16
  41. package/src/help.mjs +1 -1
  42. package/src/lib/rules-builder.cjs +1 -1
  43. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +3 -1
  44. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +4 -0
  45. package/src/runtime/agent/orchestrator/config-presets.d.mts +1 -0
  46. package/src/runtime/agent/orchestrator/config-presets.mjs +44 -0
  47. package/src/runtime/agent/orchestrator/config-providers-merge.mjs +9 -1
  48. package/src/runtime/agent/orchestrator/config.mjs +102 -21
  49. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +3 -2
  50. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +90 -26
  51. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +44 -14
  52. package/src/runtime/agent/orchestrator/providers/anthropic-midstream-recovery.mjs +4 -4
  53. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +5 -3
  54. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +79 -8
  55. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +29 -4
  56. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn.mjs +3 -0
  57. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +10 -1
  58. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +11 -2
  59. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +9 -5
  60. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +9 -1
  61. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +2 -0
  62. package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +30 -22
  63. package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +2 -0
  64. package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +28 -10
  65. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +14 -72
  66. package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +19 -3
  67. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +20 -10
  68. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-sink.mjs +36 -9
  69. package/src/runtime/agent/orchestrator/providers/cursor.mjs +11 -17
  70. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +388 -0
  71. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +92 -109
  72. package/src/runtime/agent/orchestrator/providers/custom-reasoning.mjs +107 -0
  73. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +138 -30
  74. package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +32 -15
  75. package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +2 -0
  76. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +39 -5
  77. package/src/runtime/agent/orchestrator/providers/gemini.mjs +9 -2
  78. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +3 -2
  79. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +6 -1
  80. package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +10 -1
  81. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +3 -2
  82. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +5 -1
  83. package/src/runtime/agent/orchestrator/providers/lib/note-error-usage.mjs +7 -0
  84. package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +57 -0
  85. package/src/runtime/agent/orchestrator/providers/model-cache.mjs +12 -5
  86. package/src/runtime/agent/orchestrator/providers/npm-cli-version.mjs +20 -15
  87. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +14 -9
  88. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +56 -11
  89. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-result.mjs +2 -1
  90. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +28 -1
  91. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +27 -3
  92. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +7 -5
  93. package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +12 -0
  94. package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +23 -8
  95. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +26 -6
  96. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-state.mjs +7 -1
  97. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +34 -15
  98. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +16 -2
  99. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +12 -2
  100. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +0 -6
  101. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +0 -19
  102. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +12 -43
  103. package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +6 -0
  104. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +39 -9
  105. package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog.mjs +31 -9
  106. package/src/runtime/agent/orchestrator/providers/openai-oauth-dispatch.mjs +13 -38
  107. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +5 -2
  108. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +40 -9
  109. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +7 -39
  110. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +28 -101
  111. package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +8 -4
  112. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +3 -6
  113. package/src/runtime/agent/orchestrator/providers/openai-service-tier.mjs +19 -0
  114. package/src/runtime/agent/orchestrator/providers/openai-startup-prewarm.mjs +5 -195
  115. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +9 -18
  116. package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +4 -66
  117. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state.mjs +11 -5
  118. package/src/runtime/agent/orchestrator/providers/openai-ws-send/acquire-attempt.mjs +3 -15
  119. package/src/runtime/agent/orchestrator/providers/openai-ws-send/attempt-request.mjs +6 -56
  120. package/src/runtime/agent/orchestrator/providers/openai-ws-send/send-context.mjs +1 -19
  121. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +2 -2
  122. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/handshake-failure.mjs +2 -1
  123. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/policy.mjs +3 -0
  124. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/stream-failure-stamps.mjs +0 -1
  125. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/stream-failure.mjs +3 -1
  126. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts.mjs +3 -5
  127. package/src/runtime/agent/orchestrator/providers/openai-ws-send-outcome.mjs +10 -89
  128. package/src/runtime/agent/orchestrator/providers/openai-ws-send-span.mjs +1 -1
  129. package/src/runtime/agent/orchestrator/providers/openai-ws-send-trace.mjs +0 -6
  130. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/terminal-frames.mjs +34 -11
  131. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +0 -1
  132. package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +3 -7
  133. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +44 -19
  134. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +40 -12
  135. package/src/runtime/agent/orchestrator/providers/quota-reading.mjs +70 -0
  136. package/src/runtime/agent/orchestrator/providers/registry.mjs +38 -3
  137. package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +4 -0
  138. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +1 -0
  139. package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +2 -0
  140. package/src/runtime/agent/orchestrator/providers/retry-midstream.mjs +11 -0
  141. package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +31 -41
  142. package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +5 -0
  143. package/src/runtime/agent/orchestrator/runtime-core/goal-mutations.mjs +0 -17
  144. package/src/runtime/agent/orchestrator/runtime-core/goal-state.mjs +0 -3
  145. package/src/runtime/agent/orchestrator/runtime-core/goal-store.mjs +0 -1
  146. package/src/runtime/agent/orchestrator/runtime-core/goal-text.mjs +1 -1
  147. package/src/runtime/agent/orchestrator/runtime-core/goal-tool-defs.mjs +1 -1
  148. package/src/runtime/agent/orchestrator/runtime-core/goal-tool-exec.mjs +1 -2
  149. package/src/runtime/agent/orchestrator/runtime-core/goal-turns.mjs +18 -4
  150. package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +8 -1
  151. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +7 -1
  152. package/src/runtime/agent/orchestrator/runtime-core/session-route-policy.mjs +16 -0
  153. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +2 -0
  154. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +6 -2
  155. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +7 -7
  156. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +6 -0
  157. package/src/runtime/agent/orchestrator/session/compact/observation-supersession.mjs +45 -2
  158. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +5 -3
  159. package/src/runtime/agent/orchestrator/session/eager-dispatch/entry.mjs +5 -0
  160. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +6 -13
  161. package/src/runtime/agent/orchestrator/session/loop/request-boundary.mjs +6 -2
  162. package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +114 -0
  163. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +2 -0
  164. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +5 -9
  165. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +51 -18
  166. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +7 -4
  167. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +3 -1
  168. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +0 -2
  169. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +6 -9
  170. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +63 -3
  171. package/src/runtime/agent/orchestrator/session/manager/session-owner-liveness.mjs +10 -28
  172. package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +4 -4
  173. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +13 -0
  174. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +10 -19
  175. package/src/runtime/agent/orchestrator/session/tool-batch/execute-call.mjs +7 -1
  176. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +1 -0
  177. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +1 -3
  178. package/src/runtime/agent/orchestrator/stall-policy.mjs +0 -1
  179. package/src/runtime/agent/orchestrator/tools/builtin/atomic-write.mjs +18 -8
  180. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +19 -20
  181. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +6 -11
  182. package/src/runtime/agent/orchestrator/tools/builtin/file-version-test-helpers.mjs +11 -0
  183. package/src/runtime/agent/orchestrator/tools/builtin/file-version.mjs +13 -0
  184. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-single-file-rescue.mjs +8 -5
  185. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-task-output.mjs +25 -12
  186. package/src/runtime/agent/orchestrator/tools/builtin/raw-content-publication.mjs +17 -0
  187. package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +136 -76
  188. package/src/runtime/agent/orchestrator/tools/builtin/read-single-render.mjs +15 -17
  189. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +4 -2
  190. package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +11 -10
  191. package/src/runtime/agent/orchestrator/tools/builtin/read-source-windows.mjs +5 -3
  192. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +30 -16
  193. package/src/runtime/agent/orchestrator/tools/builtin/shell-lossless-compact.mjs +2 -2
  194. package/src/runtime/agent/orchestrator/tools/builtin/snapshot-helpers.mjs +4 -11
  195. package/src/runtime/agent/orchestrator/tools/builtin/snapshot-store.mjs +7 -3
  196. package/src/runtime/agent/orchestrator/tools/builtin/snapshot-validation.mjs +16 -19
  197. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
  198. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background.mjs +4 -1
  199. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +14 -4
  200. package/src/runtime/agent/orchestrator/tools/patch/apply-patch/codex-batch.mjs +58 -8
  201. package/src/runtime/agent/orchestrator/tools/patch/content-guard.mjs +47 -0
  202. package/src/runtime/agent/orchestrator/tools/patch/delete-quarantine.mjs +155 -0
  203. package/src/runtime/agent/orchestrator/tools/patch/dispatch/native-proofs.mjs +6 -2
  204. package/src/runtime/agent/orchestrator/tools/patch/dispatch/native-snapshots.mjs +6 -2
  205. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +19 -16
  206. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +13 -44
  207. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +2 -2
  208. package/src/runtime/agent/orchestrator/tools/patch/post-patch-excerpts.mjs +5 -2
  209. package/src/runtime/agent/orchestrator/tools/patch/replay-capture.mjs +47 -7
  210. package/src/runtime/agent/orchestrator/tools/patch/rollback-state.mjs +72 -8
  211. package/src/runtime/agent/orchestrator/tools/patch/section-coalesce.mjs +3 -2
  212. package/src/runtime/agent/orchestrator/tools/patch/sequence/run.mjs +9 -1
  213. package/src/runtime/agent/orchestrator/tools/patch/sequence/units.mjs +9 -1
  214. package/src/runtime/agent/orchestrator/tools/patch/sequence.mjs +6 -6
  215. package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +16 -4
  216. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +35 -14
  217. package/src/runtime/agent/orchestrator/tools/patch/wave.mjs +42 -4
  218. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +2 -0
  219. package/src/runtime/agent/orchestrator/tools/shell-codepage.mjs +296 -0
  220. package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +75 -24
  221. package/src/runtime/browser-bridge/tool-defs.mjs +1 -0
  222. package/src/runtime/channels/lib/config.mjs +3 -8
  223. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +7 -3
  224. package/src/{tui → runtime/channels}/lib/voice-setup.mjs +38 -22
  225. package/src/runtime/channels/lib/webhook/request-handler.mjs +42 -8
  226. package/src/runtime/computer-bridge/actions.mjs +2 -0
  227. package/src/runtime/effort-judge/judge-client.mjs +125 -25
  228. package/src/runtime/effort-judge/judge-input.mjs +96 -0
  229. package/src/runtime/effort-judge/judge-worker.mjs +52 -20
  230. package/src/runtime/effort-judge/model-install.mjs +74 -0
  231. package/src/runtime/effort-judge/model-manifest.json +21 -0
  232. package/src/runtime/effort-judge/previous-human-request.mjs +162 -0
  233. package/src/runtime/effort-judge/verified-context-input.mjs +10 -0
  234. package/src/runtime/github/tool.mjs +214 -14
  235. package/src/runtime/local-provider/context-settings.mjs +15 -6
  236. package/src/runtime/local-provider/gguf-header.mjs +13 -5
  237. package/src/runtime/local-provider/hugging-face.mjs +27 -5
  238. package/src/runtime/local-provider/install-progress.mjs +7 -0
  239. package/src/runtime/local-provider/managed-runtime.mjs +2 -1
  240. package/src/runtime/local-provider/model-maintenance.mjs +15 -6
  241. package/src/runtime/local-provider/resumable-installations.mjs +40 -1
  242. package/src/runtime/local-provider/server.mjs +2 -0
  243. package/src/runtime/memory/lib/embedding-provider.mjs +12 -0
  244. package/src/runtime/memory/lib/http-router/session-routes.mjs +2 -1
  245. package/src/runtime/memory/lib/memory-config-flags.mjs +3 -2
  246. package/src/runtime/memory/lib/memory-embed.mjs +5 -0
  247. package/src/runtime/memory/lib/pg/process.mjs +57 -5
  248. package/src/runtime/office/README.md +13 -7
  249. package/src/runtime/office/authoring/docx-html-build.mjs +4 -4
  250. package/src/runtime/office/authoring/docx-html-measure.mjs +60 -0
  251. package/src/runtime/office/authoring/pdf-author-action.mjs +6 -1
  252. package/src/runtime/office/authoring/pptx-brief.mjs +40 -1
  253. package/src/runtime/office/authoring/pptx-html-build.mjs +1 -1
  254. package/src/runtime/office/authoring/pptx-html-measure.mjs +1 -1
  255. package/src/runtime/office/bench/deck-bench.mjs +1 -1
  256. package/src/runtime/office/com/office-com-host.ps1 +8 -0
  257. package/src/runtime/office/core/office-actions-render.mjs +2 -0
  258. package/src/runtime/office/design/design-art-direction.mjs +10 -2
  259. package/src/runtime/office/design/design-discipline.mjs +4 -5
  260. package/src/runtime/office/design/design-tokens.mjs +20 -1
  261. package/src/runtime/office/design/docx/design-docx/front-matter.mjs +7 -4
  262. package/src/runtime/office/design/docx/design-docx/section.mjs +3 -1
  263. package/src/runtime/office/design/docx/design-docx/writer.mjs +1 -0
  264. package/src/runtime/office/design/docx/design-docx-components.mjs +19 -15
  265. package/src/runtime/office/design/docx/design-docx.mjs +5 -6
  266. package/src/runtime/office/design/docx/document-typography.mjs +20 -6
  267. package/src/runtime/office/design/xlsx/design-xlsx.mjs +9 -2
  268. package/src/runtime/office/pdf/document-preview.mjs +133 -12
  269. package/src/runtime/office/pdf/pdf-writer.mjs +56 -22
  270. package/src/runtime/office/portable/portable-snapshot-docx.mjs +46 -3
  271. package/src/runtime/office/portable/portable-snapshot-shared.mjs +27 -1
  272. package/src/runtime/office/quality/assurance-rendered.mjs +21 -5
  273. package/src/runtime/office/quality/assurance-structure-docx.mjs +41 -9
  274. package/src/runtime/office/quality/design-aesthetics.mjs +4 -2
  275. package/src/runtime/office/quality/design-deck-diversity.mjs +15 -3
  276. package/src/runtime/office/quality/design-review.mjs +13 -5
  277. package/src/runtime/office/quality/document-brief.mjs +8 -2
  278. package/src/runtime/office/quality/forced-break-pages.mjs +203 -0
  279. package/src/runtime/office/quality/pptx-deck-rubric.mjs +21 -3
  280. package/src/runtime/office/quality/quality-pipeline.mjs +14 -17
  281. package/src/runtime/session-search/session-search-db.mjs +183 -0
  282. package/src/runtime/session-search/session-search-ingest.mjs +106 -0
  283. package/src/runtime/shared/agent-route-config.mjs +50 -2
  284. package/src/runtime/shared/bounded-file-read.mjs +166 -0
  285. package/src/runtime/shared/builtin-grandfathering.mjs +37 -0
  286. package/src/runtime/shared/config.mjs +24 -4
  287. package/src/runtime/shared/defaults-separation.mjs +263 -0
  288. package/src/runtime/shared/error-presentation.mjs +8 -1
  289. package/src/runtime/shared/llm/anthropic-effort.mjs +4 -0
  290. package/src/runtime/shared/llm/cost.mjs +4 -2
  291. package/src/runtime/shared/llm/model-catalog-projection.mjs +1 -1
  292. package/src/runtime/shared/llm/model-catalog.mjs +25 -0
  293. package/src/runtime/shared/llm/reasoning-usage.mjs +1 -2
  294. package/src/runtime/shared/llm/usage-accounting.mjs +14 -4
  295. package/src/runtime/shared/llm/usage-context.mjs +11 -1
  296. package/src/runtime/shared/llm/usage-ledger-repair.mjs +3 -1
  297. package/src/runtime/shared/llm/usage-ledger-rollup.mjs +105 -1
  298. package/src/runtime/shared/llm/usage-ledger-worker.mjs +2 -1
  299. package/src/runtime/shared/llm/usage-ledger.mjs +72 -5
  300. package/src/runtime/shared/onnx-runtime-support.mjs +6 -0
  301. package/src/runtime/shared/orchestration.mjs +14 -6
  302. package/src/runtime/shared/pristine-execution-contract.json +7 -2
  303. package/src/runtime/shared/projects.mjs +65 -48
  304. package/src/runtime/shared/provider-accounts.mjs +47 -6
  305. package/src/runtime/shared/quota-resume-store.mjs +255 -0
  306. package/src/runtime/shared/shipped-definitions.mjs +275 -0
  307. package/src/runtime/shared/tool-card-model/agent-surface.mjs +30 -9
  308. package/src/runtime/shared/tool-card-model/aggregate-card.mjs +7 -3
  309. package/src/runtime/shared/tool-card-model/background-task.mjs +17 -1
  310. package/src/runtime/shared/tool-card-model/card-detail.mjs +10 -4
  311. package/src/runtime/shared/tool-card-model/card-header.mjs +10 -4
  312. package/src/runtime/shared/tool-card-model/card-status.mjs +22 -5
  313. package/src/runtime/shared/tool-card-model/displayed-result.mjs +6 -1
  314. package/src/runtime/shared/tool-card-model/media-result.mjs +53 -0
  315. package/src/runtime/shared/tool-card-model/terminal-status.mjs +17 -1
  316. package/src/runtime/shared/tool-card-model.golden-cases.mjs +1 -1
  317. package/src/runtime/shared/tool-card-model.golden.json +75 -66
  318. package/src/runtime/shared/tool-card-model.mjs +1 -0
  319. package/src/runtime/shared/tool-output-limit.mjs +3 -0
  320. package/src/runtime/shared/tool-primitives.mjs +24 -18
  321. package/src/runtime/shared/tool-result-summary.mjs +128 -17
  322. package/src/runtime/shared/tool-status.mjs +20 -7
  323. package/src/runtime/shared/tool-surface.mjs +105 -28
  324. package/src/runtime/shared/tool-work-units.mjs +221 -18
  325. package/src/runtime/shared/transcript-metadata.mjs +2 -0
  326. package/src/runtime/shared/turn-review-reply.mjs +2 -0
  327. package/src/runtime/shared/turn-snapshot.mjs +65 -6
  328. package/src/runtime/shared/turn-worktree-snapshot.mjs +202 -22
  329. package/src/runtime/shared/worker-requests.mjs +14 -2
  330. package/src/runtime/terminal-bridge/client.mjs +311 -0
  331. package/src/runtime/terminal-bridge/tool-defs.mjs +36 -0
  332. package/src/runtime/web-search/lib/ssrf-guard.mjs +12 -56
  333. package/src/runtime/web-search/tool-defs.mjs +2 -0
  334. package/src/session-runtime/boot/apis.mjs +30 -5
  335. package/src/session-runtime/boot/config.mjs +4 -0
  336. package/src/session-runtime/boot/facade.mjs +4 -1
  337. package/src/session-runtime/boot/infrastructure.mjs +21 -2
  338. package/src/session-runtime/boot/session.mjs +5 -5
  339. package/src/session-runtime/boot/tools.mjs +2 -0
  340. package/src/session-runtime/builtin-features.d.mts +1 -0
  341. package/src/session-runtime/channel-config-api.mjs +5 -0
  342. package/src/session-runtime/config-lifecycle/config-writers.mjs +3 -0
  343. package/src/session-runtime/config-lifecycle.mjs +31 -6
  344. package/src/session-runtime/goal-facade-api.mjs +4 -0
  345. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +15 -3
  346. package/src/session-runtime/lifecycle/inheritance.mjs +28 -6
  347. package/src/session-runtime/lifecycle/session-switch/context-switch.mjs +3 -0
  348. package/src/session-runtime/lifecycle/session-switch/resume.mjs +7 -1
  349. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +9 -0
  350. package/src/session-runtime/lifecycle/teardown/session-close.mjs +14 -2
  351. package/src/session-runtime/lifecycle/teardown.mjs +5 -0
  352. package/src/session-runtime/local-provider-settings.mjs +10 -0
  353. package/src/session-runtime/media-api.mjs +32 -22
  354. package/src/session-runtime/model-recency.mjs +14 -0
  355. package/src/session-runtime/model-route/route-persist.mjs +17 -2
  356. package/src/session-runtime/model-route/route-tuning.mjs +16 -5
  357. package/src/session-runtime/model-route/set-route.mjs +55 -22
  358. package/src/session-runtime/model-route/web-search-route.mjs +2 -2
  359. package/src/session-runtime/model-route-api.mjs +2 -1
  360. package/src/session-runtime/notification-bus.mjs +31 -6
  361. package/src/session-runtime/provider-auth/login.mjs +6 -0
  362. package/src/session-runtime/provider-models/catalog-load.mjs +3 -1
  363. package/src/session-runtime/provider-models/row-hydration.mjs +36 -10
  364. package/src/session-runtime/quick-model-rows/model-meta.mjs +26 -0
  365. package/src/session-runtime/quick-model-rows/provider-rows.mjs +40 -14
  366. package/src/session-runtime/resource-api.mjs +8 -1
  367. package/src/session-runtime/route-state.mjs +9 -0
  368. package/src/session-runtime/runtime-core.mjs +4 -0
  369. package/src/session-runtime/runtime-facade.mjs +20 -1
  370. package/src/session-runtime/runtime-feature-gates.mjs +2 -0
  371. package/src/session-runtime/runtime-review-api.mjs +12 -1
  372. package/src/session-runtime/services/agent-tool/helpers.mjs +14 -1
  373. package/src/session-runtime/services/agent-tool/index-write-queue.mjs +10 -15
  374. package/src/session-runtime/services/agent-tool/lead-worker-index/reap-timers.mjs +6 -1
  375. package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +20 -2
  376. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +14 -6
  377. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +2 -0
  378. package/src/session-runtime/services/agent-tool/tag-registry.mjs +12 -1
  379. package/src/session-runtime/services/agent-tool/worker-index.mjs +17 -1
  380. package/src/session-runtime/services/agent-tool.mjs +1 -0
  381. package/src/session-runtime/services/channel-admin.mjs +87 -13
  382. package/src/session-runtime/services/provider-admin.mjs +306 -66
  383. package/src/session-runtime/services/seeds.mjs +73 -142
  384. package/src/session-runtime/services/usage-dashboard-model.mjs +29 -0
  385. package/src/session-runtime/services/usage-dashboard.mjs +7 -0
  386. package/src/session-runtime/session-address.mjs +125 -0
  387. package/src/session-runtime/session-create.mjs +57 -2
  388. package/src/session-runtime/settings-api.mjs +1 -0
  389. package/src/session-runtime/settings-builtin-tools-api.mjs +15 -10
  390. package/src/session-runtime/settings-compaction-api.mjs +13 -0
  391. package/src/session-runtime/setup-tool/executor.mjs +93 -40
  392. package/src/session-runtime/setup-tool/extended-actions.mjs +64 -5
  393. package/src/session-runtime/setup-tool/settings-contract.mjs +46 -3
  394. package/src/session-runtime/setup-tool/tool-defs.mjs +14 -6
  395. package/src/session-runtime/tool-surface.mjs +16 -9
  396. package/src/session-runtime/turn/session-ops.mjs +3 -0
  397. package/src/session-runtime/turn/share-ops.mjs +4 -1
  398. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +6 -0
  399. package/src/session-runtime/usage-stats-api.mjs +43 -3
  400. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +31 -5
  401. package/src/session-runtime/workflow-agents-api/agent-route.mjs +6 -2
  402. package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +27 -7
  403. package/src/standalone/daemon-boot-coordinator.mjs +10 -0
  404. package/src/standalone/daemon-desktop-runtime.mjs +1 -0
  405. package/src/standalone/daemon-drain-policy.mjs +9 -0
  406. package/src/standalone/daemon-stored-session-views.mjs +7 -8
  407. package/src/standalone/daemon.mjs +36 -8
  408. package/src/standalone/local-session-runtime.mjs +11 -0
  409. package/src/standalone/session-client.mjs +52 -7
  410. package/src/standalone/session-protocol.mjs +9 -0
  411. package/src/standalone/session-service/agent-tree/agent-child.mjs +2 -0
  412. package/src/standalone/session-service/entries/lifecycle.mjs +2 -0
  413. package/src/standalone/session-service/entries/session-loads.mjs +9 -0
  414. package/src/standalone/session-service/projection/revisions.mjs +28 -4
  415. package/src/standalone/session-service/projection.mjs +1 -0
  416. package/src/standalone/session-service/quota-resume.mjs +140 -0
  417. package/src/standalone/session-service/session-calls/session-action.mjs +2 -2
  418. package/src/standalone/session-service/session-calls.mjs +10 -2
  419. package/src/standalone/session-service/stored-reader.mjs +26 -1
  420. package/src/standalone/session-service.mjs +21 -0
  421. package/src/standalone/session-transport/routes.mjs +2 -0
  422. package/src/tui/app/doctor.mjs +7 -11
  423. package/src/tui/app/maintenance-pickers/auto-clear-picker.mjs +3 -5
  424. package/src/tui/app/model-picker/model-footer.mjs +2 -2
  425. package/src/tui/app/model-picker/route-selection/fast-parameters.mjs +21 -2
  426. package/src/tui/app/model-picker/route-selection.mjs +6 -1
  427. package/src/tui/app/settings-picker/settings-rows.mjs +4 -6
  428. package/src/tui/app/settings-picker/settings-toggles/store-toggles.mjs +1 -1
  429. package/src/tui/app/slash-commands.mjs +1 -1
  430. package/src/tui/app/slash-dispatch/route-commands.mjs +5 -3
  431. package/src/tui/app/transcript-row-estimate.mjs +17 -5
  432. package/src/tui/components/ContextPanel.jsx +5 -1
  433. package/src/tui/components/Spinner.jsx +34 -319
  434. package/src/tui/components/SpinnerByline.jsx +256 -0
  435. package/src/tui/components/tool-execution/aggregate-card.jsx +4 -2
  436. package/src/tui/components/tool-execution/text-format.mjs +1 -0
  437. package/src/tui/components/use-spinner-progress.mjs +113 -0
  438. package/src/tui/dist/index.mjs +15896 -14452
  439. package/src/tui/session/agent-job-feed/execution-delivery.mjs +13 -1
  440. package/src/tui/session/agent-job-feed/notification-router.mjs +3 -0
  441. package/src/tui/session/agent-job-feed/pending-resume.mjs +14 -1
  442. package/src/tui/session/agent-job-feed.mjs +4 -0
  443. package/src/tui/session/context-state.mjs +13 -1
  444. package/src/tui/session/initial-state.mjs +6 -0
  445. package/src/tui/session/quota-resume.mjs +263 -0
  446. package/src/tui/session/session-api/intake/abort.mjs +39 -35
  447. package/src/tui/session/session-api/intake/submission.mjs +13 -21
  448. package/src/tui/session/session-api/intake.mjs +3 -3
  449. package/src/tui/session/session-api/integrations.mjs +5 -1
  450. package/src/tui/session/session-api/lifecycle/dispose.mjs +1 -0
  451. package/src/tui/session/session-api/lifecycle/inheritance.mjs +2 -2
  452. package/src/tui/session/session-api/lifecycle/session-reset.mjs +4 -0
  453. package/src/tui/session/session-api/lifecycle.mjs +71 -2
  454. package/src/tui/session/session-api/media.mjs +2 -2
  455. package/src/tui/session/session-api/quota-resume.mjs +21 -0
  456. package/src/tui/session/session-api/routes.mjs +5 -2
  457. package/src/tui/session/session-api/settings.mjs +2 -0
  458. package/src/tui/session/session-api.mjs +2 -0
  459. package/src/tui/session/session-flow/auto-clear.mjs +14 -6
  460. package/src/tui/session/session-flow/drain.mjs +52 -5
  461. package/src/tui/session/session-flow/queue/take-entries.mjs +9 -4
  462. package/src/tui/session/session-flow/queue.mjs +4 -0
  463. package/src/tui/session/session-flow/steering.mjs +11 -0
  464. package/src/tui/session/session-flow.mjs +1 -0
  465. package/src/tui/session/tool-approval.mjs +7 -2
  466. package/src/tui/session/transcript-intake.mjs +1 -0
  467. package/src/tui/session/turn-tool-cards.mjs +4 -6
  468. package/src/tui/session/turn.mjs +40 -4
  469. package/src/vendor/statusline/bin/statusline-route.mjs +3 -2
  470. package/src/vendor/statusline/src/gateway/route-meta.mjs +1 -1
  471. package/src/runtime/agent/orchestrator/providers/openai-ws-warmup.mjs +0 -165
@@ -21,6 +21,8 @@ export function createContextSwitch(deps, { ingestSessionIntoMemory, closeSurfac
21
21
  } = deps;
22
22
 
23
23
  async function switchContext({ cwd, desktopSession: nextDesktopSession, forResume = false } = {}) {
24
+ // Leaving the open conversation would re-key an addressed runtime.
25
+ deps.assertNotAddressed?.('switchContext');
24
26
  clearRoutePreparation?.();
25
27
  const session = getSession();
26
28
  if (session?.id) {
@@ -55,6 +57,7 @@ export function createContextSwitch(deps, { ingestSessionIntoMemory, closeSurfac
55
57
  }
56
58
 
57
59
  async function newSession() {
60
+ deps.assertNotAddressed?.('newSession');
58
61
  const session = getSession();
59
62
  if (session?.id) {
60
63
  void ingestSessionIntoMemory(session);
@@ -5,7 +5,10 @@
5
5
  */
6
6
  import { writeStatuslineRoute } from '../../statusline-route.mjs';
7
7
  import { clean, hasOwn } from '../../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
8
- import { toolSpecForMode, deferredSurfaceModeForLead } from '../../../runtime/agent/orchestrator/runtime-core/effort.mjs';
8
+ import {
9
+ toolSpecForMode,
10
+ deferredSurfaceModeForLead,
11
+ } from '../../../runtime/agent/orchestrator/runtime-core/effort.mjs';
9
12
  import { isScratchConversation } from '../shared.mjs';
10
13
 
11
14
  export function resolveResumeCwd(session, currentCwd) {
@@ -97,6 +100,9 @@ export function createSessionResume(deps, { ingestSessionIntoMemory, closeSurfac
97
100
  }
98
101
 
99
102
  async function resume(id) {
103
+ // An addressed runtime may only reopen its own conversation; checked
104
+ // before anything is retired or closed.
105
+ deps.assertResumeTarget?.(id);
100
106
  clearRoutePreparation?.();
101
107
  const prev = getSession();
102
108
  // Captured before the manager reopens anything: the scratch decision for
@@ -4,6 +4,7 @@
4
4
  * owns (shared with the context switch).
5
5
  */
6
6
  import { clean } from '../../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
7
+ import { forgetSessionSearch } from '../../../runtime/session-search/session-search-ingest.mjs';
7
8
  import { listLeadSessions } from '../session-catalog.mjs';
8
9
  import { SESSION_ID_PATTERN } from '../shared.mjs';
9
10
 
@@ -58,6 +59,7 @@ export function createSessionDelete(deps, { cancelBackgroundTasks }) {
58
59
  if (current?.id !== sessionId) {
59
60
  const deleted = mgr.deleteSession(sessionId) === true;
60
61
  if (!deleted) return false;
62
+ void forgetSessionSearch(sessionId);
61
63
  // The parent is already irreversibly gone, so child cleanup is
62
64
  // best-effort and idempotent. Any vetoed child becomes sweep-eligible
63
65
  // because its retained-parent proof disappeared with the parent.
@@ -74,6 +76,7 @@ export function createSessionDelete(deps, { cancelBackgroundTasks }) {
74
76
  // immediately would let a late provider/save continuation resurrect the
75
77
  // deleted conversation after the user has moved to its replacement.
76
78
  if (mgr.closeSession(sessionId, cleanupReason, { tombstone: true }) !== true) return false;
79
+ void forgetSessionSearch(sessionId);
77
80
  // Active parent deletion uses the same durable tombstone boundary for
78
81
  // every linked child. Their files then mature with the parent instead of
79
82
  // disappearing while the parent task is still retained.
@@ -85,6 +88,12 @@ export function createSessionDelete(deps, { cancelBackgroundTasks }) {
85
88
  setSession(null);
86
89
  invalidateContextStatusCache();
87
90
  invalidatePreSessionToolSurface();
91
+ // An addressed runtime never mints a replacement under the deleted
92
+ // address: it becomes terminal and a new conversation needs a new entry.
93
+ if (deps.getAddressedSessionId?.()) {
94
+ deps.markAddressTerminal?.();
95
+ return true;
96
+ }
88
97
  await createCurrentSession();
89
98
  return true;
90
99
  }
@@ -63,10 +63,22 @@ export function closeOwnSession(deps, closeSurfaceSession, reason) {
63
63
  /** Plant the ordinary manager/store tombstone barrier for a canonical
64
64
  * child session without running whole-process teardown. */
65
65
  export function closeCanonicalSession(deps, reason = 'canonical-session-close') {
66
- const { getSession, setSession, mgr, invalidateContextStatusCache } = deps;
66
+ const { getSession, setSession, mgr, invalidateContextStatusCache, beginCanonicalClose, finishCanonicalClose } = deps;
67
+ // Already terminal: closing is idempotent and keeps the address.
68
+ if (deps.isCanonicallyClosed?.() === true) return true;
67
69
  const session = getSession?.();
68
70
  if (!session?.id || session.remoteAttached === true) return false;
69
- const closed = mgr.closeSession(session.id, reason, { tombstone: true }) === true;
71
+ // Fence execution admission BEFORE the manager close: that close cancels the
72
+ // session's shells synchronously, and their completions must not be able to
73
+ // enqueue model work or create a conversation.
74
+ const fence = beginCanonicalClose?.(session.id);
75
+ let closed = false;
76
+ try {
77
+ closed = mgr.closeSession(session.id, reason, { tombstone: true }) === true;
78
+ } finally {
79
+ // Terminal after a durable close; admission restored when it failed.
80
+ finishCanonicalClose?.(fence, closed);
81
+ }
70
82
  if (!closed) return false;
71
83
  setSession?.(null);
72
84
  invalidateContextStatusCache?.();
@@ -165,6 +165,11 @@ export function createTeardown(deps, { ingestSessionIntoMemory, closeSurfaceSess
165
165
  agentTool.closeAll(reason, scope.scopedTeardown ? { callerSessionId: scope.closingSessionId } : {});
166
166
  } catch {}
167
167
  }
168
+ // Release the worker/lead index exit registrations (persists pending rows
169
+ // first). Idempotent, so repeated closes of one runtime dispose once.
170
+ try {
171
+ agentTool.dispose?.({ settle: scope.teardownReapsWork === true });
172
+ } catch {}
168
173
  const { ok, stops } = await inOwnLoopTurn(() => {
169
174
  const runtimeStops = startRuntimeStops(deps, reason, scope);
170
175
  const closed = closeOwnSession(deps, closeSurfaceSession, reason);
@@ -10,6 +10,7 @@ export function createLocalProviderSettings({
10
10
  prepareLocalProviderModel,
11
11
  refreshLocalProviderCatalog,
12
12
  cancelLocalProviderInstallation,
13
+ discardLocalProviderInstallation,
13
14
  configureLocalProviderIdleTtl,
14
15
  }) {
15
16
  let commandError = null;
@@ -52,6 +53,15 @@ export function createLocalProviderSettings({
52
53
  cancelLocalProviderInstallation(jobId);
53
54
  return this.getToolModuleSettings();
54
55
  },
56
+ discardLocalProviderInstallation(phase, modelId) {
57
+ if (!['runtime', 'model', 'verify', 'repair'].includes(phase))
58
+ throw new TypeError('phase must be runtime, model, verify or repair.');
59
+ if (phase !== 'runtime' && (typeof modelId !== 'string' || !modelId.trim()))
60
+ throw new TypeError('modelId is required.');
61
+ discardLocalProviderInstallation(phase, phase === 'runtime' ? null : modelId);
62
+ commandError = null;
63
+ return this.getToolModuleSettings();
64
+ },
55
65
  setLocalProviderIdleTtl(seconds) {
56
66
  const idleTtlSeconds = localIdleTtlSeconds(seconds);
57
67
  const config = getConfig();
@@ -4,33 +4,43 @@
4
4
  * The media graph (lane catalog, adapters, asset store) is imported on first
5
5
  * use: a session that never opens the Studio must not pay for it at boot.
6
6
  */
7
- let laneModule = null;
8
- let jobModule = null;
9
- let storeModule = null;
10
-
11
- async function lanes() {
12
- laneModule ??= await import('../runtime/media/lanes.mjs');
13
- return laneModule;
14
- }
15
-
16
- async function jobs() {
17
- jobModule ??= await import('../runtime/media/jobs.mjs');
18
- return jobModule;
19
- }
20
-
21
- async function store() {
22
- storeModule ??= await import('../runtime/media/store.mjs');
23
- return storeModule;
7
+ // Each loader keeps its literal import() so bundlers can trace it; only a
8
+ // resolved module is cached, so a failed import is retried on the next call.
9
+ function lazy(load) {
10
+ let module = null;
11
+ return async () => {
12
+ module ??= await load();
13
+ return module;
14
+ };
24
15
  }
25
16
 
26
- let defaultsModule = null;
27
- async function defaults() {
28
- defaultsModule ??= await import('../runtime/media/defaults.mjs');
29
- return defaultsModule;
30
- }
17
+ const lanes = lazy(() => import('../runtime/media/lanes.mjs'));
18
+ const jobs = lazy(() => import('../runtime/media/jobs.mjs'));
19
+ const store = lazy(() => import('../runtime/media/store.mjs'));
20
+ const defaults = lazy(() => import('../runtime/media/defaults.mjs'));
21
+ const voice = lazy(() => import('../runtime/channels/lib/voice-setup.mjs'));
31
22
 
32
23
  export function createMediaApi() {
33
24
  return {
25
+ /** Read-only voice state: `voice.enabled` plus the managed whisper/ffmpeg
26
+ * runtime on disk. */
27
+ async getVoiceStatus() {
28
+ return (await voice()).getVoiceStatus();
29
+ },
30
+ /** Same toggle as the session `toggleVoice` action: enabling installs any
31
+ * missing whisper/ffmpeg component first. `error` carries the last
32
+ * warning or failure the toggle reported. */
33
+ async setVoiceEnabled(enabled) {
34
+ const module = await voice();
35
+ let error = null;
36
+ await module.toggleVoice({
37
+ enabled,
38
+ pushNotice: (text, level) => {
39
+ if (level === 'warn' || level === 'error') error = text;
40
+ },
41
+ });
42
+ return { status: await module.getVoiceStatus(), error };
43
+ },
34
44
  async listMediaLanes() {
35
45
  return (await lanes()).listMediaLanes();
36
46
  },
@@ -3,6 +3,19 @@
3
3
  // injected route provider (for sort priority) and webSearchCapableFor predicate.
4
4
  import { clean } from '../runtime/agent/orchestrator/runtime-core/session-text.mjs';
5
5
  import { contextWindowRange } from '../runtime/shared/llm/default-context-window.mjs';
6
+ import { isCustomProviderName } from '../runtime/agent/orchestrator/providers/custom-reasoning.mjs';
7
+
8
+ // A custom provider's effective row fields, carried when the row states them
9
+ // (explicit false included); first-party rows never carry them.
10
+ function customRowFields(name, m) {
11
+ if (!isCustomProviderName(name)) return {};
12
+ return {
13
+ ...(typeof m.name === 'string' && m.name ? { name: m.name } : {}),
14
+ ...(typeof m.hidden === 'boolean' ? { hidden: m.hidden } : {}),
15
+ ...(typeof m.manual === 'boolean' ? { manual: m.manual } : {}),
16
+ ...(m.customReasoning && typeof m.customReasoning === 'object' ? { customReasoning: m.customReasoning } : {}),
17
+ };
18
+ }
6
19
 
7
20
  function parsedProviderModelVersion(id) {
8
21
  const text = clean(id).toLowerCase();
@@ -148,5 +161,6 @@ export function providerModelCacheRow(name, m, webSearchCapableFor) {
148
161
  defaultFast: m.defaultFast === true,
149
162
  supportsMaxMode: m.supportsMaxMode === true,
150
163
  mode: m.mode || null,
164
+ ...customRowFields(name, m),
151
165
  };
152
166
  }
@@ -7,6 +7,13 @@ import { saveModelSettings } from '../../runtime/agent/orchestrator/runtime-core
7
7
  import { writeStatuslineRoute } from '../statusline-route.mjs';
8
8
  import { sessionUsesRoute } from '../../runtime/agent/orchestrator/runtime-core/session-route-policy.mjs';
9
9
 
10
+ /** Re-tuning the live model (effort, Fast, context, or re-picking it) is not a
11
+ * main-model choice: new tasks keep their main model unless it is that model. */
12
+ function tunesNonMainModel(liveRoute, route, mainRoute) {
13
+ const sameModel = (other) => other?.provider === route.provider && other?.model === route.model;
14
+ return sameModel(liveRoute) && !sameModel(mainRoute);
15
+ }
16
+
10
17
  export function createRoutePersistence(deps) {
11
18
  const {
12
19
  getConfig,
@@ -18,6 +25,7 @@ export function createRoutePersistence(deps) {
18
25
  adoptConfig,
19
26
  saveConfigAndAdopt,
20
27
  persistLeadRoute,
28
+ resolveRoute,
21
29
  invalidateContextStatusCache,
22
30
  } = deps;
23
31
  return {
@@ -27,11 +35,14 @@ export function createRoutePersistence(deps) {
27
35
  hasSecrets: getConfigHasSecrets(),
28
36
  });
29
37
  },
30
- persistAdoptedModelSettings(route) {
38
+ persistAdoptedModelSettings(route, { keepMainModel = false } = {}) {
31
39
  // saveModelSettings is in-memory only. persistLeadRoute debounce-writes
32
40
  // the adopted config (including modelSettings). If the lead preset cannot
33
41
  // be normalized, still debounce-persist so effort/fast are not memory-only.
34
- const leadRoute = persistLeadRoute(route);
42
+ const leadRoute =
43
+ keepMainModel || tunesNonMainModel(getRoute(), route, resolveRoute(getConfig(), {}))
44
+ ? null
45
+ : persistLeadRoute(route);
35
46
  if (!leadRoute) saveConfigAndAdopt(getConfig());
36
47
  return leadRoute;
37
48
  },
@@ -42,6 +53,10 @@ export function createRoutePersistence(deps) {
42
53
  if (!sessionUsesRoute(session, route)) return;
43
54
  session.fast = route.fast === true;
44
55
  session.effort = route.effectiveEffort || null;
56
+ // The request path reads session.modelParameters; keep only serviceTier in step.
57
+ const { serviceTier: _previous, ...otherParameters } = session.modelParameters || {};
58
+ const serviceTier = route.modelParameters?.serviceTier;
59
+ session.modelParameters = serviceTier ? { ...otherParameters, serviceTier } : otherParameters;
45
60
  writeStatuslineRoute(statusRoutes, session, route);
46
61
  invalidateContextStatusCache();
47
62
  },
@@ -2,7 +2,8 @@
2
2
  * route-tuning.mjs — fast and effort changes on the current main route.
3
3
  */
4
4
  import { normalizeEffortInput } from '../../runtime/agent/orchestrator/runtime-core/effort.mjs';
5
- import { fastCapableFor } from '../../runtime/agent/orchestrator/runtime-core/model-capabilities.mjs';
5
+ import { selectionFastCapable } from '../route-state.mjs';
6
+ import { ultrafastCapableFor } from '../../runtime/agent/orchestrator/runtime-core/model-capabilities.mjs';
6
7
  import { workflowPresetId } from '../../runtime/agent/orchestrator/runtime-core/workflow.mjs';
7
8
 
8
9
  export function createRouteTuning(deps, persist) {
@@ -19,9 +20,19 @@ export function createRouteTuning(deps, persist) {
19
20
  }
20
21
 
21
22
  async function setFast(value) {
22
- const enabled = value === true;
23
+ const ultra = value === 'ultrafast';
24
+ const enabled = value === true || ultra;
23
25
  const modelMeta = await lookupModelMeta(getRoute().provider, getRoute().model);
24
- const fastCapable = fastCapableFor(
26
+ // lookupModelMeta may answer with the provider's raw (unhydrated) model, so
27
+ // ask the capability directly instead of the row's serviceTier option.
28
+ if (ultra && !ultrafastCapableFor(getRoute().provider, modelMeta)) {
29
+ throw new Error(`ultrafast is not available for ${getRoute().provider}/${getRoute().model}`);
30
+ }
31
+ const { serviceTier: previousTier, ...otherParameters } = getRoute().modelParameters || {};
32
+ let modelParameters = getRoute().modelParameters;
33
+ if (ultra) modelParameters = { ...otherParameters, serviceTier: 'ultrafast' };
34
+ else if (previousTier === 'ultrafast') modelParameters = otherParameters;
35
+ const fastCapable = selectionFastCapable(
25
36
  getRoute().provider,
26
37
  modelMeta,
27
38
  getRoute().effectiveEffort || getRoute().effort,
@@ -36,7 +47,7 @@ export function createRouteTuning(deps, persist) {
36
47
  model: getRoute().model,
37
48
  effort: getRoute().effort,
38
49
  fast: fastCapable ? enabled : false,
39
- modelParameters: getRoute().modelParameters,
50
+ modelParameters,
40
51
  })
41
52
  );
42
53
  await commitRouteTuning(fastCapable, modelMeta);
@@ -52,7 +63,7 @@ export function createRouteTuning(deps, persist) {
52
63
  const normalized = normalizeEffortInput(value);
53
64
  setRouteState({ ...getRoute(), effort: normalized });
54
65
  const modelMeta = await lookupModelMeta(getRoute().provider, getRoute().model);
55
- const fastCapable = fastCapableFor(getRoute().provider, modelMeta, normalized, getRoute().modelParameters);
66
+ const fastCapable = selectionFastCapable(getRoute().provider, modelMeta, normalized, getRoute().modelParameters);
56
67
  await commitRouteTuning(fastCapable, modelMeta);
57
68
  return getRoute();
58
69
  },
@@ -4,7 +4,6 @@
4
4
  * (untouched, tuned, recreated, or updated in place when empty).
5
5
  */
6
6
  import { clean, hasOwn } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
7
- import { fastCapableFor } from '../../runtime/agent/orchestrator/runtime-core/model-capabilities.mjs';
8
7
  import {
9
8
  ensureProviderEnabled,
10
9
  findPreset,
@@ -13,9 +12,13 @@ import {
13
12
  } from '../../runtime/agent/orchestrator/runtime-core/config-helpers.mjs';
14
13
  import { getModelMetadataSync } from '../../runtime/agent/orchestrator/providers/model-catalog.mjs';
15
14
  import { workflowPresetId } from '../../runtime/agent/orchestrator/runtime-core/workflow.mjs';
16
- import { resolveRouteContextState } from '../route-state.mjs';
15
+ import { resolveRouteContextState, selectionFastCapable } from '../route-state.mjs';
17
16
  import { writeStatuslineRoute } from '../statusline-route.mjs';
18
- import { sessionHasRouteHistory, shouldRecreateEmptySessionForRouteChange } from '../../runtime/agent/orchestrator/runtime-core/session-route-policy.mjs';
17
+ import {
18
+ sessionHasRouteHistory,
19
+ sessionRouteCommitted,
20
+ shouldRecreateEmptySessionForRouteChange,
21
+ } from '../../runtime/agent/orchestrator/runtime-core/session-route-policy.mjs';
19
22
  import { rebuildDeferredToolSurfaceForProvider } from '../../runtime/agent/orchestrator/runtime-core/tool-catalog.mjs';
20
23
 
21
24
  /** The request, completed from the live route where the caller left gaps. */
@@ -46,7 +49,7 @@ async function resolveSelectedRoute(deps, next) {
46
49
  ) {
47
50
  throw new Error(`unknown model: ${selectedRoute.provider}/${selectedRoute.model}`);
48
51
  }
49
- const fastCapable = fastCapableFor(
52
+ const fastCapable = selectionFastCapable(
50
53
  selectedRoute.provider,
51
54
  modelMeta,
52
55
  selectedRoute.effort,
@@ -82,7 +85,7 @@ async function recreateEmptySession(deps) {
82
85
  }
83
86
 
84
87
  /** Rewrite the addressed empty session to the route it was asked to run on. */
85
- function updateEmptySessionInPlace(deps, session) {
88
+ function updateSessionInPlace(deps, session, updateOptions) {
86
89
  const { getRoute, getSession, setSession, mgr, statusRoutes } = deps;
87
90
  const route = getRoute();
88
91
  const fields = {
@@ -94,53 +97,83 @@ function updateEmptySessionInPlace(deps, session) {
94
97
  contextPercent: route.contextPercent,
95
98
  selectedContextWindow: route.selectedContextWindow || null,
96
99
  };
97
- const updated = mgr.updateSessionRoute?.(session.id, fields);
100
+ const updated = mgr.updateSessionRoute?.(session.id, fields, updateOptions);
98
101
  if (updated) setSession(updated);
99
102
  else Object.assign(session, fields);
100
103
  rebuildDeferredToolSurfaceForProvider(getSession(), route.provider);
101
104
  writeStatuslineRoute(statusRoutes, getSession(), route);
102
105
  }
103
106
 
104
- export function createSetRoute(deps, persist) {
107
+ /** Validate, persist and adopt the requested route as the runtime's route. */
108
+ async function adoptRoute(deps, persist, next, options) {
105
109
  const {
106
110
  getConfig,
107
111
  getRoute,
108
112
  setRouteState,
109
- getSession,
110
113
  resolveRoute,
111
114
  refreshRouteEffort,
112
115
  refreshStatuslineUsageSnapshot,
113
116
  scheduleStatuslineUsageRefresh,
114
- invalidateContextStatusCache,
115
- createCurrentSession,
116
117
  } = deps;
118
+ const { selectedRoute, fastCapable, modelMeta } = await resolveSelectedRoute(deps, next);
119
+ persist.saveRouteModelSettings(selectedRoute, fastCapable);
120
+ // An heir opening on its source's model is not a main-model choice.
121
+ const leadRoute = persist.persistAdoptedModelSettings(selectedRoute, {
122
+ keepMainModel: options?.keepMainModel === true,
123
+ });
124
+ setRouteState(resolveRoute(getConfig(), leadRoute ? { model: workflowPresetId('lead') } : selectedRoute));
125
+ await refreshRouteEffort(modelMeta);
126
+ refreshStatuslineUsageSnapshot(getRoute());
127
+ scheduleStatuslineUsageRefresh();
128
+ }
129
+
130
+ /**
131
+ * Quota-exhaustion escape hatch: apply `next` to the addressed, ESTABLISHED
132
+ * session in place. The caller (session API) has already proven the latest
133
+ * settled turn failed on quota and that nothing is running. Starts no turn;
134
+ * the next turn's auto-compaction fits history to the new window.
135
+ */
136
+ export function createContinueSessionInModel(deps, persist) {
137
+ const { getSession, invalidateContextStatusCache } = deps;
138
+ return async function continueSessionInModel(sessionId, next) {
139
+ const session = getSession();
140
+ if (!sessionId || !session?.id || session.id !== sessionId) {
141
+ throw new Error(`session ${sessionId || ''} is not the active session`);
142
+ }
143
+ await adoptRoute(deps, persist, next, {});
144
+ updateSessionInPlace(deps, session, { crossProvider: true });
145
+ invalidateContextStatusCache();
146
+ return true;
147
+ };
148
+ }
149
+
150
+ export function createSetRoute(deps, persist) {
151
+ const { getRoute, getSession, invalidateContextStatusCache, createCurrentSession } = deps;
117
152
 
118
153
  return async function setRoute(next, options = {}) {
119
154
  // Selection is for a new/inherited session. An explicit durable address
120
155
  // permits initializing an EMPTY session, never rewriting an existing
121
156
  // conversation's model, context boundary or provider cache.
122
157
  const applyToCurrentSession = options?.applyToCurrentSession === true;
123
- const { selectedRoute, fastCapable, modelMeta } = await resolveSelectedRoute(deps, next);
124
- persist.saveRouteModelSettings(selectedRoute, fastCapable);
125
- const leadRoute = persist.persistAdoptedModelSettings(selectedRoute);
126
- setRouteState(resolveRoute(getConfig(), leadRoute ? { model: workflowPresetId('lead') } : selectedRoute));
127
- await refreshRouteEffort(modelMeta);
128
- refreshStatuslineUsageSnapshot(getRoute());
129
- scheduleStatuslineUsageRefresh();
158
+ await adoptRoute(deps, persist, next, options);
130
159
  const session = getSession();
131
160
  // Model/provider changes are next-session-only for a session the user
132
161
  // has already talked in or compacted (provider-keyed prompt cache). But
133
- // an EMPTY current session — no committed route history and no in-flight
134
- // first-turn prompt — has no cache to protect, so /model before the first
135
- // chat takes effect live: route + statusline update immediately.
136
- const currentSessionEmpty = !!session && !sessionHasRouteHistory(session);
162
+ // a current session with no model reply, no compaction summary and no
163
+ // in-flight first turn — empty, or holding only refused prompts — has no
164
+ // cache to protect, so /model takes effect live: route + statusline
165
+ // update immediately. Only a truly empty session is recreated below.
166
+ const currentSessionEmpty = !!session && !sessionRouteCommitted(session);
137
167
  if (!currentSessionEmpty) {
138
168
  // Some desktop effort changes travel as a same-model route update.
139
169
  // Only effort/Fast may affect the source, starting with its next turn.
140
170
  if (applyToCurrentSession) persist.applySessionTuning();
141
171
  return getRoute();
142
172
  }
173
+ // An addressed runtime keeps its durable id: the empty session is updated
174
+ // in place, never closed and replaced by a freshly minted one.
143
175
  if (
176
+ !deps.getAddressedSessionId?.() &&
144
177
  shouldRecreateEmptySessionForRouteChange(session, applyToCurrentSession) &&
145
178
  session?.id &&
146
179
  typeof createCurrentSession === 'function'
@@ -154,7 +187,7 @@ export function createSetRoute(deps, persist) {
154
187
  // durable address. The caller asked to apply this route to THIS empty
155
188
  // session, so update it in place instead of closing A and silently
156
189
  // materializing B before the first prompt.
157
- updateEmptySessionInPlace(deps, session);
190
+ updateSessionInPlace(deps, session);
158
191
  invalidateContextStatusCache();
159
192
  }
160
193
  return getRoute();
@@ -4,8 +4,8 @@
4
4
  */
5
5
  import { clean, hasOwn } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
6
6
  import { coerceEffortFor } from '../../runtime/agent/orchestrator/runtime-core/effort.mjs';
7
- import { fastCapableFor } from '../../runtime/agent/orchestrator/runtime-core/model-capabilities.mjs';
8
7
  import { ensureProviderEnabled } from '../../runtime/agent/orchestrator/runtime-core/config-helpers.mjs';
8
+ import { selectionFastCapable } from '../route-state.mjs';
9
9
  import {
10
10
  isDefaultWebSearchRouteConfig,
11
11
  isWebSearchCapableProvider,
@@ -71,7 +71,7 @@ export function createWebSearchRouteApi(deps) {
71
71
  // bucket belongs to the MAIN route alone, so a web-search model pick that
72
72
  // happens to match Main must not rewrite Main's saved effort/fast.
73
73
  const effort = coerceEffortFor(selectedRoute.provider, modelMeta, selectedRoute.effort);
74
- const fastCapable = fastCapableFor(selectedRoute.provider, modelMeta, effort, selectedRoute.modelParameters);
74
+ const fastCapable = selectionFastCapable(selectedRoute.provider, modelMeta, effort, selectedRoute.modelParameters);
75
75
  return normalizeWebSearchRouteConfig({
76
76
  ...selectedRoute,
77
77
  ...(effort ? { effort } : {}),
@@ -12,7 +12,7 @@
12
12
  */
13
13
  import { createRoutePersistence } from './model-route/route-persist.mjs';
14
14
  import { createWebSearchRouteApi } from './model-route/web-search-route.mjs';
15
- import { createSetRoute } from './model-route/set-route.mjs';
15
+ import { createSetRoute, createContinueSessionInModel } from './model-route/set-route.mjs';
16
16
  import { createRouteTuning } from './model-route/route-tuning.mjs';
17
17
 
18
18
  export { shouldRecreateEmptySessionForRouteChange } from '../runtime/agent/orchestrator/runtime-core/session-route-policy.mjs';
@@ -22,6 +22,7 @@ export function createModelRouteApi(deps) {
22
22
  return {
23
23
  ...createWebSearchRouteApi(deps),
24
24
  setRoute: createSetRoute(deps, persist),
25
+ continueSessionInModel: createContinueSessionInModel(deps, persist),
25
26
  ...createRouteTuning(deps, persist),
26
27
  };
27
28
  }
@@ -29,7 +29,13 @@ function cleanSessionId(value) {
29
29
  // the wake would leave its durable entry parked until the next user message.
30
30
  // A completion whose owner is no longer the live session is still dropped (the
31
31
  // durable queue carries it into that session's next turn).
32
- export function createCompletionWakeScheduler({ getCurrentSessionId, getTurnApi }) {
32
+ export function createCompletionWakeScheduler({ getCurrentSessionId, getTurnApi, getOwnerLifecycle }) {
33
+ // A closing/terminal owner never starts inference from a completion; the
34
+ // check runs at request time and again inside the delayed callback.
35
+ const ownerOpen = (sessionId) => {
36
+ const life = getOwnerLifecycle?.(sessionId);
37
+ return !life || life.state === 'open';
38
+ };
33
39
  // sessionId -> the oldest request suppressed while this session's wake is in
34
40
  // flight, or null when nothing was suppressed. A present key means in flight.
35
41
  const inFlight = new Map();
@@ -41,6 +47,7 @@ export function createCompletionWakeScheduler({ getCurrentSessionId, getTurnApi
41
47
  try {
42
48
  const turnApi = getTurnApi();
43
49
  if (cleanSessionId(getCurrentSessionId()) !== ownerSessionId || !turnApi) return;
50
+ if (!ownerOpen(ownerSessionId)) return;
44
51
  const delayMs = Math.max(0, Date.now() - queuedAt);
45
52
  if (delayMs >= WAKE_DELAY_REPORT_MS) {
46
53
  process.stderr.write(
@@ -70,7 +77,7 @@ export function createCompletionWakeScheduler({ getCurrentSessionId, getTurnApi
70
77
 
71
78
  return function wakeQueuedCompletion({ sessionId, executionId, enqueuedAt } = {}) {
72
79
  const ownerSessionId = cleanSessionId(sessionId);
73
- if (!ownerSessionId) return false;
80
+ if (!ownerSessionId || !ownerOpen(ownerSessionId)) return false;
74
81
  if (inFlight.has(ownerSessionId)) {
75
82
  const suppressed = inFlight.get(ownerSessionId);
76
83
  // Keep the oldest suppressed request so the replay reports the real wait.
@@ -99,10 +106,22 @@ function emitToListeners(source, content, meta = {}) {
99
106
  return { handled, modelVisibleDelivered: event.modelVisibleDelivered === true };
100
107
  }
101
108
 
102
- export function createNotificationBus({ listeners, mgr, onCompletionQueued }) {
109
+ export function createNotificationBus({ listeners, mgr, onCompletionQueued, getOwnerLifecycle }) {
103
110
  const boundSessionListeners = new Map();
104
111
  const queuedCompletionIds = new Map();
105
112
 
113
+ // Owner identity + lifecycle generation. A closing/terminal owner (or a
114
+ // callback captured under an earlier generation) keeps its result for
115
+ // UI/history only: it is flagged `owner_closed`, never queued for the model
116
+ // and never wakes inference.
117
+ function ownerOpen(sessionId, capturedGeneration) {
118
+ const life = getOwnerLifecycle?.(sessionId);
119
+ if (!life) return true;
120
+ if (life.state !== 'open') return false;
121
+ return capturedGeneration === undefined || capturedGeneration === life.generation;
122
+ }
123
+ const closedMeta = (meta) => ({ ...(meta && typeof meta === 'object' ? meta : {}), owner_closed: true });
124
+
106
125
  function emitRuntimeNotification(content, meta = {}) {
107
126
  return emitToListeners(listeners, content, meta);
108
127
  }
@@ -111,8 +130,10 @@ export function createNotificationBus({ listeners, mgr, onCompletionQueued }) {
111
130
  const targetSessionId = cleanSessionId(sessionId);
112
131
  if (!targetSessionId) return false;
113
132
  const targetListeners = sessionNotificationListeners.get(targetSessionId) || [];
114
- const { handled } = emitToListeners(targetListeners, content, meta);
133
+ const open = ownerOpen(targetSessionId);
134
+ const { handled } = emitToListeners(targetListeners, content, open ? meta : closedMeta(meta));
115
135
  if (handled) return true;
136
+ if (!open) return false;
116
137
  if (typeof mgr.enqueuePendingMessage !== 'function') return false;
117
138
  try {
118
139
  return mgr.enqueuePendingMessage(targetSessionId, content) > 0;
@@ -168,13 +189,15 @@ export function createNotificationBus({ listeners, mgr, onCompletionQueued }) {
168
189
  return true;
169
190
  }
170
191
 
171
- function notifySessionCompletion(sessionId, text, meta = {}) {
192
+ function notifySessionCompletion(sessionId, text, meta = {}, capturedGeneration = undefined) {
172
193
  const targetSessionId = cleanSessionId(sessionId);
173
194
  if (!targetSessionId) return false;
174
195
  const targetListeners = sessionNotificationListeners.get(targetSessionId) || [];
196
+ const open = ownerOpen(targetSessionId, capturedGeneration);
175
197
  const ownerMeta = {
176
198
  ...(meta && typeof meta === 'object' ? meta : {}),
177
199
  caller_session_id: targetSessionId,
200
+ ...(open ? {} : { owner_closed: true }),
178
201
  };
179
202
  delete ownerMeta.routing_session_id;
180
203
  const { handled, modelVisibleDelivered } = emitToListeners(targetListeners, text, ownerMeta);
@@ -187,6 +210,7 @@ export function createNotificationBus({ listeners, mgr, onCompletionQueued }) {
187
210
  handled,
188
211
  mvd: modelVisibleDelivered,
189
212
  });
213
+ if (!open) return handled;
190
214
  if (modelVisibleDelivered) {
191
215
  try {
192
216
  recordDeliveredCompletion({
@@ -219,7 +243,8 @@ export function createNotificationBus({ listeners, mgr, onCompletionQueued }) {
219
243
 
220
244
  function notifyFnForSession(callerSessionId) {
221
245
  const ownerSessionId = cleanSessionId(callerSessionId);
222
- return (text, meta = {}) => notifySessionCompletion(ownerSessionId, text, meta);
246
+ const generation = getOwnerLifecycle?.(ownerSessionId)?.generation;
247
+ return (text, meta = {}) => notifySessionCompletion(ownerSessionId, text, meta, generation);
223
248
  }
224
249
 
225
250
  function bindListenerToSession(sessionId, listener) {
@@ -10,6 +10,7 @@ import {
10
10
  saveProviderApiKey,
11
11
  saveCustomProvider,
12
12
  removeCustomProvider,
13
+ updateCustomProviderModel,
13
14
  testCustomProvider,
14
15
  discoverCustomProviderModels,
15
16
  } from '../services/provider-admin.mjs';
@@ -79,6 +80,11 @@ export function createLoginApi({ cfgMod, awaitKeychainPrewarm, reloadFullConfig,
79
80
  refresh.afterCredentialChange();
80
81
  return result;
81
82
  },
83
+ async updateCustomProviderModel(providerId, modelId, patch) {
84
+ const result = await updateCustomProviderModel(cfgMod, providerId, modelId, patch);
85
+ refresh.afterCredentialChange();
86
+ return result;
87
+ },
82
88
  async removeCustomProvider(providerId) {
83
89
  await awaitKeychainPrewarm();
84
90
  const result = removeCustomProvider(cfgMod, providerId);