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
@@ -134,7 +134,7 @@ export function changeProviderAccounts(provider, change) {
134
134
  !change ||
135
135
  typeof change !== 'object' ||
136
136
  Array.isArray(change) ||
137
- Object.keys(change).some((key) => !['selectedId', 'order', 'auto', 'rename'].includes(key))
137
+ Object.keys(change).some((key) => !['selectedId', 'order', 'auto', 'useCredits', 'rename'].includes(key))
138
138
  ) {
139
139
  throw new TypeError('Invalid provider account change.');
140
140
  }
@@ -175,6 +175,10 @@ export function changeProviderAccounts(provider, change) {
175
175
  if (typeof change.auto !== 'boolean') throw new TypeError('Automatic account switching must be boolean.');
176
176
  pool.auto = change.auto;
177
177
  }
178
+ if (change.useCredits !== undefined) {
179
+ if (typeof change.useCredits !== 'boolean') throw new TypeError('Credit use must be boolean.');
180
+ pool.useCredits = change.useCredits;
181
+ }
178
182
  });
179
183
  }
180
184
 
@@ -217,7 +221,9 @@ const MAX_BLOCK_MS = 6 * 60 * 60_000;
217
221
  // account-wide, as before. `source` records whether the server scheduled the
218
222
  // window or we inferred it, which decides whether re-measuring the account
219
223
  // early can tell us anything.
220
- export function blockProviderAccount(provider, id, until, model = '', source = 'inferred') {
224
+ // `options.quota` records that the block came from a typed quota refusal or an
225
+ // exhausted usage window, so later sends still count it as quota evidence.
226
+ export function blockProviderAccount(provider, id, until, model = '', source = 'inferred', options = {}) {
221
227
  return update(provider, (pool) => {
222
228
  const row = pool.accounts.find((entry) => entry.id === id);
223
229
  if (!row) return;
@@ -227,6 +233,8 @@ export function blockProviderAccount(provider, id, until, model = '', source = '
227
233
  else delete row.blockedModel;
228
234
  if (source === 'retry-after') row.blockedSource = source;
229
235
  else delete row.blockedSource;
236
+ if (options.quota === true) row.blockedQuota = true;
237
+ else delete row.blockedQuota;
230
238
  });
231
239
  }
232
240
 
@@ -248,6 +256,7 @@ function clearAccountBlock(row) {
248
256
  delete row.blockedUntil;
249
257
  delete row.blockedModel;
250
258
  delete row.blockedSource;
259
+ delete row.blockedQuota;
251
260
  }
252
261
 
253
262
  // Single-token weekly labels ("7D Fable", "7D Opus") belong to that Claude
@@ -256,7 +265,7 @@ function clearAccountBlock(row) {
256
265
  // model.
257
266
  const FAMILY_QUOTA_WINDOW = /^7D\s+([A-Za-z][A-Za-z0-9]*)$/i;
258
267
 
259
- function quotaWindowAppliesToModel(window, model) {
268
+ export function quotaWindowAppliesToModel(window, model) {
260
269
  const match = FAMILY_QUOTA_WINDOW.exec(String(window?.label || '').trim());
261
270
  if (!match) return true;
262
271
  const family = match[1].toLowerCase();
@@ -280,8 +289,36 @@ function blockAppliesToModel(row, model) {
280
289
  return scoped.some((window) => quotaWindowAppliesToModel(window, scope) && quotaWindowAppliesToModel(window, model));
281
290
  }
282
291
 
283
- export function providerAccountExhausted(row, now = Date.now(), model = '') {
292
+ // Earliest moment any of the given exhausted accounts can serve `model` again,
293
+ // or null when unknown. Per account the LATEST blocking reset counts (every
294
+ // blocking window must clear); across accounts the EARLIEST account does. A
295
+ // server-scheduled block (`retry-after`) is a known time; an inferred block or
296
+ // a full window without a reset time leaves that account's reset unknown.
297
+ export function providerAccountsResetAt(rows, now = Date.now(), model = '', options = {}) {
298
+ const known = [];
299
+ for (const row of rows || []) {
300
+ if (!providerAccountExhausted(row, now, model, options)) continue;
301
+ const times = [];
302
+ let unknown = false;
303
+ if (row.blockedUntil > now && blockAppliesToModel(row, model)) {
304
+ if (row.blockedSource === 'retry-after') times.push(row.blockedUntil);
305
+ else unknown = true;
306
+ }
307
+ if (options.useCredits !== true) {
308
+ for (const window of row.usage?.windows || []) {
309
+ if (!(window.usedPct >= 100) || !quotaWindowAppliesToModel(window, model)) continue;
310
+ if (window.resetAt > now) times.push(window.resetAt);
311
+ else if (!window.resetAt && now - row.usage.checkedAt < 60_000) unknown = true;
312
+ }
313
+ }
314
+ if (!unknown && times.length) known.push(Math.max(...times));
315
+ }
316
+ return known.length ? Math.min(...known) : null;
317
+ }
318
+
319
+ export function providerAccountExhausted(row, now = Date.now(), model = '', options = {}) {
284
320
  if (row?.blockedUntil > now && blockAppliesToModel(row, model)) return true;
321
+ if (options.useCredits === true) return false;
285
322
  return (row?.usage?.windows || []).some(
286
323
  (window) =>
287
324
  window.usedPct >= 100 &&
@@ -309,9 +346,13 @@ function accountHeadroom(row, now, model) {
309
346
  }
310
347
 
311
348
  export function chooseProviderAccount(pool, excluded = new Set(), now = Date.now(), model = '') {
312
- const usable = (row) => row && !excluded.has(row.id) && !providerAccountExhausted(row, now, model);
349
+ const options = { useCredits: pool.useCredits === true };
350
+ const usable = (row) => row && !excluded.has(row.id) && !providerAccountExhausted(row, now, model, options);
313
351
  const selected = pool.accounts.find((row) => row.id === pool.selectedId);
314
- if (pool.auto === false) return excluded.has(selected?.id) ? null : selected || null;
352
+ if (pool.auto === false) {
353
+ if (excluded.has(selected?.id)) return null;
354
+ return options.useCredits || usable(selected) ? selected || null : null;
355
+ }
315
356
  if (usable(selected)) return selected;
316
357
  // Falling back in roster order is blind: the account after the one that just
317
358
  // refused may itself be nearly spent, so the request pays a second switch to
@@ -0,0 +1,255 @@
1
+ /**
2
+ * quota-resume-store.mjs — the daemon's durable queue of Lead turns waiting
3
+ * for their provider quota to reset, plus the global auto-resume setting.
4
+ *
5
+ * One process (the daemon) owns the file; every session runtime it hosts and
6
+ * its resume scheduler read and mutate this same in-memory copy, and every
7
+ * mutation is written through synchronously so a restart finds the queue
8
+ * exactly as the last mutation left it. Mutations are synchronous on purpose:
9
+ * a cancel and a fire can never interleave inside one, and each entry carries
10
+ * a generation so a stale holder can neither fire nor defer a replaced entry.
11
+ *
12
+ * Entry: { sessionId, turnKey, provider, model, resetAt, attempts, generation,
13
+ * routeProvider, routeModel }
14
+ * resetAt — when the scheduler next takes a usage reading (the provider's
15
+ * reset time, or the later re-check after a still-exhausted one).
16
+ * Suspension: { sessionId, turnKey, goalId } — the Goal one quota failure
17
+ * moved from active to usage_limited, the only Goal a resume may revive.
18
+ */
19
+ import { readFileSync } from 'node:fs';
20
+ import { join } from 'node:path';
21
+ import { resolvePluginData } from './plugin-paths.mjs';
22
+ import { writeJsonAtomicSync } from './atomic-file.mjs';
23
+ import { readSection, updateSection } from './config.mjs';
24
+
25
+ export const QUOTA_RESUME_FILE = 'quota-resume.json';
26
+ const FILE_VERSION = 1;
27
+ const SETTING_SECTION = 'quotaResume';
28
+
29
+ let state = null;
30
+ const listeners = new Set();
31
+ // Frozen public snapshots keyed by the immutable entry object they describe,
32
+ // so an unchanged entry publishes the same value identity on every read.
33
+ const publicSnapshots = new WeakMap();
34
+
35
+ const text = (value) => (typeof value === 'string' ? value.trim() : '');
36
+ const finite = (value) => typeof value === 'number' && Number.isFinite(value);
37
+
38
+ function normalizeEntry(value) {
39
+ const entry = {
40
+ sessionId: text(value?.sessionId),
41
+ turnKey: text(value?.turnKey),
42
+ provider: text(value?.provider),
43
+ model: typeof value?.model === 'string' ? value.model : '',
44
+ resetAt: value?.resetAt,
45
+ attempts: Number.isSafeInteger(value?.attempts) && value.attempts >= 0 ? value.attempts : 0,
46
+ generation: value?.generation,
47
+ // The session's own route when the resume was queued; a fire on any
48
+ // other route is not the resume of this failure.
49
+ routeProvider: typeof value?.routeProvider === 'string' ? value.routeProvider : '',
50
+ routeModel: typeof value?.routeModel === 'string' ? value.routeModel : '',
51
+ };
52
+ if (!entry.sessionId || !entry.turnKey || !entry.provider || !finite(entry.resetAt)) return null;
53
+ if (!Number.isSafeInteger(entry.generation) || entry.generation < 1) return null;
54
+ return Object.freeze(entry);
55
+ }
56
+
57
+ function normalizeSuspension(value) {
58
+ const suspension = {
59
+ sessionId: text(value?.sessionId),
60
+ turnKey: text(value?.turnKey),
61
+ goalId: text(value?.goalId),
62
+ };
63
+ return suspension.sessionId && suspension.turnKey && suspension.goalId ? Object.freeze(suspension) : null;
64
+ }
65
+
66
+ function storePath() {
67
+ return join(resolvePluginData(), QUOTA_RESUME_FILE);
68
+ }
69
+
70
+ function load() {
71
+ const path = storePath();
72
+ if (state?.path === path) return state;
73
+ let raw = null;
74
+ try {
75
+ raw = JSON.parse(readFileSync(path, 'utf8'));
76
+ } catch (error) {
77
+ // A missing or torn file is an empty queue; an unreadable one is not.
78
+ if (error?.code !== 'ENOENT' && !(error instanceof SyntaxError)) throw error;
79
+ }
80
+ const entries = new Map();
81
+ for (const value of Array.isArray(raw?.entries) ? raw.entries : []) {
82
+ const entry = normalizeEntry(value);
83
+ if (entry) entries.set(entry.sessionId, entry);
84
+ }
85
+ const suspensions = new Map();
86
+ for (const value of Array.isArray(raw?.suspensions) ? raw.suspensions : []) {
87
+ const suspension = normalizeSuspension(value);
88
+ if (suspension) suspensions.set(suspension.sessionId, suspension);
89
+ }
90
+ const generations = [Number.isSafeInteger(raw?.generation) ? raw.generation : 0];
91
+ for (const entry of entries.values()) generations.push(entry.generation);
92
+ state = { path, generation: Math.max(...generations), entries, suspensions };
93
+ return state;
94
+ }
95
+
96
+ function persist(current) {
97
+ writeJsonAtomicSync(
98
+ current.path,
99
+ {
100
+ version: FILE_VERSION,
101
+ generation: current.generation,
102
+ entries: [...current.entries.values()],
103
+ suspensions: [...current.suspensions.values()],
104
+ },
105
+ { fsyncDir: true }
106
+ );
107
+ }
108
+
109
+ function notify(sessionId) {
110
+ for (const listener of [...listeners]) {
111
+ try {
112
+ listener(sessionId);
113
+ } catch {
114
+ // The mutation is already persisted; one failing listener must neither
115
+ // undo it nor keep the others from hearing about it.
116
+ }
117
+ }
118
+ }
119
+
120
+ /** Listener receives the changed sessionId, or null when every entry changed. */
121
+ export function subscribeQuotaResume(listener) {
122
+ listeners.add(listener);
123
+ return () => listeners.delete(listener);
124
+ }
125
+
126
+ export function quotaResumeEntry(sessionId) {
127
+ return load().entries.get(text(sessionId)) || null;
128
+ }
129
+
130
+ export function listQuotaResumeEntries() {
131
+ return [...load().entries.values()];
132
+ }
133
+
134
+ /** The session snapshot's `quotaResume` value for an entry (or null). */
135
+ export function publicQuotaResume(entry) {
136
+ if (!entry) return null;
137
+ let snapshot = publicSnapshots.get(entry);
138
+ if (!snapshot) {
139
+ snapshot = Object.freeze({
140
+ state: 'waiting',
141
+ turnKey: entry.turnKey,
142
+ provider: entry.provider,
143
+ model: entry.model,
144
+ resetAt: entry.resetAt,
145
+ attempts: entry.attempts,
146
+ });
147
+ publicSnapshots.set(entry, snapshot);
148
+ }
149
+ return snapshot;
150
+ }
151
+
152
+ /** Queue (or replace) the session's resume under a fresh generation. */
153
+ export function scheduleQuotaResumeEntry({
154
+ sessionId,
155
+ turnKey,
156
+ provider,
157
+ model = '',
158
+ resetAt,
159
+ routeProvider = '',
160
+ routeModel = '',
161
+ } = {}) {
162
+ const current = load();
163
+ const entry = normalizeEntry({
164
+ sessionId,
165
+ turnKey,
166
+ provider,
167
+ model,
168
+ resetAt,
169
+ routeProvider,
170
+ routeModel,
171
+ attempts: 0,
172
+ generation: current.generation + 1,
173
+ });
174
+ if (!entry) throw new TypeError('quota resume needs a session, turn, provider and reset time');
175
+ current.generation = entry.generation;
176
+ current.entries.set(entry.sessionId, entry);
177
+ persist(current);
178
+ notify(entry.sessionId);
179
+ return entry;
180
+ }
181
+
182
+ /** Remove the session's entry; with `generation`, only that exact entry. */
183
+ export function cancelQuotaResumeEntry(sessionId, { generation = null } = {}) {
184
+ const current = load();
185
+ const entry = current.entries.get(text(sessionId));
186
+ if (!entry || (generation !== null && entry.generation !== generation)) return false;
187
+ current.entries.delete(entry.sessionId);
188
+ persist(current);
189
+ notify(entry.sessionId);
190
+ return true;
191
+ }
192
+
193
+ /** Move the exact entry to a later reading, keeping its generation. */
194
+ export function deferQuotaResumeEntry(sessionId, generation, { resetAt, attempts }) {
195
+ const current = load();
196
+ const entry = current.entries.get(text(sessionId));
197
+ if (!entry || entry.generation !== generation) return null;
198
+ const next = normalizeEntry({ ...entry, resetAt, attempts });
199
+ if (!next) throw new TypeError('quota resume deferral needs a reset time');
200
+ current.entries.set(next.sessionId, next);
201
+ persist(current);
202
+ notify(next.sessionId);
203
+ return next;
204
+ }
205
+
206
+ export function cancelAllQuotaResumeEntries() {
207
+ const current = load();
208
+ const count = current.entries.size;
209
+ if (count === 0) return 0;
210
+ current.entries.clear();
211
+ persist(current);
212
+ notify(null);
213
+ return count;
214
+ }
215
+
216
+ /** Record the Goal this quota failure suspended (replaces any earlier note). */
217
+ export function noteQuotaSuspendedGoal(sessionId, turnKey, goalId) {
218
+ const suspension = normalizeSuspension({ sessionId, turnKey, goalId });
219
+ if (!suspension) return false;
220
+ const current = load();
221
+ current.suspensions.set(suspension.sessionId, suspension);
222
+ persist(current);
223
+ return true;
224
+ }
225
+
226
+ /** The Goal the failure `turnKey` suspended, or '' when it suspended none. */
227
+ export function quotaSuspendedGoal(sessionId, turnKey) {
228
+ const suspension = load().suspensions.get(text(sessionId));
229
+ return suspension && suspension.turnKey === text(turnKey) ? suspension.goalId : '';
230
+ }
231
+
232
+ export function forgetQuotaSuspendedGoal(sessionId) {
233
+ const current = load();
234
+ if (!current.suspensions.delete(text(sessionId))) return false;
235
+ persist(current);
236
+ return true;
237
+ }
238
+
239
+ /** Global setting, default on. */
240
+ export function quotaAutoResumeEnabled() {
241
+ return readSection(SETTING_SECTION)?.autoResume !== false;
242
+ }
243
+
244
+ /** Persist the setting; turning it off cancels every pending resume. */
245
+ export function setQuotaAutoResumeEnabled(enabled) {
246
+ const next = enabled !== false;
247
+ updateSection(SETTING_SECTION, (section) => ({ ...(section || {}), autoResume: next }));
248
+ if (!next) cancelAllQuotaResumeEntries();
249
+ return next;
250
+ }
251
+
252
+ /** Drop the in-memory copy so the next read loads from disk (a daemon restart). */
253
+ export function reloadQuotaResumeStore() {
254
+ state = null;
255
+ }
@@ -0,0 +1,275 @@
1
+ // Shipped definitions live in code; the data dir should hold only copies the
2
+ // user actually changed. This module holds the pieces shared by the startup
3
+ // migration (seeds.mjs), the editors, and scripts/build-shipped-history.mjs:
4
+ // content normalisation + hashing (so the generator and the runtime compare
5
+ // exactly the same thing), the shipped-history file, and the backup mover.
6
+ import { createHash } from 'node:crypto';
7
+ import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync } from 'node:fs';
8
+ import { dirname, join, relative } from 'node:path';
9
+ import { readMarkdownDocument } from './markdown-frontmatter.mjs';
10
+
11
+ export const SHIPPED_HISTORY_FILE = ['defaults', 'shipped-history.json'];
12
+ export const EMPTY_HISTORY = Object.freeze({ agents: {}, workflows: {}, outputStyles: {}, skills: {} });
13
+
14
+ // Leftovers from hand edits or editors that never belong to a definition.
15
+ const IGNORABLE_STRAY = /(\.(bak|orig|tmp)|~)$/i;
16
+ export const isIgnorableStray = (name) => IGNORABLE_STRAY.test(name);
17
+
18
+ const digest = (text) => createHash('sha256').update(text).digest('hex').slice(0, 32);
19
+
20
+ /** CRLF -> LF, trailing whitespace stripped per line and at the end. */
21
+ export function normalizeText(value) {
22
+ return (Buffer.isBuffer(value) ? value.toString('utf8') : String(value ?? ''))
23
+ .replace(/\r\n/g, '\n')
24
+ .replace(/[^\S\n]+$/gm, '')
25
+ .trimEnd();
26
+ }
27
+
28
+ export const hashText = (value) => digest(normalizeText(value));
29
+
30
+ const oneLine = (value) =>
31
+ String(value || '')
32
+ .replace(/\s+/g, ' ')
33
+ .trim();
34
+
35
+ /**
36
+ * Editable surface of an agent/workflow pack. `read(name)` returns the text of
37
+ * a file in the pack or null. agent.json wins over frontmatter for
38
+ * name/description, matching the loaders.
39
+ */
40
+ export function packDefinitionFrom(read, entry, id = '') {
41
+ let manifest = {};
42
+ try {
43
+ const parsed = JSON.parse(read('agent.json') || '{}');
44
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) manifest = parsed;
45
+ } catch {
46
+ manifest = {};
47
+ }
48
+ const doc = readMarkdownDocument(read(oneLine(manifest.entry) || entry) || '');
49
+ const body = normalizeText(doc.body);
50
+ if (!body) return null;
51
+ // Every other loader-visible field (frontmatter flags such as permission or
52
+ // hidden, model/tool fields, manifest keys). An `id` equal to the directory
53
+ // id and the default `entry` are what the loaders assume anyway, so they are
54
+ // not differences.
55
+ const sameId = (value) =>
56
+ String(value ?? '')
57
+ .trim()
58
+ .toLowerCase() === String(id).toLowerCase();
59
+ const fields = {};
60
+ for (const [key, value] of Object.entries(doc.frontmatter)) {
61
+ if (key === 'name' || key === 'description' || (key === 'id' && sameId(value))) continue;
62
+ fields[`frontmatter.${key}`] = String(value).trim();
63
+ }
64
+ for (const [key, value] of Object.entries(manifest)) {
65
+ if (key === 'name' || key === 'description' || (key === 'id' && sameId(value))) continue;
66
+ if (key === 'entry' && (oneLine(value) === entry || !oneLine(value))) continue;
67
+ fields[`manifest.${key}`] = JSON.stringify(value);
68
+ }
69
+ return {
70
+ name: oneLine(manifest.name || doc.frontmatter.name),
71
+ description: oneLine(manifest.description || doc.frontmatter.description),
72
+ body,
73
+ fields,
74
+ };
75
+ }
76
+
77
+ const fieldsKey = (fields) => {
78
+ const source = fields || {};
79
+ return JSON.stringify(
80
+ Object.keys(source)
81
+ .sort()
82
+ .map((key) => [key, source[key]])
83
+ );
84
+ };
85
+
86
+ /** surfaceOnly: name/description/body, what the editors can express. */
87
+ export const hashDefinition = ({ name, description, body, fields }, { surfaceOnly = false } = {}) =>
88
+ digest(
89
+ JSON.stringify([oneLine(name), oneLine(description), normalizeText(body), surfaceOnly ? '' : fieldsKey(fields)])
90
+ );
91
+
92
+ export function packDefinitionFromDir(dir, entry, id = '') {
93
+ return packDefinitionFrom(
94
+ (name) => {
95
+ try {
96
+ return readFileSync(join(dir, name), 'utf8');
97
+ } catch {
98
+ return null;
99
+ }
100
+ },
101
+ entry,
102
+ id
103
+ );
104
+ }
105
+
106
+ export function packHashFromDir(dir, entry, id = '') {
107
+ const definition = packDefinitionFromDir(dir, entry, id);
108
+ return definition ? hashDefinition(definition) : null;
109
+ }
110
+
111
+ const utf8 = new TextDecoder('utf-8', { fatal: true });
112
+ // Anything that is not valid UTF-8 text hashes byte-for-byte.
113
+ const isBinary = (content) => {
114
+ if (!Buffer.isBuffer(content)) return false;
115
+ if (content.includes(0)) return true;
116
+ try {
117
+ utf8.decode(content);
118
+ return false;
119
+ } catch {
120
+ return true;
121
+ }
122
+ };
123
+
124
+ /**
125
+ * files: [relative '/' path, Buffer|string][]; stray files are ignored. Text
126
+ * files are normalised (CRLF/trailing space); binary files hash byte-for-byte.
127
+ */
128
+ export function hashTree(files) {
129
+ const lines = files
130
+ .filter(([rel]) => !isIgnorableStray(rel.split('/').pop()))
131
+ .map(([rel, content]) => `${rel}\0${isBinary(content) ? `b${digest(content)}` : hashText(content)}`)
132
+ .sort();
133
+ return digest(lines.join('\n'));
134
+ }
135
+
136
+ export function readTreeFiles(dir, prefix = '') {
137
+ const files = [];
138
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
139
+ const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
140
+ if (entry.isDirectory()) files.push(...readTreeFiles(join(dir, entry.name), rel));
141
+ else if (entry.isFile()) files.push([rel, readFileSync(join(dir, entry.name))]);
142
+ }
143
+ return files;
144
+ }
145
+
146
+ export function treeHashFromDir(dir) {
147
+ try {
148
+ return hashTree(readTreeFiles(dir));
149
+ } catch {
150
+ return null;
151
+ }
152
+ }
153
+
154
+ export function fileHash(file) {
155
+ try {
156
+ return hashText(readFileSync(file));
157
+ } catch {
158
+ return null;
159
+ }
160
+ }
161
+
162
+ export function loadShippedHistory(rootDir) {
163
+ try {
164
+ return { ...EMPTY_HISTORY, ...JSON.parse(readFileSync(join(rootDir, ...SHIPPED_HISTORY_FILE), 'utf8')) };
165
+ } catch {
166
+ return EMPTY_HISTORY;
167
+ }
168
+ }
169
+
170
+ /** The copy must hold the entry file and nothing but editor files and ignorable strays. */
171
+ export function holdsOnlyEditorFiles(dir, allowed, entry) {
172
+ let entries;
173
+ try {
174
+ entries = readdirSync(dir, { withFileTypes: true });
175
+ } catch {
176
+ return false;
177
+ }
178
+ return (
179
+ entries.some((e) => e.isFile() && e.name === entry) &&
180
+ entries.every((e) => e.isFile() && (allowed.includes(e.name) || isIgnorableStray(e.name)))
181
+ );
182
+ }
183
+
184
+ /**
185
+ * Editor save of a pack whose definition equals the shipped one: no user copy
186
+ * should exist, so the item keeps following the shipped version. An existing
187
+ * copy (only editor files, a tombstone, strays) moves into a backup. Returns
188
+ * true when the save was absorbed; false means the caller writes the copy.
189
+ */
190
+ export function absorbSaveEqualToShipped({ rootDir, dataDir, dir, id, entry, files, definition }) {
191
+ // The editor only expresses name/description/body; the shipped fields it
192
+ // cannot see keep applying because no copy is written.
193
+ const shipped = packDefinitionFromDir(join(rootDir, dir, id), entry, id);
194
+ if (
195
+ !shipped ||
196
+ hashDefinition(definition, { surfaceOnly: true }) !== hashDefinition(shipped, { surfaceOnly: true })
197
+ ) {
198
+ return false;
199
+ }
200
+ const copy = join(dataDir, dir, id);
201
+ if (!existsSync(copy)) return true;
202
+ const allowed = [...files, '.deleted'];
203
+ const clean = readdirSync(copy, { withFileTypes: true }).every(
204
+ (e) => e.isFile() && (allowed.includes(e.name) || isIgnorableStray(e.name))
205
+ );
206
+ if (!clean) return false;
207
+ // A real existing definition must match the shipped hidden fields exactly
208
+ // (an empty set included); a tombstone-only state has no definition to compare.
209
+ const existing = packDefinitionFromDir(copy, entry, id);
210
+ if (existing && fieldsKey(existing.fields || {}) !== fieldsKey(shipped.fields)) return false;
211
+ return makeBackup(dataDir).move(copy);
212
+ }
213
+
214
+ // Bump to re-run the history-based retirement for every install.
215
+ export const SEPARATION_VERSION = 1;
216
+ export const HISTORY_MARKER_FILE = '.shipped-history-retired';
217
+
218
+ /** Identity of the history retirement: separation version + history content. */
219
+ export const historyStamp = (rootDir) => `${SEPARATION_VERSION}:${digest(JSON.stringify(loadShippedHistory(rootDir)))}`;
220
+
221
+ /**
222
+ * Lazily-created `<dataDir>/backups/defaults-separation-<timestamp>` folder.
223
+ * Moved entries keep their data-dir-relative path; nothing is deleted.
224
+ */
225
+ export function makeBackup(dataDir) {
226
+ let root = null;
227
+ let failures = 0;
228
+ // Atomic create: an existing backup folder is never reused.
229
+ const claimRoot = () => {
230
+ const base = join(dataDir, 'backups', `defaults-separation-${new Date().toISOString().replace(/[:.]/g, '-')}`);
231
+ mkdirSync(join(dataDir, 'backups'), { recursive: true });
232
+ for (let n = 1; ; n += 1) {
233
+ const candidate = n === 1 ? base : `${base}-${n}`;
234
+ try {
235
+ mkdirSync(candidate);
236
+ return candidate;
237
+ } catch (error) {
238
+ if (error?.code !== 'EEXIST') throw error;
239
+ }
240
+ }
241
+ };
242
+ const freeDest = (wanted) => {
243
+ let dest = wanted;
244
+ for (let n = 2; existsSync(dest); n += 1) dest = `${wanted}.${n}`;
245
+ return dest;
246
+ };
247
+ return {
248
+ get root() {
249
+ return root;
250
+ },
251
+ get failures() {
252
+ return failures;
253
+ },
254
+ /** Moves `target` into the backup; returns false if it could not be moved. */
255
+ move(target) {
256
+ if (!existsSync(target)) return true;
257
+ try {
258
+ root ??= claimRoot();
259
+ const dest = freeDest(join(root, relative(dataDir, target)));
260
+ mkdirSync(dirname(dest), { recursive: true });
261
+ try {
262
+ renameSync(target, dest);
263
+ } catch {
264
+ cpSync(target, dest, { recursive: true, force: false, errorOnExist: true });
265
+ rmSync(target, { recursive: true, force: true });
266
+ }
267
+ return true;
268
+ } catch {
269
+ // best-effort; a stuck copy simply keeps shadowing until moved by hand
270
+ failures += 1;
271
+ return false;
272
+ }
273
+ },
274
+ };
275
+ }