mixdog 1.2.1 → 1.2.3

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 (557) hide show
  1. package/README.md +18 -0
  2. package/package.json +2 -1
  3. package/scripts/gate-local.mjs +19 -3
  4. package/scripts/model-catalog-audit.mjs +1 -15
  5. package/scripts/provider-pingpong-live.mjs +509 -0
  6. package/src/agents/advisor/AGENT.md +1 -1
  7. package/src/agents/advisor/agent.json +1 -1
  8. package/src/agents/reviewer/AGENT.md +3 -3
  9. package/src/agents/reviewer/agent.json +1 -1
  10. package/src/defaults/shipped-history.json +6 -0
  11. package/src/defaults/skills/setup/SKILL.md +6 -1
  12. package/src/defaults/skills/setup/references/actions.md +44 -22
  13. package/src/defaults/skills/setup/references/surfaces.md +10 -8
  14. package/src/headless-exec.mjs +15 -8
  15. package/src/lib/keychain-cjs.cjs +7 -4
  16. package/src/output-styles/common.md +11 -10
  17. package/src/repl/slash-commands.mjs +16 -1
  18. package/src/rules/agent/AGENT.md +5 -5
  19. package/src/rules/lead/LEAD.md +5 -5
  20. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -4
  21. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +10 -0
  22. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +92 -61
  23. package/src/runtime/agent/orchestrator/cache-mtime.mjs +10 -1
  24. package/src/runtime/agent/orchestrator/config-providers-merge.mjs +7 -0
  25. package/src/runtime/agent/orchestrator/config.mjs +5 -0
  26. package/src/runtime/agent/orchestrator/context/collect.mjs +1 -0
  27. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +26 -7
  28. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +112 -7
  29. package/src/runtime/agent/orchestrator/mcp/client-config.mjs +36 -7
  30. package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +49 -10
  31. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +40 -3
  32. package/src/runtime/agent/orchestrator/mcp/client.mjs +230 -96
  33. package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +103 -0
  34. package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +150 -0
  35. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +111 -0
  36. package/src/runtime/agent/orchestrator/mcp/oauth-probe.mjs +87 -0
  37. package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +95 -0
  38. package/src/runtime/agent/orchestrator/mcp/oauth-request.mjs +122 -0
  39. package/src/runtime/agent/orchestrator/mcp/oauth-shared.mjs +9 -0
  40. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +229 -0
  41. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +188 -29
  42. package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +2 -1
  43. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +48 -20
  44. package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +94 -31
  45. package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +98 -0
  46. package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.mjs +39 -18
  47. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +55 -0
  48. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +48 -37
  49. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +10 -7
  50. package/src/runtime/agent/orchestrator/providers/anthropic-midstream-recovery.mjs +18 -4
  51. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +13 -1
  52. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  53. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-midstream.mjs +10 -1
  54. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-recovery.mjs +23 -9
  55. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +46 -20
  56. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request.mjs +14 -9
  57. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +102 -45
  58. package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +69 -0
  59. package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.mjs +4 -0
  60. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-blocks.mjs +20 -1
  61. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-delta.mjs +15 -2
  62. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-start.mjs +11 -4
  63. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/message-events.mjs +3 -0
  64. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events.mjs +12 -5
  65. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-outcome.mjs +1 -0
  66. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-state.mjs +14 -0
  67. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +10 -1
  68. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +9 -0
  69. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +40 -7
  70. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +30 -9
  71. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +7 -0
  72. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +88 -21
  73. package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +30 -10
  74. package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +106 -0
  75. package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +10 -61
  76. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +94 -15
  77. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
  78. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +117 -72
  79. package/src/runtime/agent/orchestrator/providers/compat-request-policy.mjs +78 -2
  80. package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +75 -10
  81. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  82. package/src/runtime/agent/orchestrator/providers/cursor-wire-guards.mjs +97 -2
  83. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +103 -23
  84. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-messages.mjs +43 -2
  85. package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +103 -33
  86. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +32 -6
  87. package/src/runtime/agent/orchestrator/providers/cursor.mjs +27 -3
  88. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +64 -1
  89. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +70 -6
  90. package/src/runtime/agent/orchestrator/providers/custom-reasoning.mjs +34 -7
  91. package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +32 -6
  92. package/src/runtime/agent/orchestrator/providers/deepseek.mjs +67 -0
  93. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +80 -18
  94. package/src/runtime/agent/orchestrator/providers/gemini-cache-policy.mjs +11 -4
  95. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +46 -8
  96. package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +273 -0
  97. package/src/runtime/agent/orchestrator/providers/gemini-family.mjs +9 -0
  98. package/src/runtime/agent/orchestrator/providers/gemini-history-signatures.mjs +9 -4
  99. package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +41 -5
  100. package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +3 -1
  101. package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +180 -0
  102. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +369 -112
  103. package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +3 -1
  104. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +140 -35
  105. package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +70 -24
  106. package/src/runtime/agent/orchestrator/providers/gemini.mjs +80 -29
  107. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  108. package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
  109. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +69 -19
  110. package/src/runtime/agent/orchestrator/providers/lib/action-input-contract.mjs +60 -0
  111. package/src/runtime/agent/orchestrator/providers/lib/anthropic-native-blocks.mjs +1 -0
  112. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +246 -42
  113. package/src/runtime/agent/orchestrator/providers/lib/anthropic-server-tool-usage.mjs +18 -0
  114. package/src/runtime/agent/orchestrator/providers/lib/anthropic-system-blocks.mjs +27 -6
  115. package/src/runtime/agent/orchestrator/providers/lib/claude-bridge-schema.mjs +388 -0
  116. package/src/runtime/agent/orchestrator/providers/lib/inclusive-usage.mjs +54 -0
  117. package/src/runtime/agent/orchestrator/providers/lib/origin-bound-fetch.mjs +16 -0
  118. package/src/runtime/agent/orchestrator/providers/lib/responses-terminal-fields.mjs +11 -0
  119. package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +8 -0
  120. package/src/runtime/agent/orchestrator/providers/lib/wire-pairing.mjs +48 -22
  121. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +119 -18
  122. package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +13 -15
  123. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +3 -0
  124. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +2 -1
  125. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +1 -1
  126. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
  127. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +33 -1
  128. package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +4 -9
  129. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +2 -0
  130. package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +2 -0
  131. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +123 -61
  132. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +35 -14
  133. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-result.mjs +27 -2
  134. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +37 -7
  135. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/tool-call-acc.mjs +2 -9
  136. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +1 -0
  137. package/src/runtime/agent/orchestrator/providers/openai-compat-error-contract.mjs +50 -0
  138. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +47 -2
  139. package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +47 -25
  140. package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +5 -1
  141. package/src/runtime/agent/orchestrator/providers/openai-compat-reasoning-field.mjs +151 -0
  142. package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +64 -23
  143. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +79 -33
  144. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-head-parity.json +2901 -0
  145. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-parity.fixtures.mjs +172 -0
  146. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-state.mjs +3 -2
  147. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +144 -15
  148. package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +193 -0
  149. package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +66 -2
  150. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +9 -2
  151. package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +2 -0
  152. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +109 -26
  153. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +27 -6
  154. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +34 -20
  155. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +23 -4
  156. package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +37 -28
  157. package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +274 -0
  158. package/src/runtime/agent/orchestrator/providers/openai-http-sse-request.mjs +72 -32
  159. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +23 -10
  160. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +43 -15
  161. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +10 -0
  162. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-items.mjs +18 -7
  163. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-outcome.mjs +5 -2
  164. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-stamps.mjs +2 -2
  165. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-state.mjs +7 -3
  166. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/text-relay.mjs +9 -1
  167. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +2 -2
  168. package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +1 -3
  169. package/src/runtime/agent/orchestrator/providers/openai-model-capabilities.mjs +80 -0
  170. package/src/runtime/agent/orchestrator/providers/openai-oauth-dispatch.mjs +37 -4
  171. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +12 -1
  172. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +22 -2
  173. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +12 -1
  174. package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +76 -33
  175. package/src/runtime/agent/orchestrator/providers/openai-responses-payload-parity.fixture.json +323 -0
  176. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +44 -16
  177. package/src/runtime/agent/orchestrator/providers/openai-retry-policy.mjs +9 -0
  178. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +7 -16
  179. package/src/runtime/agent/orchestrator/providers/openai-ws-handshake.mjs +16 -8
  180. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/refusals.mjs +60 -0
  181. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/tool-calls.mjs +51 -35
  182. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state.mjs +30 -0
  183. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +1 -2
  184. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/policy.mjs +25 -14
  185. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +12 -0
  186. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/terminal-frames.mjs +25 -6
  187. package/src/runtime/agent/orchestrator/providers/openai-ws-terminal.mjs +18 -11
  188. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +41 -22
  189. package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +170 -0
  190. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +75 -70
  191. package/src/runtime/agent/orchestrator/providers/openrouter.mjs +75 -0
  192. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +720 -0
  193. package/src/runtime/agent/orchestrator/providers/registry.mjs +4 -1
  194. package/src/runtime/agent/orchestrator/providers/retry-backoff.mjs +46 -7
  195. package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +123 -219
  196. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +2 -0
  197. package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +148 -45
  198. package/src/runtime/agent/orchestrator/providers/retry-midstream.mjs +81 -16
  199. package/src/runtime/agent/orchestrator/providers/transport-scope.d.mts +6 -0
  200. package/src/runtime/agent/orchestrator/providers/transport-scope.mjs +28 -0
  201. package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +157 -0
  202. package/src/runtime/agent/orchestrator/providers/wire-route.mjs +512 -0
  203. package/src/runtime/agent/orchestrator/providers/xai-tool-search-head-parity.json +1131 -0
  204. package/src/runtime/agent/orchestrator/providers/xai-tool-search.fixtures.mjs +183 -0
  205. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +100 -0
  206. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +3 -0
  207. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +21 -0
  208. package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +15 -0
  209. package/src/runtime/agent/orchestrator/runtime-core/goal-reminder.mjs +0 -18
  210. package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +57 -28
  211. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +19 -16
  212. package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +1 -97
  213. package/src/runtime/agent/orchestrator/runtime-core/provider-request-snapshot.mjs +7 -3
  214. package/src/runtime/agent/orchestrator/runtime-core/session-title.mjs +21 -1
  215. package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +14 -3
  216. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +526 -0
  217. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
  218. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +10 -7
  219. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +176 -71
  220. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +62 -10
  221. package/src/runtime/agent/orchestrator/runtime-core/tool-exposure.mjs +96 -0
  222. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +5 -1
  223. package/src/runtime/agent/orchestrator/session/compact/constants.mjs +3 -2
  224. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +14 -93
  225. package/src/runtime/agent/orchestrator/session/compact/summary-anchor.mjs +5 -0
  226. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +120 -0
  227. package/src/runtime/agent/orchestrator/session/context-tool-schema.mjs +17 -2
  228. package/src/runtime/agent/orchestrator/session/context-utils.mjs +8 -4
  229. package/src/runtime/agent/orchestrator/session/eager-dispatch/admission.mjs +2 -1
  230. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +6 -1
  231. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +91 -1
  232. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +1 -1
  233. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +56 -0
  234. package/src/runtime/agent/orchestrator/session/loop/deferred-call-through.mjs +18 -8
  235. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -0
  236. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +21 -10
  237. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +132 -0
  238. package/src/runtime/agent/orchestrator/session/loop/request-boundary.mjs +30 -0
  239. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +67 -20
  240. package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +104 -19
  241. package/src/runtime/agent/orchestrator/session/loop/tool-call-resolution.mjs +95 -0
  242. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +30 -3
  243. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +15 -4
  244. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +15 -1
  245. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +150 -0
  246. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +7 -2
  247. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +30 -2
  248. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +11 -2
  249. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +2 -1
  250. package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +34 -12
  251. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +57 -21
  252. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +57 -25
  253. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
  254. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
  255. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +8 -0
  256. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +5 -10
  257. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +208 -14
  258. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +16 -4
  259. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +25 -3
  260. package/src/runtime/agent/orchestrator/session/manager/session-fallback-title.mjs +82 -0
  261. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +19 -2
  262. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +3 -0
  263. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +4 -1
  264. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +18 -7
  265. package/src/runtime/agent/orchestrator/session/manager.mjs +2 -1
  266. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +6 -0
  267. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +168 -29
  268. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +96 -0
  269. package/src/runtime/agent/orchestrator/session/side-question.mjs +192 -0
  270. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +17 -3
  271. package/src/runtime/agent/orchestrator/session/store/live-state.mjs +23 -0
  272. package/src/runtime/agent/orchestrator/session/store/paths-heartbeat.mjs +58 -2
  273. package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +6 -3
  274. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +41 -28
  275. package/src/runtime/agent/orchestrator/session/store/session-observers.mjs +4 -2
  276. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +12 -1
  277. package/src/runtime/agent/orchestrator/session/store/write-admission.mjs +3 -1
  278. package/src/runtime/agent/orchestrator/session/store/write-guards.mjs +16 -0
  279. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +22 -10
  280. package/src/runtime/agent/orchestrator/session/store-summary-archived-agent.mjs +34 -15
  281. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +6 -0
  282. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +13 -0
  283. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +13 -1
  284. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +10 -2
  285. package/src/runtime/agent/orchestrator/session/store.mjs +19 -3
  286. package/src/runtime/agent/orchestrator/session/tool-batch/execute-call.mjs +1 -0
  287. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +1 -1
  288. package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +1 -1
  289. package/src/runtime/agent/orchestrator/session/tool-batch/state.mjs +7 -1
  290. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +4 -0
  291. package/src/runtime/agent/orchestrator/session/tool-schema-rejection.mjs +77 -0
  292. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +20 -1
  293. package/src/runtime/agent/orchestrator/tools/bash-policy-scan.mjs +8 -2
  294. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +2 -2
  295. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/command-preflight.mjs +3 -3
  296. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +12 -15
  297. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +4 -1
  298. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +5 -5
  299. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +14 -10
  300. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +49 -34
  301. package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +23 -1
  302. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +1 -1
  303. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1 -1
  304. package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +73 -39
  305. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +18 -1
  306. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -4
  307. package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +88 -12
  308. package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +2 -0
  309. package/src/runtime/computer-bridge/action-schema.mjs +44 -21
  310. package/src/runtime/github/tool.mjs +92 -2
  311. package/src/runtime/memory/index.mjs +1 -0
  312. package/src/runtime/memory/lib/http-router/ingest-routes.mjs +22 -1
  313. package/src/runtime/memory/lib/ingest/transcript-offsets.mjs +2 -0
  314. package/src/runtime/memory/lib/pg/adapter.mjs +98 -1
  315. package/src/runtime/memory/lib/pg/process.mjs +65 -50
  316. package/src/runtime/memory/lib/pg/run-child.mjs +49 -0
  317. package/src/runtime/memory/lib/pg/supervisor.mjs +124 -16
  318. package/src/runtime/memory/lib/runtime-fetcher.mjs +12 -13
  319. package/src/runtime/memory/lib/transcript-ingest.mjs +14 -0
  320. package/src/runtime/office/README.md +2 -2
  321. package/src/runtime/office/authoring/html-document-author.mjs +2 -0
  322. package/src/runtime/office/authoring/pptx-contact-sheet.mjs +2 -0
  323. package/src/runtime/office/authoring/pptx-html-measure.mjs +2 -0
  324. package/src/runtime/office/authoring/pptx-html-runner.mjs +2 -0
  325. package/src/runtime/office/authoring/xlsx-html-runner.mjs +2 -0
  326. package/src/runtime/office/core/office-actions-read.mjs +3 -0
  327. package/src/runtime/office/core/office-render-preview.mjs +2 -0
  328. package/src/runtime/office/core/office-session-dispatch.mjs +7 -0
  329. package/src/runtime/office/core/office-sessionless-actions.mjs +8 -0
  330. package/src/runtime/office/core/office-sessions.mjs +5 -0
  331. package/src/runtime/office/core/office-transactions.mjs +3 -0
  332. package/src/runtime/office/core/office-write-ledger.mjs +98 -0
  333. package/src/runtime/office/design/library/templates/mixdog-executive.mixdog-edit.pptx +0 -0
  334. package/src/runtime/office/index.mjs +30 -0
  335. package/src/runtime/office/pdf/pdf-edit-document.mjs +2 -0
  336. package/src/runtime/office/pdf/pdf-render.mjs +23 -7
  337. package/src/runtime/office/pdf/pdf-security.mjs +2 -0
  338. package/src/runtime/office/portable/portable-opc.mjs +4 -1
  339. package/src/runtime/office/quality/visual-diff.mjs +2 -0
  340. package/src/runtime/office/shared/values.mjs +0 -5
  341. package/src/runtime/shared/background-tasks.mjs +92 -14
  342. package/src/runtime/shared/bounded-file-read.mjs +13 -0
  343. package/src/runtime/shared/bounded-line-diff.mjs +182 -0
  344. package/src/runtime/shared/buffered-appender.mjs +18 -0
  345. package/src/runtime/shared/config.mjs +78 -18
  346. package/src/runtime/shared/err-text.mjs +31 -0
  347. package/src/runtime/shared/error-presentation.mjs +159 -19
  348. package/src/runtime/shared/file-permissions.d.mts +6 -1
  349. package/src/runtime/shared/file-permissions.mjs +54 -6
  350. package/src/runtime/shared/llm/anthropic-betas.mjs +4 -3
  351. package/src/runtime/shared/llm/anthropic-effort.mjs +129 -134
  352. package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +50 -14
  353. package/src/runtime/shared/llm/model-catalog.mjs +53 -40
  354. package/src/runtime/shared/llm/openai-daybreak.mjs +4 -16
  355. package/src/runtime/shared/llm/provider-catalog-registry.mjs +11 -0
  356. package/src/runtime/shared/llm/provider-model-identities.mjs +13 -4
  357. package/src/runtime/shared/llm/usage-accounting.mjs +5 -2
  358. package/src/runtime/shared/llm/usage-ledger-rollup.mjs +3 -0
  359. package/src/runtime/shared/llm/usage-rollup.mjs +0 -1
  360. package/src/runtime/shared/loopback-listener.mjs +9 -0
  361. package/src/runtime/shared/model-display.mjs +13 -0
  362. package/src/runtime/shared/provider-api-key.mjs +2 -0
  363. package/src/runtime/shared/quota-resume-store.mjs +11 -0
  364. package/src/runtime/shared/review-diff.mjs +52 -0
  365. package/src/runtime/shared/session-baseline-store.mjs +960 -0
  366. package/src/runtime/shared/session-sidebar-host.d.mts +4 -0
  367. package/src/runtime/shared/session-sidebar-host.mjs +38 -0
  368. package/src/runtime/shared/slow-span.mjs +114 -0
  369. package/src/runtime/shared/statusline/statusline-format.mjs +4 -4
  370. package/src/runtime/shared/statusline/statusline-segments.mjs +28 -4
  371. package/src/runtime/shared/task-notification-envelope.mjs +2 -2
  372. package/src/runtime/shared/tool-card-model/card-header.mjs +6 -1
  373. package/src/runtime/shared/tool-card-model/generic-detail.mjs +39 -5
  374. package/src/runtime/shared/tool-card-model.mjs +5 -2
  375. package/src/runtime/shared/tool-output-limit.mjs +4 -0
  376. package/src/runtime/shared/tool-primitives.mjs +13 -0
  377. package/src/runtime/shared/tool-result-summary.mjs +8 -1
  378. package/src/runtime/shared/tool-surface.mjs +18 -3
  379. package/src/runtime/shared/tool-work-units.mjs +40 -4
  380. package/src/runtime/shared/transcript-writer/purge.mjs +74 -0
  381. package/src/runtime/shared/transcript-writer.mjs +10 -2
  382. package/src/runtime/shared/turn-changes.mjs +670 -274
  383. package/src/runtime/shared/turn-review-reply.mjs +8 -39
  384. package/src/runtime/shared/turn-snapshot-store.mjs +94 -2
  385. package/src/runtime/shared/turn-snapshot.mjs +986 -690
  386. package/src/runtime/shared/turn-worktree-snapshot.mjs +279 -262
  387. package/src/runtime/shared/user-data-guard.mjs +262 -49
  388. package/src/runtime/shared/worktrees/carry.mjs +531 -0
  389. package/src/runtime/shared/worktrees/contract.mjs +34 -24
  390. package/src/runtime/shared/worktrees/file-recovery.mjs +68 -0
  391. package/src/runtime/shared/worktrees/git-head.mjs +205 -0
  392. package/src/runtime/shared/worktrees/git.mjs +8 -3
  393. package/src/runtime/shared/worktrees/maintenance.mjs +1 -1
  394. package/src/runtime/shared/worktrees/manager.mjs +156 -8
  395. package/src/runtime/shared/worktrees/merge.mjs +31 -21
  396. package/src/runtime/shared/worktrees/paths.d.mts +6 -0
  397. package/src/runtime/shared/worktrees/test-support.mjs +19 -4
  398. package/src/runtime/terminal-bridge/client.mjs +44 -11
  399. package/src/runtime/terminal-bridge/tool-defs.mjs +13 -6
  400. package/src/runtime/tidy/apply.mjs +47 -2
  401. package/src/runtime/tidy/run-engines.mjs +11 -5
  402. package/src/session-runtime/agent-tool-routing.mjs +19 -13
  403. package/src/session-runtime/boot/apis.mjs +19 -5
  404. package/src/session-runtime/boot/facade.mjs +1 -0
  405. package/src/session-runtime/boot/infrastructure.mjs +12 -1
  406. package/src/session-runtime/boot/session.mjs +0 -1
  407. package/src/session-runtime/boot/tools.mjs +4 -1
  408. package/src/session-runtime/cwd-plugins/cwd-apply.mjs +12 -5
  409. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +65 -0
  410. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -1
  411. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +11 -4
  412. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +115 -18
  413. package/src/session-runtime/internal-tool-executor.mjs +6 -2
  414. package/src/session-runtime/lifecycle/cold-heir.mjs +188 -0
  415. package/src/session-runtime/lifecycle/inheritance.mjs +71 -27
  416. package/src/session-runtime/lifecycle/session-catalog.mjs +14 -3
  417. package/src/session-runtime/lifecycle/session-switch/resume.mjs +6 -0
  418. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +29 -6
  419. package/src/session-runtime/lifecycle/teardown.mjs +73 -8
  420. package/src/session-runtime/lifecycle-api.mjs +1 -0
  421. package/src/session-runtime/mcp-glue/connect.mjs +46 -14
  422. package/src/session-runtime/mcp-glue/server-catalog.mjs +20 -3
  423. package/src/session-runtime/mcp-glue.mjs +3 -0
  424. package/src/session-runtime/mcp-oauth-login.mjs +174 -0
  425. package/src/session-runtime/model-route/model-switch-target.mjs +34 -0
  426. package/src/session-runtime/model-route/set-route.mjs +85 -17
  427. package/src/session-runtime/model-route/web-search-route.mjs +1 -1
  428. package/src/session-runtime/native-web-search/routes.mjs +22 -3
  429. package/src/session-runtime/native-web-search-usage.mjs +51 -0
  430. package/src/session-runtime/native-web-search.mjs +10 -1
  431. package/src/session-runtime/new-session-config.mjs +4 -2
  432. package/src/session-runtime/provider-auth/login.mjs +4 -0
  433. package/src/session-runtime/provider-models/model-meta.mjs +12 -3
  434. package/src/session-runtime/quick-model-rows/web-search-rows.mjs +13 -8
  435. package/src/session-runtime/quick-web-search-models.mjs +13 -38
  436. package/src/session-runtime/resource-extension-sync.mjs +4 -0
  437. package/src/session-runtime/resource-mcp-api.mjs +98 -2
  438. package/src/session-runtime/resource-scope-decorate.mjs +10 -3
  439. package/src/session-runtime/runtime-review-api.mjs +21 -2
  440. package/src/session-runtime/services/agent-tool/index-write-queue.mjs +81 -20
  441. package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +13 -1
  442. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +7 -2
  443. package/src/session-runtime/services/agent-tool/lead-worker-index/forgotten-leads.mjs +29 -0
  444. package/src/session-runtime/services/agent-tool/lead-worker-index/index-file.mjs +1 -1
  445. package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +73 -7
  446. package/src/session-runtime/services/agent-tool/send-flow.mjs +22 -4
  447. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +2 -0
  448. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +22 -5
  449. package/src/session-runtime/services/agent-tool/tag-registry.mjs +15 -1
  450. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +8 -1
  451. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -2
  452. package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +10 -1
  453. package/src/session-runtime/services/agent-tool/worker-index.mjs +46 -7
  454. package/src/session-runtime/services/agent-tool/worker-rows.mjs +17 -0
  455. package/src/session-runtime/services/agent-tool.mjs +3 -0
  456. package/src/session-runtime/services/channel-admin.mjs +19 -3
  457. package/src/session-runtime/services/memory-runtime-proxy/calls.mjs +4 -0
  458. package/src/session-runtime/services/memory-runtime-proxy.mjs +1 -0
  459. package/src/session-runtime/services/provider-admin.mjs +81 -15
  460. package/src/session-runtime/session-transcript-purge.mjs +32 -0
  461. package/src/session-runtime/session-turn-api.mjs +14 -5
  462. package/src/session-runtime/settings-builtin-tools-api.mjs +9 -0
  463. package/src/session-runtime/settings-compaction-api.mjs +1 -0
  464. package/src/session-runtime/setup-tool/executor.mjs +36 -6
  465. package/src/session-runtime/setup-tool/extended-actions.mjs +86 -6
  466. package/src/session-runtime/setup-tool/settings-contract.mjs +13 -3
  467. package/src/session-runtime/setup-tool/tool-defs.mjs +13 -4
  468. package/src/session-runtime/skills-api.mjs +3 -5
  469. package/src/session-runtime/tool-surface.mjs +5 -1
  470. package/src/session-runtime/turn/compaction-gate.mjs +25 -0
  471. package/src/session-runtime/turn/session-ops.mjs +7 -5
  472. package/src/session-runtime/turn/side-question-ops.mjs +22 -0
  473. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +5 -1
  474. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +8 -3
  475. package/src/session-runtime/turn/turn-run/turn-settle.mjs +10 -0
  476. package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
  477. package/src/session-runtime/turn/turn-run.mjs +25 -9
  478. package/src/session-runtime/usage-stats-api.mjs +43 -11
  479. package/src/session-runtime/workspace/project-branches.mjs +7 -5
  480. package/src/session-runtime/workspace/transition-steps.mjs +129 -31
  481. package/src/session-runtime/workspace/transitions.mjs +190 -15
  482. package/src/standalone/daemon-agent-control.mjs +9 -1
  483. package/src/standalone/daemon-drain-policy.mjs +8 -0
  484. package/src/standalone/daemon-local-session-bridge.mjs +14 -1
  485. package/src/standalone/daemon-owner.mjs +17 -4
  486. package/src/standalone/daemon-pg-stop.mjs +51 -0
  487. package/src/standalone/daemon-stop-budget.d.mts +2 -0
  488. package/src/standalone/daemon-stop-budget.mjs +18 -0
  489. package/src/standalone/daemon-stored-session-views.mjs +2 -0
  490. package/src/standalone/daemon-telemetry.mjs +74 -0
  491. package/src/standalone/daemon.mjs +64 -8
  492. package/src/standalone/desktop-service-registry.mjs +5 -0
  493. package/src/standalone/local-session-runtime.mjs +70 -19
  494. package/src/standalone/session-client.mjs +42 -0
  495. package/src/standalone/session-protocol.mjs +14 -0
  496. package/src/standalone/session-proxy/projection.mjs +41 -4
  497. package/src/standalone/session-runtime-inline-host/boot-loaders.mjs +15 -1
  498. package/src/standalone/session-runtime-inline-host.mjs +1 -0
  499. package/src/standalone/session-service/agent-tree/agent-cancel.mjs +33 -2
  500. package/src/standalone/session-service/agent-tree/agent-turns.mjs +26 -10
  501. package/src/standalone/session-service/agent-tree.mjs +12 -0
  502. package/src/standalone/session-service/quota-resume.mjs +8 -0
  503. package/src/standalone/session-service/retention/busy.mjs +15 -1
  504. package/src/standalone/session-service/session-calls/session-action.mjs +18 -3
  505. package/src/standalone/session-service-api.mjs +11 -1
  506. package/src/standalone/session-service.mjs +1 -0
  507. package/src/standalone/session-state-patch.d.mts +2 -1
  508. package/src/standalone/session-state-patch.mjs +41 -1
  509. package/src/tui/App.jsx +25 -0
  510. package/src/tui/app/app-view/floating-panel.jsx +26 -1
  511. package/src/tui/app/app-view.jsx +1 -0
  512. package/src/tui/app/btw-controller.mjs +118 -0
  513. package/src/tui/app/create-app-pickers.mjs +6 -0
  514. package/src/tui/app/extension-pickers/mcp-login.mjs +107 -0
  515. package/src/tui/app/extension-pickers/mcp-servers-picker.mjs +2 -1
  516. package/src/tui/app/extension-pickers.mjs +2 -1
  517. package/src/tui/app/model-options.mjs +11 -16
  518. package/src/tui/app/shell-layout/row-budgets.mjs +11 -1
  519. package/src/tui/app/shell-layout/surfaces.mjs +3 -1
  520. package/src/tui/app/slash-commands.mjs +20 -5
  521. package/src/tui/app/slash-dispatch/panel-commands.mjs +16 -1
  522. package/src/tui/app/slash-dispatch/session-commands.mjs +19 -6
  523. package/src/tui/app/use-global-key-input/approval-shortcut.mjs +12 -0
  524. package/src/tui/app/use-global-key-input.mjs +5 -12
  525. package/src/tui/app/use-prompt-handlers.mjs +11 -0
  526. package/src/tui/components/BtwPanel.jsx +53 -0
  527. package/src/tui/components/QuestionPanel.jsx +275 -0
  528. package/src/tui/components/StatusLine.jsx +2 -0
  529. package/src/tui/components/question-panel-model.mjs +156 -0
  530. package/src/tui/components/status-line/local-line.mjs +5 -2
  531. package/src/tui/components/status-line/use-statusline-text.mjs +7 -1
  532. package/src/tui/dist/index.mjs +3221 -1500
  533. package/src/tui/session/context-state.mjs +4 -2
  534. package/src/tui/session/quota-resume.mjs +46 -14
  535. package/src/tui/session/session-api/commands.mjs +43 -24
  536. package/src/tui/session/session-api/controls.mjs +2 -2
  537. package/src/tui/session/session-api/extensions.mjs +22 -0
  538. package/src/tui/session/session-api/integrations.mjs +16 -2
  539. package/src/tui/session/session-api/lifecycle/dispose.mjs +1 -0
  540. package/src/tui/session/session-api/lifecycle.mjs +7 -2
  541. package/src/tui/session/session-api/routes.mjs +71 -7
  542. package/src/tui/session/session-api/settings.mjs +11 -14
  543. package/src/tui/session/session-api.mjs +1 -0
  544. package/src/tui/session/session-flow/drain.mjs +7 -1
  545. package/src/tui/session/session-flow/failed-turn-rewind.mjs +10 -4
  546. package/src/tui/session/tool-approval.mjs +10 -7
  547. package/src/tui/session/transcript-spill.mjs +14 -7
  548. package/src/tui/session/turn-stream.mjs +174 -31
  549. package/src/tui/session/turn.mjs +46 -16
  550. package/src/tui/statusline-ansi-bridge.mjs +1 -1
  551. package/src/ui/statusline-agents.mjs +15 -4
  552. package/src/ui/statusline.mjs +7 -3
  553. package/src/ui/streaming-markdown-heal.mjs +34 -3
  554. package/src/vendor/statusline/src/gateway/claude-current.mjs +14 -4
  555. package/src/workflows/default/WORKFLOW.md +3 -1
  556. package/src/runtime/memory/lib/memory-session-merge.mjs +0 -32
  557. package/src/runtime/shared/llm/usage-session-history.mjs +0 -266
@@ -18,6 +18,10 @@ export { TOOL_ASYNC_EXECUTION_CONTRACT, TOOL_MANUAL_CONTROL_CONTRACT, TOOL_SYNC_
18
18
  const TASK_TTL_MS = 30 * 60_000;
19
19
  const MAX_TASKS = 300;
20
20
  const TERMINAL_STATUSES = new Set(['completed', 'failed', 'cancelled']);
21
+ // A `notifyOnCancel` task reports its cancellation to the owner once, after its
22
+ // run settles, so the notification can carry what the run produced before it
23
+ // stopped. A run that never settles still reports, header-only, after this grace.
24
+ const CANCEL_NOTIFY_GRACE_MS = 10_000;
21
25
  const tasks = new Map();
22
26
  let seq = 0;
23
27
 
@@ -151,6 +155,7 @@ export function registerBackgroundTask({
151
155
  resultType,
152
156
  renderResult,
153
157
  cancel,
158
+ notifyOnCancel = false,
154
159
  } = {}) {
155
160
  pruneTasks();
156
161
  const explicitId = clean(taskId);
@@ -189,6 +194,7 @@ export function registerBackgroundTask({
189
194
  resultType: clean(resultType) || null,
190
195
  renderResult: typeof renderResult === 'function' ? renderResult : null,
191
196
  cancel: typeof cancel === 'function' ? cancel : null,
197
+ notifyOnCancel: notifyOnCancel === true,
192
198
  };
193
199
  tasks.set(id, task);
194
200
  return task;
@@ -200,10 +206,14 @@ export function startBackgroundTask(options = {}) {
200
206
  .then(() => options.run?.())
201
207
  .then((result) => {
202
208
  completeBackgroundTask(task.taskId, { status: 'completed', result, terminalReason: 'run-resolved' });
209
+ deliverCancelledNotification(task);
203
210
  return result;
204
211
  })
205
212
  .catch((error) => {
206
- if (task.status === 'cancelled') return null;
213
+ if (task.status === 'cancelled') {
214
+ deliverCancelledNotification(task);
215
+ return null;
216
+ }
207
217
  completeBackgroundTask(task.taskId, {
208
218
  status: 'failed',
209
219
  error,
@@ -258,15 +268,62 @@ export function cleanupBackgroundTasks(options = {}) {
258
268
  return { removed: before - after, tasks: after };
259
269
  }
260
270
 
261
- export function cancelBackgroundTask(taskId, reason = 'cancelled') {
271
+ // `notify`: true notifies immediately, false never notifies (owner teardown).
272
+ // Left unset, a `notifyOnCancel` task defers its single `cancelled`
273
+ // notification until its run settles (see settleCancelledBackgroundTask); any
274
+ // other task stays silent.
275
+ function cancelRunningTask(task, reason, notify) {
276
+ try {
277
+ task.cancel?.();
278
+ } catch {}
279
+ completeBackgroundTask(task.taskId, { status: 'cancelled', error: reason, notify: notify === true });
280
+ if (task.notifyOnCancel !== true || notify === true || task.status !== 'cancelled' || task.cancelNotify) return;
281
+ if (notify === false) {
282
+ task.cancelNotify = 'silent';
283
+ return;
284
+ }
285
+ task.cancelNotify = 'pending';
286
+ task.cancelNotifyTimer = setTimeout(() => deliverCancelledNotification(task), CANCEL_NOTIFY_GRACE_MS);
287
+ task.cancelNotifyTimer.unref?.();
288
+ }
289
+
290
+ // Owner teardown/delete (`notify: false`) also silences a cancellation that is
291
+ // still waiting for its run to settle.
292
+ function resolvePendingCancelNotify(task, next) {
293
+ if (task.cancelNotify !== 'pending') return false;
294
+ clearTimeout(task.cancelNotifyTimer);
295
+ task.cancelNotifyTimer = null;
296
+ task.cancelNotify = next;
297
+ return true;
298
+ }
299
+
300
+ function silenceCancelledNotification(task) {
301
+ resolvePendingCancelNotify(task, 'silent');
302
+ }
303
+
304
+ function deliverCancelledNotification(task) {
305
+ if (!resolvePendingCancelNotify(task, 'delivered')) return false;
306
+ if (tasks.get(task.taskId) !== task) return false;
307
+ task.meta = sanitizeTaskMeta(task.meta);
308
+ return notifyTaskCompletion(task, task.notificationInstruction);
309
+ }
310
+
311
+ export function cancelBackgroundTask(taskId, reason = 'cancelled', { notify } = {}) {
262
312
  const task = getBackgroundTask(taskId);
263
313
  if (!task) return null;
264
- if (!TERMINAL_STATUSES.has(task.status)) {
265
- try {
266
- task.cancel?.();
267
- } catch {}
268
- completeBackgroundTask(task.taskId, { status: 'cancelled', error: reason, notify: false });
269
- }
314
+ if (!TERMINAL_STATUSES.has(task.status)) cancelRunningTask(task, reason, notify);
315
+ else if (notify === false) silenceCancelledNotification(task);
316
+ return task;
317
+ }
318
+
319
+ /** The cancelled run has stopped: attach what it produced before stopping (if
320
+ * any) and deliver the task's single deferred `cancelled` notification. A
321
+ * no-op unless that notification is still pending. */
322
+ export function settleCancelledBackgroundTask(taskId, { result } = {}) {
323
+ const task = getBackgroundTask(taskId);
324
+ if (task?.status !== 'cancelled' || task.cancelNotify !== 'pending') return null;
325
+ if (result !== undefined) task.result = result;
326
+ deliverCancelledNotification(task);
270
327
  return task;
271
328
  }
272
329
 
@@ -280,11 +337,11 @@ export function cancelBackgroundTasks(options = {}) {
280
337
  // visible/recoverable, but may not be claimed by whichever session happens
281
338
  // to close first.
282
339
  if (!taskMatchesScope(task, options, { includeUnattributed: false })) continue;
283
- if (TERMINAL_STATUSES.has(task.status)) continue;
284
- try {
285
- task.cancel?.();
286
- } catch {}
287
- completeBackgroundTask(task.taskId, { status: 'cancelled', error: reason, notify: options.notify === true });
340
+ if (TERMINAL_STATUSES.has(task.status)) {
341
+ if (options.notify === false) silenceCancelledNotification(task);
342
+ continue;
343
+ }
344
+ cancelRunningTask(task, reason, options.notify);
288
345
  cancelled += 1;
289
346
  }
290
347
  return { cancelled };
@@ -358,10 +415,26 @@ export function completeBackgroundTask(
358
415
  return task;
359
416
  }
360
417
 
418
+ // Surfaces whose later reads consume the structured result object itself:
419
+ // shell `task read` re-reads output via result paths and renders exit_code /
420
+ // timed_out / signal; agent job views return `job.result`. They keep it for the
421
+ // TTL; every other surface keeps only resultText + metadata once delivered.
422
+ const RESULT_KEPT_SURFACES = new Set(['shell', 'agent']);
423
+
424
+ function releaseDeliveredResult(task, body) {
425
+ if (task.result === undefined || RESULT_KEPT_SURFACES.has(task.surface)) return;
426
+ // Pin the rendered body first so re-renders and retries stay identical.
427
+ if (!task.resultText) task.resultText = body;
428
+ task.result = undefined;
429
+ }
430
+
361
431
  export function notifyTaskCompletion(task, instruction) {
362
432
  if (!task) return false;
363
433
  if (!TERMINAL_STATUSES.has(task.status)) return false;
364
434
  if (task.completionAcknowledged === true) return false;
435
+ // A deferred cancellation is delivered only by deliverCancelledNotification;
436
+ // a silent (owner-teardown) cancellation never notifies.
437
+ if (task.cancelNotify === 'pending' || task.cancelNotify === 'silent') return false;
365
438
  // `notified` tracks whether *any* completion notification was sent (e.g. an
366
439
  // early header-only preview from the agent surface). A later call that can
367
440
  // finally include the result body must still fire once, otherwise the owner
@@ -396,7 +469,10 @@ export function notifyTaskCompletion(task, instruction) {
396
469
  });
397
470
  if (sent) {
398
471
  task.notified = true;
399
- if (hasBody) task.notifiedWithBody = true;
472
+ if (hasBody) {
473
+ task.notifiedWithBody = true;
474
+ releaseDeliveredResult(task, body);
475
+ }
400
476
  }
401
477
  return sent;
402
478
  }
@@ -457,6 +533,8 @@ export function renderBackgroundTaskNotification(task) {
457
533
  status: task.status,
458
534
  result,
459
535
  error: task.error,
536
+ // A cancelled Agent's header names the cancel reason.
537
+ detail: task.surface === 'agent' && task.status === 'cancelled' ? task.error : null,
460
538
  });
461
539
  }
462
540
 
@@ -156,6 +156,19 @@ export function readBoundedFile(
156
156
  }
157
157
  }
158
158
 
159
+ /**
160
+ * A file's state for the turn review of a tool write: null when absent, its
161
+ * bytes (binary included) up to PATCH_EDIT_MAX_BYTES and within `budget`,
162
+ * otherwise an omitted marker — counted as changed, never diffed or restored.
163
+ * Throws ENOTREGULAR for anything but a regular file.
164
+ */
165
+ export function reviewFileContent(path, { budget = null } = {}) {
166
+ const read = readBoundedFile(path, { maxBytes: PATCH_EDIT_MAX_BYTES, budget, detectBinary: false });
167
+ if (read.state === 'absent') return null;
168
+ if (read.state === 'omitted') return { omitted: true, size: read.size, reason: read.reason };
169
+ return read.content;
170
+ }
171
+
159
172
  /** Human phrase for an omitted read: "binary (N bytes)", "larger than 8 MiB (N bytes)". */
160
173
  export function describeOmittedFile({ reason, size, limit } = {}) {
161
174
  const bytes = `${Number(size) || 0} bytes`;
@@ -0,0 +1,182 @@
1
+ // In-process line diffs for the turn-change paths, bounded the way codex
2
+ // bounds its own: every file gets a short timeout, every operation (one tool
3
+ // call, one review or seal pass) a total budget. A file over the size limits,
4
+ // past its timeout or past the spent budget gets one replacement hunk between
5
+ // its common line prefix and suffix instead — still an exact patch that
6
+ // applies, only not a minimal one. Results are cached by content, so the same
7
+ // pair is not diffed again across calls and reviews.
8
+ import { createHash } from 'node:crypto';
9
+ import { performance } from 'node:perf_hooks';
10
+ import { formatPatch, structuredPatch } from 'diff';
11
+
12
+ /** Longest a single file's Myers diff may run (codex's per-file value). */
13
+ export const LINE_DIFF_FILE_TIMEOUT_MS = 100;
14
+ /** Total Myers diff time one operation may spend across its files. */
15
+ // Kept well under the lag target: an operation also renders, hashes and
16
+ // bounds its patches outside the diff itself.
17
+ export const LINE_DIFF_OPERATION_BUDGET_MS = 150;
18
+ // A side over either limit is never Myers-diffed.
19
+ const LINE_DIFF_MAX_CHARS = 1024 * 1024;
20
+ const LINE_DIFF_MAX_LINES = 20_000;
21
+ const CONTEXT_LINES = 3;
22
+ const CACHE_MAX_ENTRIES = 512;
23
+ // Cached hunks reference their texts: bound the cache by their length.
24
+ const CACHE_MAX_CHARS = 32 * 1024 * 1024;
25
+
26
+ /** One operation's diff budget; pass it to every lineDiffHunks call of it. */
27
+ export function createLineDiffBudget(totalMs = LINE_DIFF_OPERATION_BUDGET_MS) {
28
+ return { remainingMs: totalMs };
29
+ }
30
+
31
+ const _cache = new Map();
32
+ let _cachedChars = 0;
33
+ const _hashByBytes = new WeakMap();
34
+
35
+ const sha1 = (value) => createHash('sha1').update(value).digest('hex');
36
+
37
+ /** Content hash of one side: a byte array is hashed once per object. A text
38
+ * hashes to the same id as its UTF-8 bytes; one that has no exact UTF-8 form
39
+ * (a lone surrogate) gets no id. */
40
+ function sideHash(source, text) {
41
+ if (source && typeof source === 'object') {
42
+ let hash = _hashByBytes.get(source);
43
+ if (!hash) {
44
+ hash = sha1(source);
45
+ _hashByBytes.set(source, hash);
46
+ }
47
+ return hash;
48
+ }
49
+ return text.isWellFormed() ? sha1(text) : null;
50
+ }
51
+
52
+ function cacheKey(sources, oldText, newText) {
53
+ const before = sideHash(sources[0], oldText);
54
+ const after = before && sideHash(sources[1], newText);
55
+ return after ? `${before}:${after}` : null;
56
+ }
57
+
58
+ function cacheGet(key) {
59
+ const entry = _cache.get(key);
60
+ if (!entry) return null;
61
+ _cache.delete(key);
62
+ _cache.set(key, entry);
63
+ return entry.result;
64
+ }
65
+
66
+ function cacheSet(key, result, chars) {
67
+ if (chars > CACHE_MAX_CHARS) return;
68
+ _cachedChars -= _cache.get(key)?.chars || 0;
69
+ _cache.delete(key);
70
+ _cache.set(key, { result, chars });
71
+ _cachedChars += chars;
72
+ while (_cache.size > CACHE_MAX_ENTRIES || _cachedChars > CACHE_MAX_CHARS) {
73
+ const [oldest, entry] = _cache.entries().next().value;
74
+ _cache.delete(oldest);
75
+ _cachedChars -= entry.chars;
76
+ }
77
+ }
78
+
79
+ function overLimits(text) {
80
+ if (text.length > LINE_DIFF_MAX_CHARS) return true;
81
+ let lines = 0;
82
+ for (let index = text.indexOf('\n'); index >= 0; index = text.indexOf('\n', index + 1)) {
83
+ lines += 1;
84
+ if (lines > LINE_DIFF_MAX_LINES) return true;
85
+ }
86
+ return false;
87
+ }
88
+
89
+ /** Lines with their `\n` (the last one may lack it), as jsdiff tokenizes. */
90
+ function lineTokens(text) {
91
+ if (!text) return [];
92
+ const lines = text.split('\n');
93
+ const tokens = lines.map((line, index) => (index < lines.length - 1 ? `${line}\n` : line));
94
+ if (tokens.at(-1) === '') tokens.pop();
95
+ return tokens;
96
+ }
97
+
98
+ /** The one hunk replacing everything between the texts' common line prefix
99
+ * and suffix, with up to three context lines each side, in jsdiff's hunk
100
+ * shape (`` included). */
101
+ function replacementHunks(oldText, newText) {
102
+ const before = lineTokens(oldText);
103
+ const after = lineTokens(newText);
104
+ const shortest = Math.min(before.length, after.length);
105
+ let start = 0;
106
+ while (start < shortest && before[start] === after[start]) start += 1;
107
+ let end = 0;
108
+ while (end < shortest - start && before[before.length - 1 - end] === after[after.length - 1 - end]) end += 1;
109
+ const lead = Math.min(CONTEXT_LINES, start);
110
+ const trail = Math.min(CONTEXT_LINES, end);
111
+ const lines = [];
112
+ const push = (mark, tokens) => {
113
+ for (const token of tokens) {
114
+ if (token.endsWith('\n')) lines.push(mark + token.slice(0, -1));
115
+ else lines.push(mark + token, '\');
116
+ }
117
+ };
118
+ push(' ', before.slice(start - lead, start));
119
+ push('-', before.slice(start, before.length - end));
120
+ push('+', after.slice(start, after.length - end));
121
+ push(' ', before.slice(before.length - end, before.length - end + trail));
122
+ const first = start - lead;
123
+ return [
124
+ {
125
+ oldStart: first + 1,
126
+ oldLines: before.length - end + trail - first,
127
+ newStart: first + 1,
128
+ newLines: after.length - end + trail - first,
129
+ lines,
130
+ },
131
+ ];
132
+ }
133
+
134
+ /**
135
+ * Hunks (context 3, jsdiff's structuredPatch shape) turning `oldText` into
136
+ * `newText`: jsdiff's own when its diff finishes within the file timeout and
137
+ * what is left of `budget` (`exact`), else the replacement hunk. `sources`
138
+ * are the raw sides the texts came from (bytes or the texts), used only as
139
+ * the cache key. The returned value is shared: never mutate it.
140
+ */
141
+ export function lineDiffHunks(oldText, newText, budget, sources = [oldText, newText]) {
142
+ if (oldText === newText) return { hunks: [], exact: true };
143
+ const key = cacheKey(sources, oldText, newText);
144
+ const cached = key ? cacheGet(key) || budget.fallbacks?.get(key) : null;
145
+ if (cached) return cached;
146
+ let result = null;
147
+ // A fallback forced by the operation's spent budget is not this pair's own
148
+ // outcome: it is not cached, so a later operation can still diff it.
149
+ let repeatable = true;
150
+ if (!overLimits(oldText) && !overLimits(newText)) {
151
+ if (budget.remainingMs > 0) {
152
+ const timeout = Math.min(LINE_DIFF_FILE_TIMEOUT_MS, budget.remainingMs);
153
+ const startedAt = performance.now();
154
+ const patch = structuredPatch('', '', oldText, newText, '', '', { context: CONTEXT_LINES, timeout });
155
+ budget.remainingMs -= performance.now() - startedAt;
156
+ if (patch) result = { hunks: patch.hunks, exact: true };
157
+ else repeatable = timeout >= LINE_DIFF_FILE_TIMEOUT_MS;
158
+ } else {
159
+ repeatable = false;
160
+ }
161
+ }
162
+ result ||= { hunks: replacementHunks(oldText, newText), exact: false };
163
+ if (key && repeatable) cacheSet(key, result, oldText.length + newText.length);
164
+ // Within its own operation it is final: the budget never grows back.
165
+ else if (key) (budget.fallbacks ||= new Map()).set(key, result);
166
+ return result;
167
+ }
168
+
169
+ /** The `--- `/`+++ ` headers and hunks of `hunks`, as jsdiff formats them,
170
+ * ending in exactly one newline. */
171
+ export function unifiedHunksBody(oldHeader, newHeader, hunks) {
172
+ const generated = formatPatch({ oldFileName: oldHeader, newFileName: newHeader, oldHeader: '', newHeader: '', hunks });
173
+ let end = generated.length;
174
+ // Linear trim: a /\n*$/ regex rescans from every position of a large patch.
175
+ while (end > 0 && generated.charCodeAt(end - 1) === 10) end -= 1;
176
+ return `${generated.slice(generated.search(/^--- /m), end)}\n`;
177
+ }
178
+
179
+ export function _resetLineDiffCacheForTest() {
180
+ _cache.clear();
181
+ _cachedChars = 0;
182
+ }
@@ -175,6 +175,24 @@ export function appendBuffered(path, text) {
175
175
  }
176
176
  }
177
177
 
178
+ /**
179
+ * Drop every not-yet-written chunk for `path` (its file is being deleted, so a
180
+ * later flush must not recreate it). A write already handed to the OS cannot
181
+ * be recalled; callers unlink after this and it lands before or is removed.
182
+ */
183
+ export function discardBuffered(path) {
184
+ const q = queues.get(path);
185
+ if (!q) return;
186
+ if (q.timer) {
187
+ clearTimeout(q.timer);
188
+ q.timer = null;
189
+ }
190
+ q.chunks = [];
191
+ q.bytes = 0;
192
+ q.failCount = 0;
193
+ deleteIfIdle(path, q);
194
+ }
195
+
178
196
  /** Observable overflow counters for buffered append queues. */
179
197
  export function getBufferedAppenderStats(path) {
180
198
  const q = queues.get(path);
@@ -18,8 +18,10 @@ import {
18
18
  backupUserData,
19
19
  backupUserDataAsync,
20
20
  markUserDataInitialized,
21
+ ensureUserDataInitialized,
21
22
  loadLatestMixdogConfigFromBackup,
22
- hasUserDataInitMarker,
23
+ isUserDataRecoveryExpected,
24
+ findLegacyUserDataEvidence,
23
25
  } from './user-data-guard.mjs';
24
26
  import { AGENT_PROVIDER_ENV, AGENT_PROVIDER_ENV_ALIASES, getAgentApiKey } from './provider-api-key.mjs';
25
27
 
@@ -236,7 +238,7 @@ function readConfigFile() {
236
238
  }
237
239
  }
238
240
 
239
- function writeConfigFile(data) {
241
+ function writeConfigFile(data, postBackupReason = 'post-config-write') {
240
242
  // R4 data-at-rest: mixdog-config.json holds provider apiKeys; clamp to
241
243
  // owner-only on POSIX via 0o600/0o700 mode bits, AND fail-closed on
242
244
  // Windows where those bits are advisory — `secret: true` makes the
@@ -261,29 +263,76 @@ function writeConfigFile(data) {
261
263
  markUserDataInitialized(resolvePluginData());
262
264
  } catch {}
263
265
  try {
264
- backupUserData(resolvePluginData(), 'post-config-write');
266
+ backupUserData(resolvePluginData(), postBackupReason);
265
267
  } catch {}
266
268
  }
267
269
 
270
+ // Config paths whose restore could not be persisted: the in-memory restore is
271
+ // still served, but the failure is logged (and the write retried) only once
272
+ // per process instead of on every read.
273
+ const _restorePersistFailed = new Set();
274
+
275
+ /**
276
+ * Write a config restored on a plain read back to disk through the normal save
277
+ * path (atomic write + backups), so later reads hit the file instead of
278
+ * restoring again. Try-once lock: a contended lock means a writer (possibly
279
+ * this process's own outer RMW) is about to persist, so the next read retries.
280
+ */
281
+ function persistRestoredConfig(restored) {
282
+ const path = configPath();
283
+ if (_restorePersistFailed.has(path)) return;
284
+ try {
285
+ const wrote = withFileLockSync(
286
+ `${path}.lock`,
287
+ () => {
288
+ if (existsSync(path)) return false;
289
+ writeAll(restored, 'post-restore');
290
+ return true;
291
+ },
292
+ { timeoutMs: 0 }
293
+ );
294
+ if (wrote) {
295
+ process.stderr.write(
296
+ '[config] read: restored mixdog-config.json from latest user-data backup (missing after init) and saved it\n'
297
+ );
298
+ }
299
+ } catch (err) {
300
+ if (err?.code === 'ELOCKCONTENDED') return;
301
+ _restorePersistFailed.add(path);
302
+ process.stderr.write(
303
+ `[config] read: restored mixdog-config.json from latest user-data backup (missing after init); saving it failed, serving it from memory: ${err?.message || err}\n`
304
+ );
305
+ }
306
+ }
307
+
268
308
  function readAll() {
269
309
  const parsed = readConfigFile();
270
- if (parsed != null) return canonicalizeUnifiedConfig(parsed);
310
+ if (parsed != null) {
311
+ // A data dir from before the in-dir init marker gets one (plus a first
312
+ // scoped snapshot) once per process, so its config stays recoverable.
313
+ try {
314
+ ensureUserDataInitialized(resolvePluginData());
315
+ } catch {}
316
+ return canonicalizeUnifiedConfig(parsed);
317
+ }
271
318
  // Symmetric with readAllForRmW(): a missing/unreadable config on a data
272
319
  // dir that was previously initialized means the file was LOST (deleted,
273
- // failed write), not a fresh install. Self-heal from the newest
320
+ // failed write), not a fresh install. A recreated data dir has no marker
321
+ // and starts fresh. Self-heal from this data dir's newest
274
322
  // structurally-complete backup instead of silently collapsing to {} —
275
323
  // which callers (readSection/getCapabilities) merge with in-memory
276
- // defaults, dropping the user's presets/agent routes/sections. In-memory only:
277
- // readAll() runs OUTSIDE the config lock (unlike the RMW path), so we do
278
- // not write here to avoid an unlocked-write race; the next updateSection
279
- // re-persists the file under the lock.
280
- if (hasUserDataInitMarker(resolvePluginData())) {
324
+ // defaults, dropping the user's presets/agent routes/sections. The restore
325
+ // is persisted under the config lock (only if the file is still missing) so
326
+ // later reads hit the file instead of restoring and logging again.
327
+ // An upgraded data dir (legacy marker / matching legacy backup, no in-dir
328
+ // marker yet) is a recovery case too; without a verifiable backup reads stay
329
+ // empty and RMW writes refuse (see readAllForRmW).
330
+ if (isUserDataRecoveryExpected(resolvePluginData())) {
281
331
  const restored = loadLatestMixdogConfigFromBackup(resolvePluginData());
282
332
  if (restored && isPlainObject(restored)) {
283
- process.stderr.write(
284
- '[config] read: restored mixdog-config.json from latest user-data backup (missing after init)\n'
285
- );
286
- return canonicalizeUnifiedConfig(restored);
333
+ const config = canonicalizeUnifiedConfig(restored);
334
+ persistRestoredConfig(config);
335
+ return config;
287
336
  }
288
337
  }
289
338
  return {};
@@ -300,11 +349,22 @@ function quarantineMalformedConfig(parseErr) {
300
349
  }
301
350
 
302
351
  function restoreAllForRmWOrThrow(reason) {
303
- const restored = loadLatestMixdogConfigFromBackup(resolvePluginData());
352
+ const restored = loadLatestMixdogConfigFromBackup(resolvePluginData(), { fresh: true });
304
353
  if (restored && isPlainObject(restored)) {
305
354
  process.stderr.write(`[config] RMW read: restored mixdog-config.json from latest user-data backup (${reason})\n`);
306
355
  return restored;
307
356
  }
357
+ const legacy = findLegacyUserDataEvidence(resolvePluginData(), { fresh: true });
358
+ if (legacy) {
359
+ const err = new Error(
360
+ `[config] mixdog-config.json is missing but this data dir (${resolvePluginData()}) was initialized by an older ` +
361
+ `Mixdog build and no backup verified to belong to it was found; refusing to write a fresh config over a ` +
362
+ `recoverable state. Restore mixdog-config.json from a backup under ${legacy.roots.join(' or ')}, or delete ` +
363
+ `the legacy marker(s) to start fresh: ${legacy.markers.join(', ') || '(none; matching legacy backups are incomplete)'}`
364
+ );
365
+ err.code = 'EUSERDATARECOVERY';
366
+ throw err;
367
+ }
308
368
  throw new Error(
309
369
  `[config] mixdog-config.json is unreadable and no valid backup was found (${reason}); refusing section write that would wipe other sections`
310
370
  );
@@ -321,7 +381,7 @@ function readAllForRmW() {
321
381
  raw = readFileSync(configPath(), 'utf8');
322
382
  } catch (err) {
323
383
  if (err.code === 'ENOENT') {
324
- if (hasUserDataInitMarker(resolvePluginData())) {
384
+ if (isUserDataRecoveryExpected(resolvePluginData(), { fresh: true })) {
325
385
  return restoreAllForRmWOrThrow('config file missing after user-data was initialized');
326
386
  }
327
387
  return {};
@@ -402,8 +462,8 @@ export function subscribeConfigChange(listener) {
402
462
  };
403
463
  }
404
464
 
405
- function writeAll(data) {
406
- writeConfigFile(canonicalizeUnifiedConfig(data));
465
+ function writeAll(data, postBackupReason) {
466
+ writeConfigFile(canonicalizeUnifiedConfig(data), postBackupReason);
407
467
  notifyConfigChanged();
408
468
  }
409
469
 
@@ -180,9 +180,34 @@ export function isCancelLikeError(error) {
180
180
  );
181
181
  }
182
182
 
183
+ // One-line reason per provider kind. Fixed sentences (optionally with a
184
+ // parenthesised value) are the renderer's translation keys.
185
+ const KIND_LINES = {
186
+ overloaded: 'Provider is busy at capacity.',
187
+ rate_limited: ({ retryAfterMs }) =>
188
+ retryAfterMs ? `Provider is rate-limited (retry in ${formatDurationMs(retryAfterMs)}).` : 'Provider is rate-limited.',
189
+ quota_exhausted: 'Provider usage limit reached.',
190
+ entitlement: 'Not included in your plan.',
191
+ billing: 'Out of credits or at spend limit.',
192
+ permission: 'Access denied by the provider.',
193
+ policy: 'Refused by the provider safety policy.',
194
+ auth: 'Provider authentication failed.',
195
+ };
196
+
197
+ // Typed evidence: a persisted descriptor or structured status/code fields.
198
+ // A bare message carries none, so its wording keeps the legacy text path.
199
+ function hasTypedEvidence(error) {
200
+ if (!error || typeof error !== 'object') return false;
201
+ return Boolean(error.errorKind?.kind) || errorHttpStatus(error) > 0 || Boolean(error.code) || Boolean(error.retryAfterMs);
202
+ }
203
+
183
204
  export function presentErrorText(error, options = {}) {
184
205
  const presentation = describeError(error);
185
206
  if (['image-size', 'payload-size', 'request'].includes(presentation.kind)) return presentation.summary;
207
+ const kindLine = hasTypedEvidence(error) ? KIND_LINES[presentation.providerKind] : null;
208
+ if (kindLine && presentation.kind !== 'unknown') {
209
+ return typeof kindLine === 'function' ? kindLine(presentation) : kindLine;
210
+ }
186
211
  const surface = options.surface || options.tool || '';
187
212
  const subject = subjectForSurface(surface);
188
213
  const max = options.max ?? 240;
@@ -190,6 +215,12 @@ export function presentErrorText(error, options = {}) {
190
215
  const name = error && typeof error === 'object' ? String(error.name || '') : '';
191
216
  const code = error && typeof error === 'object' ? String(error.code || '') : '';
192
217
  const status = errorHttpStatus(error);
218
+ // The fixed request overhead error is actionable as written (sizes,
219
+ // contributors, remedy): keep its lines instead of a generic "Context too
220
+ // large." that would suggest compacting.
221
+ if (code === 'TOOL_SURFACE_EXCEEDS_CONTEXT' || /\(TOOL_SURFACE_EXCEEDS_CONTEXT\)/.test(text)) {
222
+ return stripErrorPrefix(text);
223
+ }
193
224
  const jsonError = extractJsonError(text);
194
225
  if (jsonError) text = jsonError;
195
226
  const embeddedError = extractEmbeddedError(text);