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
@@ -30,6 +30,13 @@ export { cleanupStaleTranscriptSpillDirs };
30
30
 
31
31
  let staleSpillSweepDone = false;
32
32
 
33
+ /** One sweep per process: it lists the whole temp directory synchronously. */
34
+ export function sweepStaleTranscriptSpillDirsOnce() {
35
+ if (staleSpillSweepDone) return;
36
+ staleSpillSweepDone = true;
37
+ cleanupStaleTranscriptSpillDirs();
38
+ }
39
+
33
40
  // Serialized pages deliberately release the old item object graph while
34
41
  // keeping every byte restorable. Only `items` is render-live and walkable.
35
42
  // This buffer owns the page list, the reading cursor and snapshots; the spill
@@ -47,12 +54,9 @@ export function createTranscriptSpillBuffer({
47
54
  try {
48
55
  writeOwnerRegistry();
49
56
  } catch {}
50
- // One sweep per process: it lists the whole temp directory synchronously,
51
- // and a buffer is created per session.
52
- if (!staleSpillSweepDone) {
53
- staleSpillSweepDone = true;
54
- cleanupStaleTranscriptSpillDirs();
55
- }
57
+ // A buffer is created per session; the sweep runs once per process (the
58
+ // daemon runs it in its post-ready warm-up).
59
+ sweepStaleTranscriptSpillDirsOnce();
56
60
  const pages = [];
57
61
  let cursor = null;
58
62
  let spillDir = null;
@@ -228,13 +232,16 @@ export function reindexItems(itemIndexById, items) {
228
232
  export function replaceSessionItemsState({ state, items, itemIndexById, preserveStreamingTail = false, extra = {} }) {
229
233
  const nextItems = Array.isArray(items) ? items : [];
230
234
  reindexItems(itemIndexById, nextItems);
231
- return {
235
+ const next = {
232
236
  ...state,
233
237
  ...extra,
234
238
  items: nextItems,
235
239
  structureRevision: (Number(state.structureRevision) || 0) + 1,
236
240
  streamingTail: preserveStreamingTail ? state.streamingTail : null,
237
241
  };
242
+ // The desktop's partial line belongs to the live row it continues.
243
+ if (!preserveStreamingTail && next.streamingPartial) next.streamingPartial = null;
244
+ return next;
238
245
  }
239
246
 
240
247
  // Shared by the live session runtime and focused transcript tests so revision/tail
@@ -1,6 +1,7 @@
1
1
  // Per-turn streaming state for the lead TUI turn: the assistant text
2
2
  // segments, the thinking (reasoning) timer, and the frame-batched flush that
3
- // publishes completed visible lines and thinking transitions to the store.
3
+ // publishes completed visible lines and thinking transitions to the store,
4
+ // plus the desktop-only unfinished line (`streamingPartial`, see partialNow).
4
5
  // `createTurnStream` returns one explicit state object (deps + mutable
5
6
  // scalars); every function here takes it as its first argument.
6
7
  import { TUI_FRAME_MS, yieldToRenderer } from './render-timing.mjs';
@@ -11,6 +12,10 @@ import { newTranscriptMessageId } from '../../runtime/shared/transcript-metadata
11
12
  // 16ms timer left every other terminal frame idle, so completed script lines
12
13
  // accumulated and then climbed into view in coarse two-step chunks.
13
14
  const STREAM_BATCH_INTERVAL_MS = TUI_FRAME_MS;
15
+ // The desktop's unfinished-line channel (see partialNow): at most one
16
+ // publication per interval, the first chunk after an idle gap on the next
17
+ // frame. Forced flushes (tool calls, notices, finalization) publish at once.
18
+ const PARTIAL_INTERVAL_MS = 40;
14
19
 
15
20
  // deps: getState, set, nextId, updateStreamingTail, settleStreamingTail,
16
21
  // clearStreamingTail, isCurrentTurn, transcriptMeta, routeMeta, sealToolBlock.
@@ -53,42 +58,59 @@ export function createTurnStream(deps) {
53
58
  publishedThinkingActive: false,
54
59
  // Live countdown of a reconnect wait (see startReconnectCountdown).
55
60
  reconnectTimer: null,
61
+ // The row identity (id, time, message id) claimed before the row exists,
62
+ // while only the desktop's partial line shows it; the row takes it over.
63
+ assistantIdentity: null,
64
+ // Desktop partial-line publication timer and last publish time.
65
+ partialTimer: null,
66
+ partialPublishedAt: 0,
67
+ // This segment's partial is on screen / was cleared by a transcript
68
+ // replacement (and must not come back).
69
+ partialShown: false,
70
+ partialDropped: false,
56
71
  };
57
72
  }
58
73
 
74
+ function claimAssistantIdentity(stream) {
75
+ if (stream.assistantIdentity) return stream.assistantIdentity;
76
+ const id = stream.nextId();
77
+ const meta = stream.transcriptMeta;
78
+ // A non-streaming continuation was persisted before its text reached this
79
+ // row: the runtime left that message's id and time (ask-session
80
+ // takeAssistant), and the row carries them instead of minting new ones.
81
+ const persisted = stream.claimPersistedAssistant ? meta.persistedAssistant : null;
82
+ if (persisted) meta.persistedAssistant = null;
83
+ let assistantAt = persisted?.at;
84
+ let messageId = persisted?.messageId;
85
+ if (!persisted) {
86
+ assistantAt =
87
+ Number.isFinite(Number(meta.assistantAt)) && Number(meta.assistantAt) > 0 ? Number(meta.assistantAt) : Date.now();
88
+ meta.assistantAt = assistantAt;
89
+ // The reply this segment becomes is persisted with this stable id (the
90
+ // runtime takes it with assistantAt), so a compaction that drops the
91
+ // reply greys this row.
92
+ if (!(typeof meta.assistantMessageId === 'string' && meta.assistantMessageId)) {
93
+ meta.assistantMessageId = newTranscriptMessageId();
94
+ }
95
+ messageId = meta.assistantMessageId;
96
+ }
97
+ stream.assistantIdentity = { id, at: assistantAt, messageId };
98
+ return stream.assistantIdentity;
99
+ }
100
+
59
101
  function ensureAssistant(stream, initialText = '') {
60
102
  if (!stream.currentAssistantId) {
61
- stream.currentAssistantId = stream.nextId();
62
- const meta = stream.transcriptMeta;
63
- // A non-streaming continuation was persisted before its text reached this
64
- // row: the runtime left that message's id and time (ask-session
65
- // takeAssistant), and the row carries them instead of minting new ones.
66
- const persisted = stream.claimPersistedAssistant ? meta.persistedAssistant : null;
67
- if (persisted) meta.persistedAssistant = null;
68
- let assistantAt = persisted?.at;
69
- let messageId = persisted?.messageId;
70
- if (!persisted) {
71
- assistantAt =
72
- Number.isFinite(Number(meta.assistantAt)) && Number(meta.assistantAt) > 0
73
- ? Number(meta.assistantAt)
74
- : Date.now();
75
- meta.assistantAt = assistantAt;
76
- // The reply this segment becomes is persisted with this stable id (the
77
- // runtime takes it with assistantAt), so a compaction that drops the
78
- // reply greys this row.
79
- if (!(typeof meta.assistantMessageId === 'string' && meta.assistantMessageId)) {
80
- meta.assistantMessageId = newTranscriptMessageId();
81
- }
82
- messageId = meta.assistantMessageId;
83
- }
103
+ const { id, at, messageId } = claimAssistantIdentity(stream);
104
+ stream.assistantIdentity = null;
105
+ stream.currentAssistantId = id;
84
106
  // Do NOT reset currentAssistantText here: the first onTextDelta has
85
107
  // already accumulated the opening chunk before this batched flush runs.
86
108
  // Segment resets are owned by closeAssistantSegment(). Seed the new row
87
109
  // with the already-visible text so the ● gutter and the first body line
88
110
  // appear in the SAME set()/emit() — no empty "●-only" row.
89
- stream.updateStreamingTail(stream.currentAssistantId, {
111
+ stream.updateStreamingTail(id, {
90
112
  text: String(initialText || ''),
91
- at: assistantAt,
113
+ at,
92
114
  messageId,
93
115
  ...stream.routeMeta,
94
116
  });
@@ -96,6 +118,85 @@ function ensureAssistant(stream, initialText = '') {
96
118
  return stream.currentAssistantId;
97
119
  }
98
120
 
121
+ // The desktop paints streamed text before its line completes; the terminal
122
+ // keeps the line-gated tail below. `streamingPartial` is the unfinished line
123
+ // beyond the published tail text (`from` is that text's length), cut from the
124
+ // same leak-guarded text the tail is, never raw provider output. Before the
125
+ // row's first completed line there is no tail: the partial then carries all
126
+ // visible text so far plus the row identity (`tail`) the row will take over.
127
+ // Terminal projections drop the field (session-proxy/projection.mjs).
128
+ function partialNow(stream) {
129
+ const text = stream.currentAssistantText;
130
+ const id = stream.currentAssistantId;
131
+ if (id != null) {
132
+ const tail = stream.getState().streamingTail;
133
+ if (!tail || tail.id !== id) return null;
134
+ const shown = typeof tail.text === 'string' ? tail.text : '';
135
+ if (text.length <= shown.length || !text.startsWith(shown)) return null;
136
+ return { id, from: shown.length, text: text.slice(shown.length) };
137
+ }
138
+ // Same rule as the row itself: nothing appears before visible content.
139
+ if (!text.trim()) return null;
140
+ const identity = claimAssistantIdentity(stream);
141
+ return {
142
+ id: identity.id,
143
+ from: 0,
144
+ text,
145
+ tail: {
146
+ kind: 'assistant',
147
+ id: identity.id,
148
+ streaming: true,
149
+ at: identity.at,
150
+ messageId: identity.messageId,
151
+ ...stream.routeMeta,
152
+ },
153
+ };
154
+ }
155
+
156
+ function samePartial(left, right) {
157
+ if (left === right) return true;
158
+ if (!left || !right) return false;
159
+ return left.id === right.id && left.from === right.from && left.text === right.text && !left.tail === !right.tail;
160
+ }
161
+
162
+ function clearPartialTimer(stream) {
163
+ if (stream.partialTimer === null) return;
164
+ clearTimeout(stream.partialTimer);
165
+ stream.partialTimer = null;
166
+ }
167
+
168
+ function publishPartial(stream) {
169
+ clearPartialTimer(stream);
170
+ if (!stream.isCurrentTurn()) return;
171
+ const current = stream.getState().streamingPartial ?? null;
172
+ // A transcript replacement cleared the partial this segment showed: the
173
+ // segment stays gone, like a tail the replacement dropped.
174
+ if (stream.partialShown && current === null) stream.partialDropped = true;
175
+ if (stream.partialDropped) return;
176
+ const next = partialNow(stream);
177
+ stream.partialShown = next !== null;
178
+ if (samePartial(current, next)) return;
179
+ stream.partialPublishedAt = Date.now();
180
+ stream.set({ streamingPartial: next });
181
+ }
182
+
183
+ function schedulePartial(stream) {
184
+ if (stream.partialTimer !== null) return;
185
+ const wait = Math.max(STREAM_BATCH_INTERVAL_MS, PARTIAL_INTERVAL_MS - (Date.now() - stream.partialPublishedAt));
186
+ stream.partialTimer = setTimeout(() => {
187
+ stream.partialTimer = null;
188
+ flushStreamBatch(stream);
189
+ }, wait);
190
+ stream.partialTimer?.unref?.();
191
+ }
192
+
193
+ // The segment's text settled (or was dropped): no partial line outlives it.
194
+ function clearPartial(stream) {
195
+ clearPartialTimer(stream);
196
+ stream.partialShown = false;
197
+ if (stream.getState().streamingPartial != null && stream.isCurrentTurn()) stream.set({ streamingPartial: null });
198
+ }
199
+
99
200
  function resetLineScan(stream) {
100
201
  stream.scanLen = 0;
101
202
  stream.lastNewlineIdx = -1;
@@ -107,7 +208,10 @@ function closeAssistantSegment(stream) {
107
208
  stream.currentAssistantId = null;
108
209
  stream.currentAssistantText = '';
109
210
  stream.claimPersistedAssistant = false;
211
+ stream.assistantIdentity = null;
110
212
  resetLineScan(stream);
213
+ clearPartial(stream);
214
+ stream.partialDropped = false;
111
215
  }
112
216
 
113
217
  export function commitAssistantSegment(stream, { sealToolBlock = false } = {}) {
@@ -173,12 +277,14 @@ function flushTextBatch(stream) {
173
277
  const { getState, set } = stream;
174
278
  const visibleText = streamingVisibleTextNow(stream);
175
279
  const patch = {};
280
+ let rowCreated = false;
176
281
  // Do NOT create the assistant row (and scroll the transcript) before there
177
282
  // is a completed line with VISIBLE content: the row appears together with
178
283
  // its first visible line. `.trim()` also guards a response that opens with
179
284
  // leading newlines, whose blank first line would reserve rows and scroll
180
285
  // onto an empty band until a non-blank line lands.
181
286
  if (stream.currentAssistantId || visibleText.trim()) {
287
+ rowCreated = !stream.currentAssistantId;
182
288
  const id = ensureAssistant(stream, visibleText);
183
289
  const current = getState().streamingTail;
184
290
  if (!current || current.id !== id || !Object.is(current.text, visibleText)) {
@@ -214,6 +320,9 @@ function flushTextBatch(stream) {
214
320
  set(patch);
215
321
  }
216
322
  stream.pendingThinkingLastEndedAt = 0;
323
+ // A completed line created or moved the tail: the partial line restarts
324
+ // after it in the same frame, so the two never disagree on screen.
325
+ if (rowCreated || patch.streamingTail) publishPartial(stream);
217
326
  }
218
327
 
219
328
  function thinkingSpinnerPatch(stream, thinkingElapsedMs) {
@@ -256,7 +365,7 @@ function flushThinkBatch(stream) {
256
365
  stream.publishedThinkingActive = nextThinkingActive;
257
366
  }
258
367
 
259
- export function flushStreamBatch(stream) {
368
+ function flushPendingBatches(stream) {
260
369
  if (stream.batchTimer !== null) {
261
370
  clearTimeout(stream.batchTimer);
262
371
  stream.batchTimer = null;
@@ -265,6 +374,7 @@ export function flushStreamBatch(stream) {
265
374
  stream.pendingTextFlush = false;
266
375
  stream.pendingThinkFlush = false;
267
376
  stream.pendingThinkingLastEndedAt = 0;
377
+ clearPartialTimer(stream);
268
378
  return;
269
379
  }
270
380
  if (stream.pendingTextFlush) {
@@ -277,9 +387,17 @@ export function flushStreamBatch(stream) {
277
387
  }
278
388
  }
279
389
 
390
+ // A forced flush is a boundary (tool call, notice, finalization, error, or
391
+ // the partial-line tick): the line-gated batch lands, and so does the
392
+ // desktop's unfinished line.
393
+ export function flushStreamBatch(stream) {
394
+ flushPendingBatches(stream);
395
+ publishPartial(stream);
396
+ }
397
+
280
398
  function scheduleStreamFlush(stream) {
281
399
  if (stream.batchTimer !== null) return; // already scheduled; do not re-arm
282
- stream.batchTimer = setTimeout(() => flushStreamBatch(stream), STREAM_BATCH_INTERVAL_MS);
400
+ stream.batchTimer = setTimeout(() => flushPendingBatches(stream), STREAM_BATCH_INTERVAL_MS);
283
401
  stream.batchTimer?.unref?.(); // don't prevent process exit
284
402
  }
285
403
 
@@ -306,6 +424,7 @@ export function appendTextDelta(stream, textChunk) {
306
424
  stream.pendingTextFlush = true;
307
425
  if (thinkingLastEndedAt) stream.pendingThinkingLastEndedAt = thinkingLastEndedAt;
308
426
  scheduleStreamFlush(stream);
427
+ schedulePartial(stream);
309
428
  }
310
429
 
311
430
  // Mid-turn assistant text that precedes a tool call from a provider that only
@@ -320,6 +439,9 @@ export function appendAssistantText(stream, full, { committed = false } = {}) {
320
439
  // id. Any other text (a tool preamble published before its message commits)
321
440
  // leaves an earlier recorded id unclaimed, so drop it.
322
441
  stream.claimPersistedAssistant = committed;
442
+ // A committed message's row takes its persisted identity, never one claimed
443
+ // earlier for partial text that has since been retracted.
444
+ if (committed && !stream.currentAssistantId) stream.assistantIdentity = null;
323
445
  if (!committed && stream.transcriptMeta && typeof stream.transcriptMeta === 'object') {
324
446
  stream.transcriptMeta.persistedAssistant = null;
325
447
  }
@@ -364,6 +486,8 @@ export function resetStreamedText(stream, { chars, reasoning } = {}) {
364
486
  stream.currentAssistantId = null;
365
487
  }
366
488
  }
489
+ // The retraction applies to the desktop's partial line in the same frame.
490
+ publishPartial(stream);
367
491
  return true;
368
492
  }
369
493
 
@@ -508,18 +632,37 @@ export function settleFinalText(stream, finalText) {
508
632
  text: stream.currentAssistantText || stream.assistantText,
509
633
  });
510
634
  }
635
+ clearPartial(stream);
511
636
  }
512
637
 
513
638
  // On abort, preserve only the still-open segment: tool boundaries already
514
639
  // sealed prior progress segments into their own rows, and replaying the
515
640
  // turn-global text would create one giant duplicate after the cancelled card.
516
641
  export function settleOpenSegmentOnCancel(stream) {
517
- if (!stream.currentAssistantText.trim()) return;
518
- const id = stream.currentAssistantId || ensureAssistant(stream, stream.currentAssistantText);
519
- stream.settleStreamingTail(id, { text: stream.currentAssistantText });
642
+ if (stream.currentAssistantText.trim()) {
643
+ const id = stream.currentAssistantId || ensureAssistant(stream, stream.currentAssistantText);
644
+ stream.settleStreamingTail(id, { text: stream.currentAssistantText });
645
+ }
646
+ clearPartial(stream);
647
+ }
648
+
649
+ // A segment that never completed a line has no tail, only the partial row the
650
+ // desktop shows. A turn that ends without finalizing it (provider error,
651
+ // aborted tool loop) keeps that text as its row, like cancellation does. A
652
+ // transcript replacement cleared the partial, and then the text stays gone.
653
+ function settlePartialOnlySegment(stream) {
654
+ const identity = stream.assistantIdentity;
655
+ const open = stream.getState().streamingPartial;
656
+ if (stream.currentAssistantId || stream.partialDropped || !identity || open?.id !== identity.id) return;
657
+ if (!stream.isCurrentTurn()) return;
658
+ const text = stream.currentAssistantText;
659
+ if (!text.trim()) return;
660
+ stream.settleStreamingTail(ensureAssistant(stream, text), { text });
520
661
  }
521
662
 
522
663
  export function settleStreamingTailAtClose(stream) {
664
+ settlePartialOnlySegment(stream);
665
+ clearPartial(stream);
523
666
  const id = stream.currentAssistantId;
524
667
  if (!id || stream.getState().streamingTail?.id !== id) return;
525
668
  if (stream.currentAssistantText.trim()) stream.settleStreamingTail(id, { text: stream.currentAssistantText });
@@ -15,6 +15,7 @@ import { preserveGoalStateAfterTurn } from './goal-turn-state.mjs';
15
15
  export { preserveGoalStateAfterTurn } from './goal-turn-state.mjs';
16
16
  export { transcriptToolCallDisplayMode } from './turn-tool-cards.mjs';
17
17
  import { isRejected429, safeErrorDetails } from '../../runtime/shared/error-presentation.mjs';
18
+ import { QUOTA_KINDS, providerErrorKind } from '../../runtime/agent/orchestrator/providers/provider-error-kind.mjs';
18
19
  import { registerTaskWaitSteeringCheck } from '../../runtime/agent/orchestrator/session/task-wait-control.mjs';
19
20
  import { promptDisplayText, STEERING_SUPPRESSED_DISPLAY } from './queue-helpers.mjs';
20
21
  import { yieldToRenderer } from './render-timing.mjs';
@@ -47,14 +48,11 @@ import {
47
48
  startLiveTail,
48
49
  } from './turn-tool-cards.mjs';
49
50
 
50
- function isUsageLimitError(error) {
51
+ // A usage limit is exactly a spent quota, a plan-gated feature or a billing
52
+ // refusal; overloads, rate limits and server faults recover by waiting.
53
+ function isUsageLimitError(error, kind = providerErrorKind(error)) {
51
54
  if (!error || isRejected429(error)) return false;
52
- const status = Number(error?.httpStatus || error?.status || error?.response?.status || 0);
53
- if (error?.providerQuota === true || error?.quotaExceeded === true || status === 429) return true;
54
- const text = String(error?.message || error);
55
- return /\brate[_ -]?limit\b|\bquota\b|too many requests|resource exhausted|insufficient_quota|quota_exceeded/i.test(
56
- text
57
- );
55
+ return QUOTA_KINDS.has(kind.kind);
58
56
  }
59
57
 
60
58
  function turnOutcome(cancelled, failed) {
@@ -165,7 +163,7 @@ function promptRestoreFor(displayText, options, submittedIds, content = null) {
165
163
  };
166
164
  }
167
165
 
168
- function commitPromptRestore(flags, turn) {
166
+ export function commitPromptRestore(flags, turn) {
169
167
  const restore = flags.activePromptRestore;
170
168
  if (!restore) return;
171
169
  if (!turn.promptCommitted && typeof restore.onCommitted === 'function') {
@@ -389,16 +387,27 @@ function toolHandlers(ctx) {
389
387
  };
390
388
  }
391
389
 
392
- function streamHandlers(ctx) {
390
+ export function streamHandlers(ctx) {
393
391
  const { isCurrentTurn, stream } = ctx;
394
392
  return {
395
393
  onStageChange: async (stage, detail = null) => {
396
- if (isCurrentTurn()) await applyStageChange(stream, stage, detail);
394
+ if (!isCurrentTurn()) return;
395
+ // A runtime notice (e.g. a tool the provider left out of the request)
396
+ // becomes a transcript notice the TUI and desktop render.
397
+ if (stage === 'notice') {
398
+ flushStreamBatch(stream);
399
+ ctx.pushNotice(detail?.message, detail?.tone === 'warn' ? 'warn' : 'info', { transcript: true });
400
+ return;
401
+ }
402
+ await applyStageChange(stream, stage, detail);
397
403
  },
398
404
  onTextDelta: (chunk) => {
399
405
  const textChunk = String(chunk ?? '');
400
406
  if (!textChunk || !isCurrentTurn()) return;
401
- ctx.markPromptCommitted();
407
+ // Same verdict as the runtime's interruption check (finalizeInterruptedTurn
408
+ // trims): whitespace-only text is not output, so it must not commit the
409
+ // prompt — the runtime would rewind it while the host skipped the requeue.
410
+ if (textChunk.trim()) ctx.markPromptCommitted();
402
411
  appendTextDelta(stream, textChunk);
403
412
  },
404
413
  onTextReset: (reset = {}) => (isCurrentTurn() ? resetStreamedText(stream, reset) : false),
@@ -448,13 +457,30 @@ export function failTurn(ctx, error) {
448
457
  }
449
458
  turn.failed = true;
450
459
  cards.aggregates.finalizeToolHeaders();
451
- turn.usageLimited = isUsageLimitError(error);
460
+ // Presentation only: legacy untyped failure text may still name a usage limit.
461
+ const errorKind = providerErrorKind(error, { legacyQuotaWording: true });
462
+ // The UI reads this descriptor instead of re-parsing the error text.
463
+ turn.errorKind = {
464
+ kind: errorKind.kind,
465
+ code: errorKind.code,
466
+ status: errorKind.status,
467
+ retryAfterMs: errorKind.retryAfterMs,
468
+ resetAt: errorKind.resetAt,
469
+ };
470
+ turn.usageLimited = isUsageLimitError(error, errorKind);
452
471
  const exhausted = error?.quotaExhausted;
453
- if (exhausted && typeof exhausted === 'object') {
472
+ // Quota metadata: the pool's validated exhaustion, or a spent quota whose
473
+ // provider named the real reset time.
474
+ const resetKnown = errorKind.kind === 'quota_exhausted' && Number.isFinite(errorKind.resetAt);
475
+ if ((exhausted && typeof exhausted === 'object') || resetKnown) {
476
+ const source = exhausted && typeof exhausted === 'object' ? exhausted : {};
477
+ let resetAt = null;
478
+ if (Number.isFinite(source.resetAt)) resetAt = source.resetAt;
479
+ else if (Number.isFinite(errorKind.resetAt)) resetAt = errorKind.resetAt;
454
480
  turn.quota = {
455
- provider: String(exhausted.provider || turn.routeMeta?.provider || ''),
456
- model: String(exhausted.model || turn.routeMeta?.model || ''),
457
- resetAt: Number.isFinite(exhausted.resetAt) ? exhausted.resetAt : null,
481
+ provider: String(source.provider || turn.routeMeta?.provider || ''),
482
+ model: String(source.model || turn.routeMeta?.model || ''),
483
+ resetAt,
458
484
  };
459
485
  }
460
486
  turn.failureDetail = toolErrorDisplay(error, 'turn').replace(/^Error:\s*/i, '');
@@ -485,6 +511,7 @@ export function turnDoneItem({ turn, stream, cards, getState, nextId }) {
485
511
  ...(turn.failureDiagnostic ? { errorDetails: turn.failureDiagnostic } : {}),
486
512
  ...turn.routeMeta,
487
513
  ...(turn.failed && turn.quota ? { quota: turn.quota } : {}),
514
+ ...(turn.failed && turn.errorKind ? { errorKind: turn.errorKind } : {}),
488
515
  };
489
516
  }
490
517
 
@@ -564,6 +591,7 @@ async function settleTurn(ctx) {
564
591
  result: turn.askResult,
565
592
  session: runtime.session || null,
566
593
  error: turn.failureDetail || null,
594
+ errorKind: turn.failed ? turn.errorKind || null : null,
567
595
  };
568
596
  // Publish termination before optional Goal work can delay the caller.
569
597
  bag.settleSteeredSubmissions?.(turn.epoch, detail);
@@ -626,6 +654,8 @@ export function createRunTurn(bag) {
626
654
  set({
627
655
  busy: true,
628
656
  lastTurn: null,
657
+ // A turn that unwound stale never cleared its desktop partial line.
658
+ ...(getState().streamingPartial ? { streamingPartial: null } : {}),
629
659
  spinner: {
630
660
  active: true,
631
661
  verb: pickVerb(turn.index),
@@ -65,7 +65,7 @@ export function statuslineFooterIdentityChanged(args, lastArgs) {
65
65
  if (!lastArgs) return true;
66
66
  if (args.sessionId !== lastArgs.sessionId) return true;
67
67
  if (args.provider !== lastArgs.provider || args.model !== lastArgs.model) return true;
68
- if (args.effort !== lastArgs.effort || args.fast !== lastArgs.fast) return true;
68
+ if (args.effort !== lastArgs.effort || args.fast !== lastArgs.fast || args.ultra !== lastArgs.ultra) return true;
69
69
  if (args.contextWindow !== lastArgs.contextWindow || args.rawContextWindow !== lastArgs.rawContextWindow) return true;
70
70
  if (args.displayContextWindow !== lastArgs.displayContextWindow) return true;
71
71
  if (args.compactBoundaryTokens !== lastArgs.compactBoundaryTokens) return true;
@@ -25,8 +25,7 @@ let _hiddenStatuslineAgents = null;
25
25
  function normalizeAgentWorkerForStatusline(worker = {}) {
26
26
  const tag = String(worker.tag || worker.agent || worker.name || '').trim();
27
27
  if (!tag) return null;
28
- const statuses = agentStatusValues(worker);
29
- const status = isActiveAgentEntry(statuses) ? 'running' : 'idle';
28
+ const status = isActiveAgentEntry(lifecycleStatuses(worker)) ? 'running' : 'idle';
30
29
  return {
31
30
  tag,
32
31
  status,
@@ -63,11 +62,12 @@ function normalizeAgentJobForStatusline(job = {}) {
63
62
  model: job.model || null,
64
63
  };
65
64
  }
66
- if (!isActiveAgentEntry(statuses)) return null;
65
+ const lifecycle = lifecycleStatuses(job);
66
+ if (!isActiveAgentEntry(lifecycle)) return null;
67
67
  return {
68
68
  tag,
69
69
  taskId,
70
- status: isQueuedAgentEntry(statuses) ? 'queued' : 'running',
70
+ status: isQueuedAgentEntry(lifecycle) ? 'queued' : 'running',
71
71
  startedAtMs,
72
72
  agent: job.agent || null,
73
73
  stage: job.stage || job.workerStatus || job.status || null,
@@ -214,6 +214,17 @@ function agentStatusValues(value = {}) {
214
214
  .filter((status, index, values) => Boolean(status) && values.indexOf(status) === index);
215
215
  }
216
216
 
217
+ /** The statuses that decide whether an entry is working. `status` is the
218
+ * lifecycle; `stage` is the runtime phase copied in beside it, which can read
219
+ * idle/done/error mid-work, so it decides only for an entry with no status. A
220
+ * cancel in either field still counts (it keeps both in play). */
221
+ function lifecycleStatuses(value = {}) {
222
+ const statuses = agentStatusValues(value);
223
+ if (statuses.some((status) => /cancel|killed/i.test(status))) return statuses;
224
+ const status = String(value.status || '').trim();
225
+ return status ? [status] : statuses;
226
+ }
227
+
217
228
  function isTerminalAgentEntry(statuses = []) {
218
229
  return statuses.some((status) => TERMINAL_AGENT_STATUS.test(String(status || '')));
219
230
  }
@@ -197,6 +197,7 @@ export async function renderStatusline({
197
197
  model = '',
198
198
  effort = '',
199
199
  fast = false,
200
+ ultra = false,
200
201
  cwd = '',
201
202
  stats,
202
203
  sessionId,
@@ -223,6 +224,7 @@ export async function renderStatusline({
223
224
  model,
224
225
  effort,
225
226
  fast,
227
+ ultra,
226
228
  cwd,
227
229
  stats,
228
230
  sessionId,
@@ -233,7 +235,7 @@ export async function renderStatusline({
233
235
  ...displayArgs,
234
236
  });
235
237
  } catch {
236
- return fallbackLine({ provider, model, effort, fast, cwd, stats, ...displayArgs });
238
+ return fallbackLine({ provider, model, effort, fast, ultra, cwd, stats, ...displayArgs });
237
239
  }
238
240
  }
239
241
 
@@ -244,6 +246,7 @@ function renderNativeStatusline({
244
246
  model = '',
245
247
  effort = '',
246
248
  fast = false,
249
+ ultra = false,
247
250
  stats,
248
251
  sessionId,
249
252
  contextWindow = 0,
@@ -275,7 +278,7 @@ function renderNativeStatusline({
275
278
 
276
279
  const sep = ` ${D}│${R} `;
277
280
  const l1Parts = [
278
- formatModelSegment({ provider, model, effort, fast, cols }),
281
+ formatModelSegment({ provider, model, effort, fast, ultra, cols }),
279
282
  formatContextSegment(ctxPct, cols, s.currentContextSource),
280
283
  ...quotaSegmentsFor({ provider, model, effort, fast, sessionId, clientHostPid, gatewayStatus, cols }),
281
284
  ].filter(Boolean);
@@ -664,6 +667,7 @@ function fallbackLine({
664
667
  model = '',
665
668
  effort = '',
666
669
  fast = false,
670
+ ultra = false,
667
671
  cwd: _cwd = '',
668
672
  stats,
669
673
  contextWindow = 0,
@@ -687,7 +691,7 @@ function fallbackLine({
687
691
  });
688
692
  const sep = ` ${D}│${R} `;
689
693
  const parts = [
690
- formatModelSegment({ provider, model, effort, fast, cols }),
694
+ formatModelSegment({ provider, model, effort, fast, ultra, cols }),
691
695
  formatContextSegment(ctxPct, cols, s.currentContextSource),
692
696
  ].filter(Boolean);
693
697
  if (!parts.length) return statusSubtle('> mixdog');
@@ -213,12 +213,43 @@ function healIncompleteLink(text, isPendingLocalPath) {
213
213
  return `${prefix}[${closeEmphasis(caption)}${']'.repeat(Math.max(0, depth - 1))}]()`;
214
214
  }
215
215
 
216
+ const PIPE_ROW = /^ {0,3}\|/;
217
+ const DELIMITER_ROW = /^ {0,3}\|?\s*:?-+:?\s*(?:\|\s*:?-+:?\s*)*\|?\s*$/;
218
+ const PARTIAL_DELIMITER_ROW = /^ {0,3}\|?[\s:|-]*$/;
219
+
220
+ function tableCells(line) {
221
+ const cells = line.trim().split(/(?<!\\)\|/);
222
+ if (cells[0].trim() === '') cells.shift();
223
+ if (cells.length && cells[cells.length - 1].trim() === '') cells.pop();
224
+ return cells.length;
225
+ }
226
+
227
+ // A table streams in as its header row, then its delimiter row a token at a
228
+ // time. Until the delimiter is complete (and has the header's cell count) the
229
+ // rows are a paragraph of raw pipes, which then re-laid out as a table of a
230
+ // different height — the reply grew and shrank under a followed transcript.
231
+ // Hold both rows back until the table can form in one step.
232
+ function trimPendingTableHead(text) {
233
+ let offset = 0;
234
+ for (const blank of text.matchAll(/\n[ \t]*\r?\n/g)) offset = blank.index + blank[0].length;
235
+ const block = text.slice(offset);
236
+ const lines = block.split('\n').map((line) => line.replace(/\r$/, ''));
237
+ if (lines.length === 0 || lines.length > 2 || !PIPE_ROW.test(lines[0])) return text;
238
+ if (lines.length === 2) {
239
+ const delimiter = lines[1];
240
+ if (!PARTIAL_DELIMITER_ROW.test(delimiter)) return text;
241
+ if (DELIMITER_ROW.test(delimiter) && tableCells(delimiter) === tableCells(lines[0])) return text;
242
+ }
243
+ return text.slice(0, offset);
244
+ }
245
+
216
246
  export function healStreamingMarkdownTail(text, isPendingLocalPath) {
217
- const value = String(text ?? '');
247
+ const source = String(text ?? '');
248
+ const fence = source.includes('`') || source.includes('~') ? openFence(source) : null;
249
+ if (fence) return trimPartialClosingFence(source, fence);
250
+ const value = source.includes('|') ? trimPendingTableHead(source) : source;
218
251
  const hasSyntax = HEALABLE_MARKDOWN_SYNTAX.test(value);
219
252
  if (!value || (!hasSyntax && !isPendingLocalPath)) return value;
220
- const fence = openFence(value);
221
- if (fence) return trimPartialClosingFence(value, fence);
222
253
  const healed = hasSyntax
223
254
  ? closeEmphasis(healIncompleteLink(closeInlineCode(value, isPendingLocalPath), isPendingLocalPath))
224
255
  : value;