mixdog 1.2.1 → 1.2.2

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 (233) hide show
  1. package/package.json +1 -1
  2. package/scripts/gate-local.mjs +19 -3
  3. package/scripts/model-catalog-audit.mjs +1 -15
  4. package/src/agents/reviewer/AGENT.md +3 -3
  5. package/src/agents/reviewer/agent.json +1 -1
  6. package/src/defaults/shipped-history.json +1 -0
  7. package/src/repl/slash-commands.mjs +16 -1
  8. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -4
  9. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +10 -0
  10. package/src/runtime/agent/orchestrator/mcp/client-config.mjs +36 -7
  11. package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +49 -10
  12. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +40 -3
  13. package/src/runtime/agent/orchestrator/mcp/client.mjs +230 -96
  14. package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +103 -0
  15. package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +150 -0
  16. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +111 -0
  17. package/src/runtime/agent/orchestrator/mcp/oauth-probe.mjs +87 -0
  18. package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +95 -0
  19. package/src/runtime/agent/orchestrator/mcp/oauth-request.mjs +122 -0
  20. package/src/runtime/agent/orchestrator/mcp/oauth-shared.mjs +9 -0
  21. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +229 -0
  22. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +188 -29
  23. package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +2 -1
  24. package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +94 -31
  25. package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +96 -0
  26. package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.mjs +39 -18
  27. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +55 -0
  28. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +25 -21
  29. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +3 -1
  30. package/src/runtime/agent/orchestrator/providers/anthropic-midstream-recovery.mjs +18 -4
  31. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +13 -1
  32. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  33. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-midstream.mjs +10 -1
  34. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-recovery.mjs +23 -9
  35. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +46 -20
  36. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request.mjs +14 -9
  37. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +73 -35
  38. package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +69 -0
  39. package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.mjs +4 -0
  40. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +8 -1
  41. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +34 -7
  42. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +30 -9
  43. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +7 -0
  44. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +30 -14
  45. package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +2 -1
  46. package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +93 -0
  47. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +82 -9
  48. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +3 -0
  49. package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +38 -5
  50. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  51. package/src/runtime/agent/orchestrator/providers/cursor-wire-guards.mjs +97 -2
  52. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +102 -23
  53. package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +103 -33
  54. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +29 -6
  55. package/src/runtime/agent/orchestrator/providers/cursor.mjs +18 -1
  56. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +77 -17
  57. package/src/runtime/agent/orchestrator/providers/gemini-cache-policy.mjs +1 -1
  58. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +29 -8
  59. package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +191 -0
  60. package/src/runtime/agent/orchestrator/providers/gemini-family.mjs +9 -0
  61. package/src/runtime/agent/orchestrator/providers/gemini-history-signatures.mjs +9 -4
  62. package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +3 -1
  63. package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +180 -0
  64. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +21 -82
  65. package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +4 -1
  66. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +71 -12
  67. package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +21 -20
  68. package/src/runtime/agent/orchestrator/providers/gemini.mjs +56 -23
  69. package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
  70. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +68 -19
  71. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +3 -0
  72. package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +2 -1
  73. package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +4 -9
  74. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +4 -2
  75. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-result.mjs +22 -0
  76. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +1 -0
  77. package/src/runtime/agent/orchestrator/providers/openai-compat-error-contract.mjs +50 -0
  78. package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +23 -0
  79. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +16 -8
  80. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-state.mjs +3 -2
  81. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +94 -13
  82. package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +193 -0
  83. package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +66 -2
  84. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +2 -1
  85. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +6 -0
  86. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +4 -2
  87. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +23 -14
  88. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +17 -3
  89. package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +42 -25
  90. package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +274 -0
  91. package/src/runtime/agent/orchestrator/providers/openai-http-sse-request.mjs +72 -32
  92. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +18 -11
  93. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-outcome.mjs +1 -2
  94. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-stamps.mjs +2 -2
  95. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-state.mjs +2 -3
  96. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +2 -2
  97. package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +1 -3
  98. package/src/runtime/agent/orchestrator/providers/openai-oauth-dispatch.mjs +34 -3
  99. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +5 -1
  100. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +22 -2
  101. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +5 -0
  102. package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +5 -9
  103. package/src/runtime/agent/orchestrator/providers/openai-retry-policy.mjs +9 -0
  104. package/src/runtime/agent/orchestrator/providers/openai-ws-handshake.mjs +16 -8
  105. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +1 -2
  106. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/policy.mjs +25 -14
  107. package/src/runtime/agent/orchestrator/providers/openai-ws-terminal.mjs +18 -11
  108. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +31 -20
  109. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +581 -0
  110. package/src/runtime/agent/orchestrator/providers/retry-backoff.mjs +46 -7
  111. package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +118 -219
  112. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +2 -0
  113. package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +148 -45
  114. package/src/runtime/agent/orchestrator/providers/retry-midstream.mjs +81 -16
  115. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +3 -0
  116. package/src/runtime/agent/orchestrator/runtime-core/goal-reminder.mjs +0 -18
  117. package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +57 -28
  118. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +19 -16
  119. package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +1 -97
  120. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +3 -1
  121. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +4 -0
  122. package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +102 -18
  123. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +3 -1
  124. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +30 -2
  125. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +11 -2
  126. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +41 -21
  127. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +5 -10
  128. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +1 -1
  129. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +18 -7
  130. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +155 -29
  131. package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +6 -3
  132. package/src/runtime/agent/orchestrator/session/store/write-guards.mjs +16 -0
  133. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +22 -10
  134. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +2 -0
  135. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +5 -0
  136. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +3 -0
  137. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +20 -1
  138. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +2 -2
  139. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +12 -15
  140. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +5 -5
  141. package/src/runtime/memory/lib/pg/process.mjs +34 -24
  142. package/src/runtime/memory/lib/pg/supervisor.mjs +16 -13
  143. package/src/runtime/office/README.md +1 -1
  144. package/src/runtime/office/shared/values.mjs +0 -5
  145. package/src/runtime/shared/background-tasks.mjs +92 -14
  146. package/src/runtime/shared/err-text.mjs +25 -0
  147. package/src/runtime/shared/error-presentation.mjs +159 -19
  148. package/src/runtime/shared/llm/anthropic-betas.mjs +4 -3
  149. package/src/runtime/shared/llm/anthropic-effort.mjs +123 -134
  150. package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +50 -14
  151. package/src/runtime/shared/llm/model-catalog.mjs +53 -40
  152. package/src/runtime/shared/llm/openai-daybreak.mjs +4 -16
  153. package/src/runtime/shared/llm/provider-catalog-registry.mjs +11 -0
  154. package/src/runtime/shared/llm/provider-model-identities.mjs +13 -4
  155. package/src/runtime/shared/llm/usage-rollup.mjs +0 -1
  156. package/src/runtime/shared/model-display.mjs +13 -0
  157. package/src/runtime/shared/quota-resume-store.mjs +11 -0
  158. package/src/runtime/shared/session-baseline-store.mjs +960 -0
  159. package/src/runtime/shared/statusline/statusline-format.mjs +4 -4
  160. package/src/runtime/shared/task-notification-envelope.mjs +2 -2
  161. package/src/runtime/shared/turn-changes.mjs +447 -219
  162. package/src/runtime/shared/turn-snapshot-store.mjs +182 -1
  163. package/src/runtime/shared/turn-snapshot.mjs +170 -48
  164. package/src/runtime/shared/turn-worktree-snapshot.mjs +135 -46
  165. package/src/runtime/shared/worktrees/contract.mjs +0 -24
  166. package/src/runtime/shared/worktrees/test-support.mjs +19 -4
  167. package/src/session-runtime/boot/apis.mjs +6 -0
  168. package/src/session-runtime/boot/infrastructure.mjs +6 -0
  169. package/src/session-runtime/lifecycle/session-catalog.mjs +2 -0
  170. package/src/session-runtime/lifecycle/teardown.mjs +1 -1
  171. package/src/session-runtime/mcp-glue/connect.mjs +29 -9
  172. package/src/session-runtime/mcp-glue/server-catalog.mjs +20 -3
  173. package/src/session-runtime/mcp-glue.mjs +3 -0
  174. package/src/session-runtime/mcp-oauth-login.mjs +174 -0
  175. package/src/session-runtime/model-route/model-switch-target.mjs +34 -0
  176. package/src/session-runtime/model-route/set-route.mjs +83 -15
  177. package/src/session-runtime/model-route/web-search-route.mjs +1 -1
  178. package/src/session-runtime/native-web-search/routes.mjs +22 -3
  179. package/src/session-runtime/provider-models/model-meta.mjs +12 -3
  180. package/src/session-runtime/quick-model-rows/web-search-rows.mjs +13 -8
  181. package/src/session-runtime/quick-web-search-models.mjs +13 -38
  182. package/src/session-runtime/resource-extension-sync.mjs +4 -0
  183. package/src/session-runtime/resource-mcp-api.mjs +98 -2
  184. package/src/session-runtime/runtime-review-api.mjs +13 -0
  185. package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +13 -1
  186. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +7 -2
  187. package/src/session-runtime/services/agent-tool/send-flow.mjs +22 -4
  188. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +2 -0
  189. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +22 -5
  190. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +8 -1
  191. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -2
  192. package/src/session-runtime/services/agent-tool/worker-rows.mjs +17 -0
  193. package/src/session-runtime/settings-compaction-api.mjs +1 -0
  194. package/src/standalone/daemon-drain-policy.mjs +8 -0
  195. package/src/standalone/daemon-telemetry.mjs +55 -0
  196. package/src/standalone/daemon.mjs +3 -7
  197. package/src/standalone/session-protocol.mjs +7 -0
  198. package/src/standalone/session-service/agent-tree/agent-cancel.mjs +33 -2
  199. package/src/standalone/session-service/agent-tree/agent-turns.mjs +26 -10
  200. package/src/standalone/session-service/agent-tree.mjs +12 -0
  201. package/src/standalone/session-service/quota-resume.mjs +8 -0
  202. package/src/standalone/session-service/retention/busy.mjs +15 -1
  203. package/src/standalone/session-service-api.mjs +5 -0
  204. package/src/standalone/session-service.mjs +1 -0
  205. package/src/tui/app/app-view.jsx +1 -0
  206. package/src/tui/app/create-app-pickers.mjs +4 -0
  207. package/src/tui/app/extension-pickers/mcp-login.mjs +107 -0
  208. package/src/tui/app/extension-pickers/mcp-servers-picker.mjs +2 -1
  209. package/src/tui/app/extension-pickers.mjs +2 -1
  210. package/src/tui/app/model-options.mjs +11 -16
  211. package/src/tui/app/slash-commands.mjs +7 -2
  212. package/src/tui/app/slash-dispatch/panel-commands.mjs +16 -1
  213. package/src/tui/components/StatusLine.jsx +2 -0
  214. package/src/tui/components/status-line/local-line.mjs +5 -2
  215. package/src/tui/components/status-line/use-statusline-text.mjs +7 -1
  216. package/src/tui/dist/index.mjs +816 -43
  217. package/src/tui/session/quota-resume.mjs +46 -14
  218. package/src/tui/session/session-api/commands.mjs +33 -24
  219. package/src/tui/session/session-api/extensions.mjs +22 -0
  220. package/src/tui/session/session-api/integrations.mjs +7 -0
  221. package/src/tui/session/session-api/lifecycle.mjs +5 -1
  222. package/src/tui/session/session-api/routes.mjs +71 -7
  223. package/src/tui/session/session-api/settings.mjs +5 -14
  224. package/src/tui/session/session-flow/drain.mjs +7 -1
  225. package/src/tui/session/session-flow/failed-turn-rewind.mjs +10 -4
  226. package/src/tui/session/turn.mjs +38 -15
  227. package/src/tui/statusline-ansi-bridge.mjs +1 -1
  228. package/src/ui/statusline-agents.mjs +15 -4
  229. package/src/ui/statusline.mjs +7 -3
  230. package/src/ui/streaming-markdown-heal.mjs +34 -3
  231. package/src/vendor/statusline/src/gateway/claude-current.mjs +14 -4
  232. package/src/runtime/memory/lib/memory-session-merge.mjs +0 -32
  233. package/src/runtime/shared/llm/usage-session-history.mjs +0 -266
@@ -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;
@@ -1,6 +1,8 @@
1
1
  import fs from 'fs';
2
2
  import os from 'os';
3
3
  import path from 'path';
4
+ import { getModelMetadataSync } from '../../../../runtime/shared/llm/model-catalog.mjs';
5
+ import { providerCachedModelsSync } from '../../../../runtime/shared/llm/provider-catalog-cache.mjs';
4
6
 
5
7
  export const CLAUDE_CURRENT_MODE = 'claude-current';
6
8
  export const CLAUDE_CURRENT_CHOICE_ID = 'claude-current';
@@ -95,11 +97,21 @@ function snapshotEquivalent(a, b) {
95
97
  return keys.every(k => (a[k] ?? null) === (b[k] ?? null));
96
98
  }
97
99
 
100
+ // The newest roster model of a family, by the roster's own release stamps.
101
+ function newestRosterModel(family) {
102
+ const stamp = (row) => Date.parse(row.releaseDate) || (Number(row.created) > 0 ? Number(row.created) : 0);
103
+ const rows = providerCachedModelsSync('anthropic-oauth').filter((row) =>
104
+ String(row.id).toLowerCase().startsWith(`claude-${family}-`)
105
+ );
106
+ rows.sort((a, b) => stamp(b) - stamp(a));
107
+ return rows[0]?.id || null;
108
+ }
109
+
98
110
  function familyDefaultModel(family) {
99
111
  const f = String(family || '').toLowerCase();
100
112
  if (!f) return null;
101
113
  const envName = `ANTHROPIC_DEFAULT_${f.toUpperCase()}_MODEL`;
102
- return cleanString(process.env[envName]) || DEFAULT_FAMILY_MODEL[f] || null;
114
+ return cleanString(process.env[envName]) || newestRosterModel(f) || DEFAULT_FAMILY_MODEL[f] || null;
103
115
  }
104
116
 
105
117
  export function stripClaudeContextSuffix(model) {
@@ -109,9 +121,7 @@ export function stripClaudeContextSuffix(model) {
109
121
  function contextWindowFromRawModel(raw, resolved) {
110
122
  const s = cleanString(raw).toLowerCase();
111
123
  if (/\[1m\]/.test(s)) return 1000000;
112
- if (/^claude-(opus|sonnet)-4-(6|7|8)(?:$|-)/i.test(resolved || '')) return 1000000;
113
- if (/^claude-haiku-4-5/i.test(resolved || '')) return 200000;
114
- return null;
124
+ return (resolved && getModelMetadataSync(resolved, 'anthropic-oauth')?.contextWindow) || null;
115
125
  }
116
126
 
117
127
  export function resolveClaudeModelAlias(model) {
@@ -1,32 +0,0 @@
1
- // Pure, DB-free merge of session-fallback recall rows into the already-ranked
2
- // global hybrid rows. Extracted into its own module so the starve-prevention +
3
- // dedupe contract can be unit-tested without booting the memory worker (which
4
- // has heavy top-level side effects: http server, pg, onnx embedding).
5
- //
6
- // Contract:
7
- // - APPEND session rows, never prepend. Blind prepend lets session rows
8
- // (limit+) starve the global first page and distorts sort/offset/limit.
9
- // - Re-apply the SAME sort policy as the global path so global hybrid hits
10
- // (carrying a real retrievalScore) keep their rank; session rows
11
- // (retrievalScore 0 under importance, ts-ordered under date) only fill
12
- // genuinely open slots.
13
- // - Dedupe drops any session row whose id already appears as a global row OR
14
- // as a global root's inlined chunk member (prevents member/leaf double
15
- // output).
16
- import { compareRecallByScore, compareRecallNewestFirst } from './recall-order.mjs';
17
-
18
- export function mergeSessionRowsIntoGlobal(globalRows, sessionRows, { sort = 'importance' } = {}) {
19
- const filtered = Array.isArray(globalRows) ? [...globalRows] : [];
20
- if (!Array.isArray(sessionRows) || sessionRows.length === 0) return filtered;
21
- const seen = new Set(filtered.map((r) => Number(r.id)));
22
- for (const r of filtered) {
23
- if (Array.isArray(r.members)) {
24
- for (const m of r.members) seen.add(Number(m.id));
25
- }
26
- }
27
- const merged = sessionRows.filter((r) => !seen.has(Number(r.id)));
28
- if (merged.length === 0) return filtered;
29
- const out = [...filtered, ...merged];
30
- out.sort(sort === 'date' ? compareRecallNewestFirst : compareRecallByScore);
31
- return out;
32
- }
@@ -1,266 +0,0 @@
1
- /**
2
- * Long-range usage recovered from the durable session stores.
3
- *
4
- * The gateway event list is capped by row count and the daily rollup only
5
- * carries days recorded after rolling up began, so both stores answer "what
6
- * happened recently" and neither can answer "what did I ever spend on Grok".
7
- * The session documents can: every session keeps its own lifetime token
8
- * counters, its route, and its transcript timestamps, and they outlive both
9
- * telemetry stores.
10
- *
11
- * So this module rebuilds per-day totals from those documents. The rebuild is
12
- * a derivation, not a second recording: a day that the rollup or the raw
13
- * events already cover is never taken from here, which is what keeps a turn
14
- * from being counted twice.
15
- *
16
- * Two things are approximations, and both are marked `restored` so a surface
17
- * can say so:
18
- * - a session carries ONE set of token counters, so a session that changed
19
- * model mid-way is attributed to the route it ended on;
20
- * - those counters are not stamped per turn, so a session that spans days is
21
- * split across them by how many messages each day holds.
22
- */
23
- import { billableInputTokensForProvider, computeCostUsd } from './cost.mjs';
24
- import { cleanId as clean, isConversationUsageSource, num, round6, usageRollupDayKey } from './usage-rollup.mjs';
25
-
26
- const DAY_MS = 24 * 60 * 60 * 1000;
27
-
28
- /** Timestamp a transcript message was written at, when it carries one. */
29
- function messageTimestamp(message) {
30
- const direct = num(message?.createdAt);
31
- if (direct > 0) return direct;
32
- return num(message?.meta?.transcript?.at);
33
- }
34
-
35
- /**
36
- * Spread `total` across `keys` in proportion to `weights`, keeping the sum
37
- * exact: the rounding remainder lands on the heaviest day rather than
38
- * disappearing from the totals.
39
- */
40
- function distribute(total, keys, weights, weightSum) {
41
- const out = new Map();
42
- if (!(total > 0) || !keys.length) return out;
43
- if (keys.length === 1 || !(weightSum > 0)) {
44
- out.set(keys[0], total);
45
- return out;
46
- }
47
- let assigned = 0;
48
- let heaviest = keys[0];
49
- let heaviestWeight = -1;
50
- for (const key of keys) {
51
- const weight = weights.get(key) || 0;
52
- const share = Math.floor((total * weight) / weightSum);
53
- out.set(key, share);
54
- assigned += share;
55
- if (weight > heaviestWeight) {
56
- heaviest = key;
57
- heaviestWeight = weight;
58
- }
59
- }
60
- if (assigned < total) out.set(heaviest, (out.get(heaviest) || 0) + (total - assigned));
61
- return out;
62
- }
63
-
64
- /**
65
- * One session document → the per-day slices it contributes.
66
- * Returns null for a session that never spent a token, which is most of the
67
- * short-lived ids a daemon mints.
68
- */
69
- export function summarizeSessionUsage(doc) {
70
- const provider = clean(doc?.provider);
71
- const model = clean(doc?.model);
72
- if (!provider || !model) return null;
73
-
74
- const cacheRead = num(doc?.totalCachedReadTokens);
75
- const cacheWrite = num(doc?.totalCacheWriteTokens);
76
- // `totalUncachedInputTokens` is already the billable part. Where a session
77
- // predates that counter the raw input is unpacked the same way the rollup
78
- // does it, so a provider that reports the cache inside `input` is not
79
- // counted twice.
80
- const input =
81
- num(doc?.totalUncachedInputTokens) ||
82
- billableInputTokensForProvider(provider, num(doc?.totalInputTokens), cacheRead, cacheWrite);
83
- const output = num(doc?.totalOutputTokens);
84
- if (input + output + cacheRead + cacheWrite <= 0) return null;
85
-
86
- const messages = Array.isArray(doc?.messages) ? doc.messages : [];
87
- const weights = new Map();
88
- let weightSum = 0;
89
- let firstTs = 0;
90
- let lastTs = 0;
91
- let userTurns = 0;
92
- for (const message of messages) {
93
- if (message?.role === 'user') userTurns += 1;
94
- const ts = messageTimestamp(message);
95
- if (ts <= 0) continue;
96
- const key = usageRollupDayKey(ts);
97
- weights.set(key, (weights.get(key) || 0) + 1);
98
- weightSum += 1;
99
- firstTs = firstTs > 0 ? Math.min(firstTs, ts) : ts;
100
- lastTs = Math.max(lastTs, ts);
101
- }
102
- // A transcript compacted down to nothing still spent what it spent; the day
103
- // it was last used is the only timestamp left to attribute it to.
104
- if (!weights.size) {
105
- const fallback = num(doc?.updatedAt) || num(doc?.lastUsedAt) || num(doc?.createdAt);
106
- if (!fallback) return null;
107
- const key = usageRollupDayKey(fallback);
108
- weights.set(key, 1);
109
- weightSum = 1;
110
- firstTs = fallback;
111
- lastTs = fallback;
112
- }
113
-
114
- const keys = [...weights.keys()].sort();
115
- const turns = Math.max(1, userTurns);
116
- const byInput = distribute(input, keys, weights, weightSum);
117
- const byOutput = distribute(output, keys, weights, weightSum);
118
- const byCacheRead = distribute(cacheRead, keys, weights, weightSum);
119
- const byCacheWrite = distribute(cacheWrite, keys, weights, weightSum);
120
- const byTurns = distribute(turns, keys, weights, weightSum);
121
-
122
- const days = {};
123
- for (const key of keys) {
124
- const slice = {
125
- turns: byTurns.get(key) || 0,
126
- input: byInput.get(key) || 0,
127
- output: byOutput.get(key) || 0,
128
- cacheRead: byCacheRead.get(key) || 0,
129
- cacheWrite: byCacheWrite.get(key) || 0,
130
- costUsd: 0,
131
- };
132
- if (slice.turns + slice.input + slice.output + slice.cacheRead + slice.cacheWrite <= 0) continue;
133
- let costUsd = 0;
134
- try {
135
- costUsd = num(
136
- computeCostUsd({
137
- provider,
138
- model,
139
- uncachedInputTokens: slice.input,
140
- outputTokens: slice.output,
141
- cacheReadTokens: slice.cacheRead,
142
- cacheWriteTokens: slice.cacheWrite,
143
- })
144
- );
145
- } catch {
146
- // An unpriced model still reports its tokens.
147
- }
148
- slice.costUsd = round6(costUsd);
149
- days[key] = slice;
150
- }
151
- if (!Object.keys(days).length) return null;
152
-
153
- return {
154
- provider,
155
- model,
156
- sessionId: clean(doc?.id),
157
- conversation: isConversationUsageSource(doc?.sourceType),
158
- firstTs,
159
- lastTs,
160
- days,
161
- };
162
- }
163
-
164
- function emptyRouteTotals() {
165
- return { turns: 0, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, costUsd: 0 };
166
- }
167
-
168
- function emptyTurnTotals() {
169
- return {
170
- ...emptyRouteTotals(),
171
- costBilled: 0,
172
- // Nothing here was priced by a provider invoice: the catalog priced it.
173
- costEstimated: 0,
174
- costKnownTurns: 0,
175
- durationMs: 0,
176
- durationTurns: 0,
177
- };
178
- }
179
-
180
- /** Rollup-shaped so the stats model folds a restored day with the same code. */
181
- function emptyHistoryDay() {
182
- return {
183
- ...emptyTurnTotals(),
184
- firstTs: 0,
185
- lastTs: 0,
186
- conversation: emptyTurnTotals(),
187
- conversationPartial: false,
188
- sessionsPurged: true,
189
- // Tells a reader these numbers were derived from transcripts rather than
190
- // recorded turn by turn.
191
- restored: true,
192
- models: {},
193
- sessions: {},
194
- sessionTokens: [],
195
- sessionsDropped: 0,
196
- };
197
- }
198
-
199
- function addRouteTotals(target, slice) {
200
- target.turns += slice.turns;
201
- target.input += slice.input;
202
- target.output += slice.output;
203
- target.cacheRead += slice.cacheRead;
204
- target.cacheWrite += slice.cacheWrite;
205
- target.costUsd = round6(target.costUsd + slice.costUsd);
206
- }
207
-
208
- function addTurnTotals(target, slice) {
209
- addRouteTotals(target, slice);
210
- if (slice.costUsd > 0) {
211
- target.costEstimated = round6(target.costEstimated + slice.costUsd);
212
- target.costKnownTurns += slice.turns;
213
- }
214
- }
215
-
216
- /** Fold every cached session summary into one day-keyed document. */
217
- export function buildUsageHistoryDays(entries) {
218
- const days = {};
219
- for (const entry of entries) {
220
- if (!entry?.provider || !entry?.model || !entry?.days) continue;
221
- const routeKey = `${entry.provider}/${entry.model}`;
222
- for (const [key, slice] of Object.entries(entry.days)) {
223
- if (!/^\d{4}-\d{2}-\d{2}$/.test(key)) continue;
224
- const day = days[key] || emptyHistoryDay();
225
- const normalized = {
226
- turns: num(slice?.turns),
227
- input: num(slice?.input),
228
- output: num(slice?.output),
229
- cacheRead: num(slice?.cacheRead),
230
- cacheWrite: num(slice?.cacheWrite),
231
- costUsd: num(slice?.costUsd),
232
- };
233
- addTurnTotals(day, normalized);
234
- if (entry.conversation) addTurnTotals(day.conversation, normalized);
235
-
236
- const bucket = day.models[routeKey] || {
237
- provider: entry.provider,
238
- model: entry.model,
239
- kind: '',
240
- ...emptyRouteTotals(),
241
- conversation: emptyRouteTotals(),
242
- };
243
- addRouteTotals(bucket, normalized);
244
- if (entry.conversation) addRouteTotals(bucket.conversation, normalized);
245
- day.models[routeKey] = bucket;
246
-
247
- // Only conversation ids count as sessions, matching the rollup: a
248
- // background runner mints a fresh id per cycle.
249
- if (entry.conversation && entry.sessionId) {
250
- const tokens = normalized.input + normalized.output + normalized.cacheRead + normalized.cacheWrite;
251
- day.sessions[entry.sessionId] = (day.sessions[entry.sessionId] || 0) + tokens;
252
- }
253
- const dayStart = Date.parse(`${key}T00:00:00`);
254
- if (Number.isFinite(dayStart)) {
255
- const first = num(entry.firstTs);
256
- const last = num(entry.lastTs);
257
- const from = first > dayStart && first < dayStart + DAY_MS ? first : dayStart;
258
- const to = last > dayStart && last < dayStart + DAY_MS ? last : dayStart + DAY_MS - 1;
259
- day.firstTs = day.firstTs > 0 ? Math.min(day.firstTs, from) : from;
260
- day.lastTs = Math.max(day.lastTs, to);
261
- }
262
- days[key] = day;
263
- }
264
- }
265
- return days;
266
- }