mixdog 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (285) hide show
  1. package/README.md +47 -23
  2. package/package.json +33 -27
  3. package/scripts/_test-folder-dialog.mjs +30 -0
  4. package/scripts/agent-parallel-smoke.mjs +388 -0
  5. package/scripts/agent-tag-reuse-smoke.mjs +183 -0
  6. package/scripts/background-task-meta-smoke.mjs +38 -0
  7. package/scripts/boot-smoke.mjs +52 -9
  8. package/scripts/build-runtime-linux.sh +348 -0
  9. package/scripts/build-runtime-macos.sh +217 -0
  10. package/scripts/build-runtime-windows.ps1 +242 -0
  11. package/scripts/compact-active-turn-test.mjs +68 -0
  12. package/scripts/compact-smoke.mjs +859 -129
  13. package/scripts/compact-trigger-migration-smoke.mjs +187 -0
  14. package/scripts/fix-brief-fn.mjs +35 -0
  15. package/scripts/fix-format-tool-surface.mjs +24 -0
  16. package/scripts/fix-tool-exec-visible.mjs +42 -0
  17. package/scripts/generate-runtime-manifest.mjs +166 -0
  18. package/scripts/hook-bus-test.mjs +330 -0
  19. package/scripts/lead-workflow-smoke.mjs +33 -39
  20. package/scripts/live-worker-smoke.mjs +43 -37
  21. package/scripts/llm-trace-summary.mjs +315 -0
  22. package/scripts/memory-meta-concurrency-test.mjs +20 -0
  23. package/scripts/output-style-smoke.mjs +56 -15
  24. package/scripts/parent-abort-link-test.mjs +44 -0
  25. package/scripts/patch-agent-brief.mjs +48 -0
  26. package/scripts/patch-app.mjs +21 -0
  27. package/scripts/patch-app2.mjs +18 -0
  28. package/scripts/patch-dist-brief.mjs +96 -0
  29. package/scripts/patch-tool-exec.mjs +70 -0
  30. package/scripts/pretool-ask-runtime-test.mjs +54 -0
  31. package/scripts/provider-toolcall-test.mjs +376 -0
  32. package/scripts/reactive-compact-persist-smoke.mjs +124 -0
  33. package/scripts/sanitize-tool-pairs-test.mjs +260 -0
  34. package/scripts/session-context-bench.mjs +344 -0
  35. package/scripts/session-ingest-smoke.mjs +177 -0
  36. package/scripts/set-effort-config-test.mjs +41 -0
  37. package/scripts/smoke-runtime-negative.ps1 +106 -0
  38. package/scripts/smoke-runtime-negative.sh +97 -0
  39. package/scripts/smoke.mjs +25 -0
  40. package/scripts/tool-result-hook-test.mjs +48 -0
  41. package/scripts/tool-smoke.mjs +1223 -95
  42. package/scripts/toolcall-args-test.mjs +150 -0
  43. package/scripts/tui-background-failure-smoke.mjs +73 -0
  44. package/scripts/usage-metrics-epoch-smoke.mjs +114 -0
  45. package/src/agents/debugger/AGENT.md +8 -0
  46. package/src/agents/explore/AGENT.md +4 -0
  47. package/src/agents/heavy-worker/AGENT.md +9 -3
  48. package/src/agents/maintainer/AGENT.md +4 -0
  49. package/src/agents/reviewer/AGENT.md +8 -0
  50. package/src/agents/scheduler-task/AGENT.md +12 -0
  51. package/src/agents/scheduler-task/agent.json +6 -0
  52. package/src/agents/webhook-handler/AGENT.md +12 -0
  53. package/src/agents/webhook-handler/agent.json +6 -0
  54. package/src/agents/worker/AGENT.md +9 -3
  55. package/src/app.mjs +77 -3
  56. package/src/defaults/hidden-roles.json +17 -12
  57. package/src/headless-role.mjs +117 -0
  58. package/src/help.mjs +30 -0
  59. package/src/hooks/lib/permission-evaluator.cjs +11 -475
  60. package/src/lib/keychain-cjs.cjs +9 -1
  61. package/src/lib/mixdog-debug.cjs +0 -7
  62. package/src/lib/rules-builder.cjs +240 -96
  63. package/src/lib/text-utils.cjs +1 -1
  64. package/src/mixdog-session-runtime.mjs +2325 -450
  65. package/src/output-styles/default.md +12 -28
  66. package/src/output-styles/extreme-simple.md +9 -6
  67. package/src/output-styles/simple.md +22 -9
  68. package/src/repl.mjs +118 -59
  69. package/src/rules/agent/00-common.md +15 -0
  70. package/src/rules/{bridge → agent}/20-skip-protocol.md +1 -2
  71. package/src/rules/agent/30-explorer.md +22 -0
  72. package/src/rules/{bridge → agent}/40-cycle1-agent.md +7 -0
  73. package/src/rules/{bridge → agent}/41-cycle2-agent.md +7 -0
  74. package/src/rules/{bridge → agent}/42-cycle3-agent.md +7 -0
  75. package/src/rules/lead/01-general.md +9 -5
  76. package/src/rules/lead/04-workflow.md +51 -12
  77. package/src/rules/lead/lead-tool.md +6 -0
  78. package/src/rules/shared/01-tool.md +12 -1
  79. package/src/runtime/agent/orchestrator/activity-bus.mjs +7 -18
  80. package/src/runtime/agent/orchestrator/agent-owner.mjs +11 -0
  81. package/src/runtime/agent/orchestrator/{smart-bridge/bridge-llm.mjs → agent-runtime/agent-dispatch.mjs} +138 -111
  82. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +94 -0
  83. package/src/runtime/agent/orchestrator/{smart-bridge → agent-runtime}/cache-strategy.mjs +32 -23
  84. package/src/runtime/agent/orchestrator/{smart-bridge → agent-runtime}/session-builder.mjs +33 -27
  85. package/src/runtime/agent/orchestrator/{bridge-trace.mjs → agent-trace.mjs} +132 -81
  86. package/src/runtime/agent/orchestrator/cache-mtime.mjs +0 -21
  87. package/src/runtime/agent/orchestrator/config.mjs +174 -55
  88. package/src/runtime/agent/orchestrator/context/collect.mjs +195 -487
  89. package/src/runtime/agent/orchestrator/dispatch-persist.mjs +1 -1
  90. package/src/runtime/agent/orchestrator/internal-roles.mjs +77 -29
  91. package/src/runtime/agent/orchestrator/internal-tools.mjs +5 -6
  92. package/src/runtime/agent/orchestrator/mcp/client.mjs +15 -9
  93. package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +7 -1
  94. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +377 -243
  95. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +146 -93
  96. package/src/runtime/agent/orchestrator/providers/api-usage.mjs +236 -4
  97. package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +49 -0
  98. package/src/runtime/agent/orchestrator/providers/gemini.mjs +58 -13
  99. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +67 -149
  100. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +132 -2
  101. package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +4 -1
  102. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +45 -0
  103. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +61 -116
  104. package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +25 -0
  105. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +79 -255
  106. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +221 -70
  107. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +477 -147
  108. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +343 -496
  109. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +6 -6
  110. package/src/runtime/agent/orchestrator/providers/registry.mjs +88 -51
  111. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +31 -11
  112. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +41 -8
  113. package/src/runtime/agent/orchestrator/session/cache/post-edit-marks.mjs +4 -4
  114. package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +1 -1
  115. package/src/runtime/agent/orchestrator/session/compact.mjs +1173 -267
  116. package/src/runtime/agent/orchestrator/session/context-utils.mjs +199 -36
  117. package/src/runtime/agent/orchestrator/session/loop.mjs +851 -674
  118. package/src/runtime/agent/orchestrator/session/manager.mjs +1593 -466
  119. package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +107 -0
  120. package/src/runtime/agent/orchestrator/session/store.mjs +291 -46
  121. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +2 -2
  122. package/src/runtime/agent/orchestrator/stall-policy.mjs +31 -16
  123. package/src/runtime/agent/orchestrator/tool-loop-guard.mjs +3 -219
  124. package/src/runtime/agent/orchestrator/tools/bash-session.mjs +34 -7
  125. package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +1 -1
  126. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +19 -0
  127. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +9 -9
  128. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +60 -37
  129. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +21 -2
  130. package/src/runtime/agent/orchestrator/tools/builtin/device-paths.mjs +1 -1
  131. package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -7
  132. package/src/runtime/agent/orchestrator/tools/builtin/glob-walk.mjs +1 -3
  133. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +36 -12
  134. package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +2 -0
  135. package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +2 -2
  136. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +5 -12
  137. package/src/runtime/agent/orchestrator/tools/builtin/read-image-resize.mjs +1 -1
  138. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +4 -36
  139. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +2 -40
  140. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +148 -27
  141. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +2 -2
  142. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +43 -75
  143. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +90 -20
  144. package/src/runtime/agent/orchestrator/tools/builtin/snapshot-store.mjs +1 -1
  145. package/src/runtime/agent/orchestrator/tools/builtin.mjs +59 -5
  146. package/src/runtime/agent/orchestrator/tools/code-graph-state.mjs +86 -0
  147. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +11 -11
  148. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +4106 -4019
  149. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +33 -4
  150. package/src/runtime/agent/orchestrator/tools/patch.mjs +90 -6
  151. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +6 -4
  152. package/src/runtime/agent/orchestrator/tools/result-compression.mjs +4 -4
  153. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +8 -1
  154. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +4 -4
  155. package/src/runtime/channels/index.mjs +152 -24
  156. package/src/runtime/channels/lib/scheduler.mjs +18 -14
  157. package/src/runtime/channels/lib/session-discovery.mjs +3 -2
  158. package/src/runtime/channels/lib/tool-format.mjs +0 -2
  159. package/src/runtime/channels/lib/transcript-discovery.mjs +3 -2
  160. package/src/runtime/channels/lib/webhook.mjs +1 -1
  161. package/src/runtime/channels/tool-defs.mjs +29 -29
  162. package/src/runtime/memory/index.mjs +635 -107
  163. package/src/runtime/memory/lib/agent-ipc.mjs +29 -12
  164. package/src/runtime/memory/lib/core-memory-store.mjs +2 -2
  165. package/src/runtime/memory/lib/embedding-model-config.mjs +55 -0
  166. package/src/runtime/memory/lib/embedding-provider.mjs +31 -4
  167. package/src/runtime/memory/lib/embedding-worker.mjs +19 -10
  168. package/src/runtime/memory/lib/memory-cycle1.mjs +38 -17
  169. package/src/runtime/memory/lib/memory-cycle2.mjs +6 -7
  170. package/src/runtime/memory/lib/memory-cycle3.mjs +4 -4
  171. package/src/runtime/memory/lib/memory-ops-policy.mjs +2 -1
  172. package/src/runtime/memory/lib/memory-session-merge.mjs +38 -0
  173. package/src/runtime/memory/lib/memory.mjs +88 -9
  174. package/src/runtime/memory/lib/model-profile.mjs +1 -1
  175. package/src/runtime/memory/lib/pg/adapter.mjs +15 -1
  176. package/src/runtime/memory/lib/pg/supervisor.mjs +12 -0
  177. package/src/runtime/memory/lib/runtime-fetcher.mjs +37 -3
  178. package/src/runtime/memory/lib/session-ingest.mjs +194 -0
  179. package/src/runtime/memory/lib/trace-store.mjs +96 -51
  180. package/src/runtime/memory/tool-defs.mjs +46 -37
  181. package/src/runtime/search/index.mjs +102 -466
  182. package/src/runtime/search/lib/web-tools.mjs +45 -25
  183. package/src/runtime/search/tool-defs.mjs +16 -23
  184. package/src/runtime/shared/abort-controller.mjs +1 -1
  185. package/src/runtime/shared/atomic-file.mjs +4 -3
  186. package/src/runtime/shared/background-tasks.mjs +122 -11
  187. package/src/runtime/shared/child-spawn-gate.mjs +145 -0
  188. package/src/runtime/shared/config.mjs +7 -4
  189. package/src/runtime/shared/err-text.mjs +131 -4
  190. package/src/runtime/shared/llm/cost.mjs +2 -2
  191. package/src/runtime/shared/llm/http-agent.mjs +23 -7
  192. package/src/runtime/shared/llm/index.mjs +34 -11
  193. package/src/runtime/shared/llm/usage-log.mjs +4 -4
  194. package/src/runtime/shared/markdown-frontmatter.mjs +56 -0
  195. package/src/runtime/shared/singleton-owner.mjs +104 -0
  196. package/src/runtime/shared/tool-execution-contract.mjs +199 -20
  197. package/src/runtime/shared/tool-execution-contract.test.mjs +183 -0
  198. package/src/runtime/shared/tool-surface.mjs +624 -98
  199. package/src/runtime/shared/user-data-guard.mjs +0 -2
  200. package/src/standalone/agent-task-status.mjs +203 -0
  201. package/src/standalone/agent-task-status.test.mjs +76 -0
  202. package/src/standalone/agent-tool.mjs +1913 -0
  203. package/src/standalone/channel-worker.mjs +370 -14
  204. package/src/standalone/explore-tool.mjs +165 -70
  205. package/src/standalone/folder-dialog.mjs +314 -0
  206. package/src/standalone/hook-bus.mjs +898 -22
  207. package/src/standalone/memory-runtime-proxy.mjs +320 -0
  208. package/src/standalone/projects.mjs +226 -0
  209. package/src/standalone/provider-admin.mjs +41 -24
  210. package/src/standalone/seeds.mjs +2 -69
  211. package/src/standalone/usage-dashboard.mjs +96 -8
  212. package/src/tui/App.jsx +4798 -2153
  213. package/src/tui/components/AnsiText.jsx +39 -28
  214. package/src/tui/components/ContextPanel.jsx +87 -29
  215. package/src/tui/components/Markdown.jsx +43 -77
  216. package/src/tui/components/MarkdownTable.jsx +9 -184
  217. package/src/tui/components/Message.jsx +28 -11
  218. package/src/tui/components/Picker.jsx +95 -56
  219. package/src/tui/components/PromptInput.jsx +367 -239
  220. package/src/tui/components/QueuedCommands.jsx +1 -1
  221. package/src/tui/components/SlashCommandPalette.jsx +27 -21
  222. package/src/tui/components/Spinner.jsx +67 -38
  223. package/src/tui/components/StatusLine.jsx +606 -38
  224. package/src/tui/components/TextEntryPanel.jsx +128 -9
  225. package/src/tui/components/ToolExecution.jsx +617 -368
  226. package/src/tui/components/TurnDone.jsx +3 -3
  227. package/src/tui/components/UsagePanel.jsx +3 -5
  228. package/src/tui/components/tool-output-format.mjs +365 -0
  229. package/src/tui/components/tool-output-format.test.mjs +220 -0
  230. package/src/tui/dist/index.mjs +8915 -2418
  231. package/src/tui/engine-runtime-notification.test.mjs +115 -0
  232. package/src/tui/engine-tool-result-text.test.mjs +75 -0
  233. package/src/tui/engine.mjs +1455 -279
  234. package/src/tui/figures.mjs +21 -40
  235. package/src/tui/index.jsx +75 -31
  236. package/src/tui/input-editing.mjs +25 -0
  237. package/src/tui/markdown/format-token.mjs +511 -68
  238. package/src/tui/markdown/format-token.test.mjs +216 -0
  239. package/src/tui/markdown/render-ansi.mjs +94 -0
  240. package/src/tui/markdown/render-ansi.test.mjs +108 -0
  241. package/src/tui/markdown/stream-fence.mjs +34 -0
  242. package/src/tui/markdown/stream-fence.test.mjs +26 -0
  243. package/src/tui/markdown/table-layout.mjs +250 -0
  244. package/src/tui/paste-attachments.mjs +0 -7
  245. package/src/tui/spinner-verbs.mjs +1 -2
  246. package/src/tui/statusline-ansi-bridge.mjs +172 -0
  247. package/src/tui/statusline-ansi-bridge.test.mjs +159 -0
  248. package/src/tui/theme.mjs +746 -24
  249. package/src/tui/time-format.mjs +1 -1
  250. package/src/tui/transcript-tool-failures.mjs +67 -0
  251. package/src/tui/transcript-tool-failures.test.mjs +111 -0
  252. package/src/ui/ansi.mjs +1 -2
  253. package/src/ui/markdown.mjs +85 -26
  254. package/src/ui/markdown.test.mjs +70 -0
  255. package/src/ui/model-display.mjs +121 -0
  256. package/src/ui/session-stats.mjs +44 -0
  257. package/src/ui/statusline-context-label.test.mjs +15 -0
  258. package/src/ui/statusline.mjs +386 -178
  259. package/src/ui/tool-card.mjs +3 -16
  260. package/src/vendor/statusline/bin/statusline-lib.mjs +8 -4
  261. package/src/vendor/statusline/bin/statusline-route.mjs +169 -37
  262. package/src/vendor/statusline/bin/statusline-route.test.mjs +80 -0
  263. package/src/vendor/statusline/scripts/lib/gateway-settings.mjs +3 -3
  264. package/src/vendor/statusline/src/gateway/claude-current.mjs +1 -1
  265. package/src/vendor/statusline/src/gateway/route-meta.mjs +44 -6
  266. package/src/vendor/statusline/src/gateway/session-routes.mjs +1 -1
  267. package/src/workflows/default/WORKFLOW.md +12 -5
  268. package/src/workflows/default/workflow.json +0 -1
  269. package/src/workflows/solo/WORKFLOW.md +15 -0
  270. package/src/workflows/solo/workflow.json +7 -0
  271. package/vendor/ink/build/output.js +6 -1
  272. package/src/agents/scheduler-task.md +0 -3
  273. package/src/agents/web-researcher/AGENT.md +0 -3
  274. package/src/agents/web-researcher/agent.json +0 -6
  275. package/src/agents/webhook-handler.md +0 -3
  276. package/src/rules/bridge/00-common.md +0 -5
  277. package/src/rules/bridge/30-explorer.md +0 -4
  278. package/src/rules/lead/00-tool-lead.md +0 -5
  279. package/src/rules/shared/00-language.md +0 -3
  280. package/src/runtime/agent/orchestrator/tools/builtin/native-edit-runner.mjs +0 -110
  281. package/src/runtime/agent/orchestrator/tools/mutation-content-cache.mjs +0 -67
  282. package/src/runtime/memory/lib/bridge-trace-queries.mjs +0 -120
  283. package/src/runtime/shared/llm/pid-cleanup.mjs +0 -27
  284. package/src/standalone/bridge-tool.mjs +0 -1414
  285. package/src/tui/runtime/shared/process-shutdown.mjs +0 -1
@@ -1,23 +1,50 @@
1
1
  import Anthropic from '@anthropic-ai/sdk';
2
2
  import { loadConfig } from '../config.mjs';
3
3
  import { sanitizeToolPairs, sanitizeAnthropicContentPairs } from '../session/context-utils.mjs';
4
- import { classifyError, withRetry } from './retry-classifier.mjs';
5
- import { traceBridgeUsage } from '../bridge-trace.mjs';
4
+ import { classifyError, midstreamBackoffFor, withRetry } from './retry-classifier.mjs';
5
+ import { traceAgentUsage } from '../agent-trace.mjs';
6
6
  import {
7
7
  PROVIDER_FIRST_BYTE_TIMEOUT_MS,
8
8
  createTimeoutSignal,
9
9
  createPassthroughSignal,
10
10
  } from '../stall-policy.mjs';
11
11
  import { createAbortController } from '../../../shared/abort-controller.mjs';
12
- import { parseSSEStream, _classifyMidstreamError } from './anthropic-oauth.mjs';
12
+ import {
13
+ ANTHROPIC_MAX_MIDSTREAM_RETRIES,
14
+ parseSSEStream,
15
+ _classifyMidstreamError,
16
+ } from './anthropic-oauth.mjs';
13
17
  import { buildAnthropicBetaHeaders, supportsAnthropicFastMode } from './anthropic-betas.mjs';
14
18
  import { normalizeContentForAnthropic } from './media-normalization.mjs';
15
19
  import { enrichModels } from './model-catalog.mjs';
16
20
  import { getLlmDispatcher } from '../../../shared/llm/http-agent.mjs';
17
21
 
18
- // 4-BP cache policy aligned with anthropic-oauth — tools + system + tier3
19
- // + messages-tail. 1h TTL requires the extended-cache-ttl beta header,
20
- // which we set on the client via defaultHeaders below.
22
+ async function _midstreamSleepWithAbort(ms, signal) {
23
+ if (!ms) return;
24
+ if (!signal) {
25
+ await new Promise((r) => setTimeout(r, ms));
26
+ return;
27
+ }
28
+ await new Promise((resolve, reject) => {
29
+ const t = setTimeout(() => {
30
+ try { signal.removeEventListener('abort', onAbort); } catch {}
31
+ resolve();
32
+ }, ms);
33
+ const onAbort = () => {
34
+ clearTimeout(t);
35
+ const reason = signal.reason;
36
+ reject(reason instanceof Error ? reason : new Error('Anthropic mid-stream retry backoff aborted'));
37
+ };
38
+ if (signal.aborted) { onAbort(); return; }
39
+ signal.addEventListener('abort', onAbort, { once: true });
40
+ });
41
+ }
42
+
43
+ // 4-BP cache policy aligned with anthropic-oauth — system + tier3 +
44
+ // messages-tail. Tool schemas sit before system and are covered by the system
45
+ // breakpoint, so they do not spend a separate cache_control slot. 1h TTL
46
+ // requires the extended-cache-ttl beta header, which we set on the client via
47
+ // defaultHeaders below.
21
48
  const CACHE_TTL_STABLE = { type: 'ephemeral', ttl: '1h' };
22
49
  const CACHE_TTL_VOLATILE = { type: 'ephemeral' };
23
50
 
@@ -39,23 +66,9 @@ function appendCacheControl(content, ttl = CACHE_TTL_VOLATILE) {
39
66
  return content;
40
67
  }
41
68
 
42
- // Mirrors anthropic-oauth.mjs gate: only the explicitly-tagged sessionMarker
43
- // claims a 1h BP3 slot. composeSystemPrompt prefixes sessionMarker with
44
- // `<!-- bp3-sentinel -->`; volatileTail (also a `<system-reminder>` user
45
- // message) deliberately omits the sentinel so it stays on the message tail.
46
- const BP3_SENTINEL = '<!-- bp3-sentinel -->';
47
-
48
- function findTier3Index(chatMsgs) {
49
- for (let i = 0; i < chatMsgs.length; i++) {
50
- const m = chatMsgs[i];
51
- if (m?.role === 'user' && typeof m.content === 'string'
52
- && m.content.startsWith('<system-reminder>')
53
- && m.content.includes(BP3_SENTINEL)) {
54
- return i;
55
- }
56
- }
57
- return -1;
58
- }
69
+ // BP3 (tier3) rides its own `system` role block (the 3rd system block, tagged
70
+ // cacheTier:'tier3'). buildSystemBlocks applies the tier3 1h cache_control to
71
+ // that block; BP1/BP2 take the system TTL. Mirrors anthropic-oauth.mjs.
59
72
 
60
73
  function resolveCacheTtls(opts) {
61
74
  const strategy = opts?.cacheStrategy || {};
@@ -67,19 +80,40 @@ function resolveCacheTtls(opts) {
67
80
  return fallback;
68
81
  };
69
82
  return {
70
- tools: pick('tools', CACHE_TTL_STABLE),
83
+ tools: pick('tools', null),
71
84
  system: pick('system', CACHE_TTL_STABLE),
72
85
  tier3: pick('tier3', CACHE_TTL_STABLE),
73
86
  messages: pick('messages', CACHE_TTL_STABLE),
74
87
  };
75
88
  }
76
89
 
90
+ function buildSystemBlocks(systemMsgs, systemTtl, tier3Ttl) {
91
+ // systemMsgs is an array of { content, cacheTier }. Each non-empty element
92
+ // becomes its own content block: cacheTier:'tier3' (BP3 sessionMarker) gets
93
+ // tier3Ttl, every other block (BP1/BP2) gets systemTtl. A null TTL leaves
94
+ // the corresponding block uncached.
95
+ const items = Array.isArray(systemMsgs)
96
+ ? systemMsgs
97
+ .map(m => ({
98
+ text: typeof m?.content === 'string' ? m.content.trim() : '',
99
+ tier: m?.cacheTier === 'tier3' ? 'tier3' : 'system',
100
+ }))
101
+ .filter(it => it.text)
102
+ : [];
103
+ return items.map(it => {
104
+ const block = { type: 'text', text: it.text };
105
+ const ttl = it.tier === 'tier3' ? tier3Ttl : systemTtl;
106
+ if (ttl) block.cache_control = ttl;
107
+ return block;
108
+ });
109
+ }
110
+
77
111
  const MODELS = [
78
- { id: 'claude-opus-4-8', name: 'Claude Opus 4.8', provider: 'anthropic', contextWindow: 1000000 },
79
- { id: 'claude-opus-4-7', name: 'Claude Opus 4.7', provider: 'anthropic', contextWindow: 1000000 },
80
- { id: 'claude-opus-4-6', name: 'Claude Opus 4.6', provider: 'anthropic', contextWindow: 1000000 },
81
- { id: 'claude-sonnet-4-6', name: 'Claude Sonnet 4.6', provider: 'anthropic', contextWindow: 1000000 },
82
- { id: 'claude-haiku-4-5-20251001', name: 'Claude Haiku 4.5', provider: 'anthropic', contextWindow: 200000 },
112
+ { id: 'claude-opus-4-8', name: 'Claude Opus 4.8', provider: 'anthropic', family: 'opus', contextWindow: 1000000 },
113
+ { id: 'claude-opus-4-7', name: 'Claude Opus 4.7', provider: 'anthropic', family: 'opus', contextWindow: 1000000 },
114
+ { id: 'claude-opus-4-6', name: 'Claude Opus 4.6', provider: 'anthropic', family: 'opus', contextWindow: 1000000 },
115
+ { id: 'claude-sonnet-4-6', name: 'Claude Sonnet 4.6', provider: 'anthropic', family: 'sonnet', contextWindow: 1000000 },
116
+ { id: 'claude-haiku-4-5-20251001', name: 'Claude Haiku 4.5', provider: 'anthropic', family: 'haiku', contextWindow: 200000 },
83
117
  ];
84
118
  const ANTHROPIC_VERSION = '2023-06-01';
85
119
 
@@ -131,8 +165,21 @@ const EFFORT_BUDGET = {
131
165
  low: 1024,
132
166
  medium: 4096,
133
167
  high: 16384,
168
+ xhigh: 32768,
134
169
  max: 32768,
135
170
  };
171
+
172
+ const MIN_THINKING_BUDGET = 1024;
173
+ const THINKING_OUTPUT_RESERVE = 1024;
174
+
175
+ function clampThinkingBudgetTokens(value, maxTokens) {
176
+ const desired = Math.floor(Number(value));
177
+ const max = Math.floor(Number(maxTokens));
178
+ if (!Number.isFinite(desired) || desired <= 0 || !Number.isFinite(max)) return null;
179
+ const ceiling = max - THINKING_OUTPUT_RESERVE;
180
+ if (ceiling < MIN_THINKING_BUDGET) return null;
181
+ return Math.max(MIN_THINKING_BUDGET, Math.min(desired, ceiling));
182
+ }
136
183
  // Anthropic forbids oneOf / allOf / anyOf at the TOP level of input_schema.
137
184
  // Mirror the same sanitizer as anthropic-oauth.mjs so both providers are safe.
138
185
  function _sanitizeInputSchema(schema, toolName) {
@@ -169,9 +216,11 @@ function _sanitizeInputSchema(schema, toolName) {
169
216
  if (addition) description = description ? `${description} ${addition}` : addition;
170
217
  }
171
218
  const mergedPropsCount = Object.keys(mergedProps).length;
172
- process.stderr.write(
173
- `[anthropic-sanitizer] tool="${toolName ?? ''}" compound="${compoundKey}" branches=${Array.isArray(compound) ? compound.length : 0} mergedProps=${mergedPropsCount}\n`
174
- );
219
+ if (process.env.MIXDOG_DEBUG_SESSION_LOG) {
220
+ process.stderr.write(
221
+ `[anthropic-sanitizer] tool="${toolName ?? ''}" compound="${compoundKey}" branches=${Array.isArray(compound) ? compound.length : 0} mergedProps=${mergedPropsCount}\n`
222
+ );
223
+ }
175
224
  return {
176
225
  type: 'object',
177
226
  ...(description ? { description } : {}),
@@ -180,11 +229,28 @@ function _sanitizeInputSchema(schema, toolName) {
180
229
  }
181
230
 
182
231
  function toAnthropicTools(tools) {
183
- return tools.map((t) => ({
184
- name: t.name,
185
- description: t.description,
186
- input_schema: _sanitizeInputSchema(t.inputSchema, t.name),
187
- }));
232
+ return tools.map((t) => {
233
+ const out = {
234
+ name: t.name,
235
+ description: t.description,
236
+ input_schema: _sanitizeInputSchema(t.inputSchema, t.name),
237
+ };
238
+ if (t.deferLoading === true || t.defer_loading === true) out.defer_loading = true;
239
+ return out;
240
+ });
241
+ }
242
+ function nativeAnthropicTools(opts) {
243
+ return Array.isArray(opts?.nativeTools)
244
+ ? opts.nativeTools.filter(t => t && typeof t === 'object')
245
+ : [];
246
+ }
247
+ function deferredAnthropicTools(activeTools, opts) {
248
+ if (opts?.session?.deferredNativeTools !== true) return [];
249
+ const active = new Set((activeTools || []).map((tool) => String(tool?.name || '').trim()).filter(Boolean));
250
+ const catalog = Array.isArray(opts.session.deferredToolCatalog) ? opts.session.deferredToolCatalog : [];
251
+ return catalog
252
+ .filter((tool) => tool?.name && !active.has(String(tool.name)))
253
+ .map((tool) => ({ ...tool, deferLoading: true }));
188
254
  }
189
255
  function toAnthropicMessages(messages) {
190
256
  // Marker-free lowering. cache_control is applied AFTER sanitization by
@@ -218,10 +284,15 @@ function toAnthropicMessages(messages) {
218
284
  }
219
285
  if (m.role === 'tool') {
220
286
  const last = result[result.length - 1];
287
+ const refs = Array.isArray(m.nativeToolSearch?.toolReferences)
288
+ ? m.nativeToolSearch.toolReferences.map((name) => String(name || '').trim()).filter(Boolean)
289
+ : [];
221
290
  const block = {
222
291
  type: 'tool_result',
223
292
  tool_use_id: m.toolCallId || '',
224
- content: normalizeContentForAnthropic(m.content),
293
+ content: refs.length
294
+ ? refs.map((name) => ({ type: 'tool_reference', tool_name: name }))
295
+ : normalizeContentForAnthropic(m.content),
225
296
  };
226
297
  if (last?.role === 'user' && Array.isArray(last.content)) {
227
298
  last.content.push(block);
@@ -241,17 +312,15 @@ function toAnthropicMessages(messages) {
241
312
  // sanitizeAnthropicContentPairs has already run (and must NOT run again
242
313
  // after this), the blocks we mark here are exactly the blocks the provider
243
314
  // sees, so the cache breakpoint is stable across turns.
244
- // tier3: the user message whose first text block / string content
245
- // startsWith '<system-reminder>' AND includes BP3_SENTINEL —
246
- // mark its last content block with tier3Ttl.
247
315
  // message-anchor: prefer a safe tool_result tail, then a previous real user
248
316
  // text turn if another slot remains. Synthetic
249
317
  // <system-reminder> messages and current pure-text prompts
250
318
  // are excluded so per-call volatileTail/current prompt
251
319
  // content never becomes a 1h prefix key.
252
- // messageTtl === null disables the tail; tier3Ttl === null disables tier3.
320
+ // messageTtl === null disables the tail. BP3 (tier3) now rides a system block,
321
+ // so it is no longer marked here.
253
322
  // ANTHROPIC_MSG_SLOTS=0 is honoured upstream by passing messageTtl = null.
254
- function applyAnthropicCacheMarkers(sanitizedMessages, { messageTtl = CACHE_TTL_VOLATILE, messageSlots = 1, tier3Ttl = null } = {}) {
323
+ function applyAnthropicCacheMarkers(sanitizedMessages, { messageTtl = CACHE_TTL_VOLATILE, messageSlots = 1 } = {}) {
255
324
  if (!Array.isArray(sanitizedMessages) || sanitizedMessages.length === 0) {
256
325
  return sanitizedMessages;
257
326
  }
@@ -265,25 +334,20 @@ function applyAnthropicCacheMarkers(sanitizedMessages, { messageTtl = CACHE_TTL_
265
334
  return '';
266
335
  };
267
336
  const isSystemReminder = (content) => firstText(content).startsWith('<system-reminder>');
268
- const isTier3SystemReminder = (content) => {
269
- const text = firstText(content);
270
- return text.startsWith('<system-reminder>') && text.includes(BP3_SENTINEL);
271
- };
272
337
 
273
338
  const markLast = (msg, ttl) => {
274
339
  if (!msg) return;
275
340
  msg.content = appendCacheControl(msg.content, ttl);
276
341
  };
277
342
  const ttlRank = (ttl) => ttl?.ttl === '1h' ? 2 : 1;
278
- const canMarkMessageIdx = (idx, tier3Idx) => {
343
+ const canMarkMessageIdx = (idx) => {
344
+ // System-reminder messages (volatileTail / roleSpecific BP4) vary
345
+ // per-call, so never pin them with a 1h marker. The 1h system blocks
346
+ // (BP1/BP2/BP3) already satisfy Anthropic's "1h before 5m" ordering.
279
347
  if (idx < 0) return false;
280
348
  const msg = sanitizedMessages[idx];
281
349
  if (ttlRank(messageTtl) > ttlRank(CACHE_TTL_VOLATILE)
282
- && isSystemReminder(msg?.content)
283
- && !isTier3SystemReminder(msg?.content)) {
284
- return false;
285
- }
286
- if (tier3Idx >= 0 && idx < tier3Idx && ttlRank(messageTtl) < ttlRank(tier3Ttl)) {
350
+ && isSystemReminder(msg?.content)) {
287
351
  return false;
288
352
  }
289
353
  return true;
@@ -312,29 +376,16 @@ function applyAnthropicCacheMarkers(sanitizedMessages, { messageTtl = CACHE_TTL_
312
376
  return -1;
313
377
  };
314
378
 
315
- // tier3 — locate the sentinel-tagged system-reminder user message.
316
- let tier3MsgIdx = -1;
317
- if (tier3Ttl !== null) {
318
- for (let i = 0; i < sanitizedMessages.length; i++) {
319
- const m = sanitizedMessages[i];
320
- if (m?.role === 'user' && isTier3SystemReminder(m.content)) {
321
- tier3MsgIdx = i;
322
- break;
323
- }
324
- }
325
- if (tier3MsgIdx >= 0) markLast(sanitizedMessages[tier3MsgIdx], tier3Ttl);
326
- }
327
-
328
379
  if (messageTtl !== null) {
329
380
  const slots = Math.max(0, Math.min(4, Number(messageSlots) || 0));
330
- const marked = new Set(tier3MsgIdx >= 0 ? [tier3MsgIdx] : []);
381
+ const marked = new Set();
331
382
  const candidates = [latestToolResultTailIdx(), previousUserTextAnchorIdx()];
332
383
  for (const idx of candidates) {
333
384
  if (slots <= 0) break;
334
- if (idx < 0 || marked.has(idx) || !canMarkMessageIdx(idx, tier3MsgIdx)) continue;
385
+ if (idx < 0 || marked.has(idx) || !canMarkMessageIdx(idx)) continue;
335
386
  markLast(sanitizedMessages[idx], messageTtl);
336
387
  marked.add(idx);
337
- if (marked.size - (tier3MsgIdx >= 0 ? 1 : 0) >= slots) break;
388
+ if (marked.size >= slots) break;
338
389
  }
339
390
  }
340
391
 
@@ -352,7 +403,7 @@ export class AnthropicProvider {
352
403
  constructor(config) {
353
404
  this.config = config;
354
405
  this.name = config.name || 'anthropic';
355
- const betaHeaders = config.disableBetaHeaders ? null : buildAnthropicBetaHeaders();
406
+ const betaHeaders = config.disableBetaHeaders ? null : buildAnthropicBetaHeaders({ toolSearch: true });
356
407
  this.client = new Anthropic({
357
408
  apiKey: config.apiKey || process.env.ANTHROPIC_API_KEY,
358
409
  ...(config.baseURL ? { baseURL: config.baseURL } : {}),
@@ -366,7 +417,7 @@ export class AnthropicProvider {
366
417
  const newKey = cfg?.apiKey || this.config.apiKey || (this.name === 'anthropic' ? process.env.ANTHROPIC_API_KEY : null);
367
418
  if (newKey) {
368
419
  this.config = { ...(this.config || {}), ...(cfg || {}), apiKey: newKey };
369
- const betaHeaders = this.config.disableBetaHeaders ? null : buildAnthropicBetaHeaders();
420
+ const betaHeaders = this.config.disableBetaHeaders ? null : buildAnthropicBetaHeaders({ toolSearch: true });
370
421
  this.client = new Anthropic({
371
422
  apiKey: newKey,
372
423
  ...(this.config.baseURL ? { baseURL: this.config.baseURL } : {}),
@@ -402,17 +453,17 @@ export class AnthropicProvider {
402
453
 
403
454
  const systemMsgs = messages.filter(m => m.role === 'system');
404
455
  const chatMsgs = messages.filter(m => m.role !== 'system');
405
- const systemText = systemMsgs.map(m => m.content).join('\n\n') || undefined;
456
+ // BP1 baseRules + BP2 stableSystem at ttls.system; BP3 sessionMarker
457
+ // (cacheTier:'tier3') at ttls.tier3 — each its own system content block.
458
+ const systemBlocks = buildSystemBlocks(systemMsgs, ttls.system, ttls.tier3);
406
459
 
407
460
  // 4-BP budget: aligned with anthropic-oauth. tools BP is dropped —
408
461
  // system BP covers the tools prefix via Anthropic prefix semantics
409
462
  // (order: tools → system → messages). That frees 1 slot for
410
463
  // messages-tail.
411
464
  const toolsBpUsed = 0;
412
- const systemBpUsed = ttls.system && systemText ? 1 : 0;
413
- const tier3Idx = ttls.tier3 ? findTier3Index(chatMsgs) : -1;
414
- const tier3BpUsed = tier3Idx >= 0 ? 1 : 0;
415
- const usedSlots = toolsBpUsed + systemBpUsed + tier3BpUsed;
465
+ const systemBpUsed = systemBlocks.filter(b => b.cache_control).length;
466
+ const usedSlots = toolsBpUsed + systemBpUsed;
416
467
  // Env override for BP strategy. ANTHROPIC_MSG_SLOTS=0 disables
417
468
  // message caching entirely. Any value >=1 first marks the previous
418
469
  // user text turn; a second free slot marks the tail. Mirrors
@@ -428,34 +479,32 @@ export class AnthropicProvider {
428
479
  // or delete a marked block. NEVER sanitize again after this.
429
480
  // msgSlots === 0 → message-tail disabled.
430
481
  const tailTtl = msgSlots > 0 ? ttls.messages : null;
431
- const tier3Ttl = tier3Idx >= 0 ? ttls.tier3 : null;
432
482
  const anthropicMessages = applyAnthropicCacheMarkers(
433
483
  toAnthropicMessages(chatMsgs),
434
- { messageTtl: tailTtl, messageSlots: msgSlots, tier3Ttl },
484
+ { messageTtl: tailTtl, messageSlots: msgSlots },
435
485
  );
436
486
 
437
487
  const params = {
438
488
  model: useModel,
439
489
  max_tokens: maxTokens,
440
- system: systemText
441
- ? [ttls.system
442
- ? { type: 'text', text: systemText, cache_control: ttls.system }
443
- : { type: 'text', text: systemText }]
444
- : undefined,
490
+ system: systemBlocks.length ? systemBlocks : undefined,
445
491
  messages: anthropicMessages,
446
492
  };
447
- if (tools?.length) {
493
+ const nativeTools = nativeAnthropicTools(opts);
494
+ if (tools?.length || nativeTools.length) {
448
495
  // No cache_control on tools — the system BP covers tools via
449
496
  // Anthropic prefix semantics (order: tools → system → messages).
450
- params.tools = toAnthropicTools(tools);
497
+ params.tools = [...nativeTools, ...toAnthropicTools([...(tools || []), ...deferredAnthropicTools(tools || [], opts)])];
451
498
  }
452
499
  // Effort → extended thinking budget. Gateway inherit mode may pass the
453
- // exact Claude Code budget from the incoming Anthropic request.
500
+ // exact OAuth client budget from the incoming Anthropic request.
454
501
  const thinkingBudgetTokens = Number(opts.thinkingBudgetTokens);
455
- if (Number.isFinite(thinkingBudgetTokens) && thinkingBudgetTokens > 0) {
456
- params.thinking = { type: 'enabled', budget_tokens: Math.floor(thinkingBudgetTokens) };
457
- } else if (opts.effort && EFFORT_BUDGET[opts.effort]) {
458
- params.thinking = { type: 'enabled', budget_tokens: EFFORT_BUDGET[opts.effort] };
502
+ const requestedThinkingBudget = Number.isFinite(thinkingBudgetTokens) && thinkingBudgetTokens > 0
503
+ ? thinkingBudgetTokens
504
+ : (opts.effort && EFFORT_BUDGET[opts.effort] ? EFFORT_BUDGET[opts.effort] : null);
505
+ const budgetTokens = clampThinkingBudgetTokens(requestedThinkingBudget, maxTokens);
506
+ if (budgetTokens) {
507
+ params.thinking = { type: 'enabled', budget_tokens: budgetTokens };
459
508
  }
460
509
  // Fast mode → speed: "fast" on models Anthropic marks as speed-capable.
461
510
  if (opts.fast === true && supportsAnthropicFastMode(useModel)) {
@@ -479,7 +528,7 @@ export class AnthropicProvider {
479
528
  // emitting SSE deltas must not be killed by a fixed total-lifetime timer.
480
529
  // Mirrors the OAuth provider (Option A). Bounded instead by the
481
530
  // per-attempt first-byte/HTTP-response timeout, the SSE idle watchdog,
482
- // the bridge stall watchdog, and externalSignal (client disconnect /
531
+ // the agent stall watchdog, and externalSignal (client disconnect /
483
532
  // replaced-by-newer-request). totalSignal is a pure pass-through.
484
533
  const externalSignal = opts.signal || null;
485
534
  const totalTimeout = createPassthroughSignal(externalSignal);
@@ -493,10 +542,11 @@ export class AnthropicProvider {
493
542
  const betaHeaders = {
494
543
  'anthropic-beta': buildAnthropicBetaHeaders({
495
544
  fastMode: this.fastModeBetaHeaderLatched,
545
+ toolSearch: true,
496
546
  }),
497
547
  };
498
548
 
499
- const MAX_MIDSTREAM_RETRIES = 1;
549
+ const MAX_MIDSTREAM_RETRIES = ANTHROPIC_MAX_MIDSTREAM_RETRIES;
500
550
  let firstAttemptError = null;
501
551
  let firstAttemptClassifier = null;
502
552
 
@@ -510,7 +560,7 @@ export class AnthropicProvider {
510
560
  const liveModel = parseResult.model || useModel;
511
561
 
512
562
  if (usageRaw || input || output || cacheRead || cacheWrite) {
513
- traceBridgeUsage({
563
+ traceAgentUsage({
514
564
  sessionId: opts.sessionId || opts.session?.id || null,
515
565
  iteration: Number.isFinite(Number(opts.iteration)) ? Number(opts.iteration) : null,
516
566
  inputTokens: input,
@@ -694,6 +744,7 @@ export class AnthropicProvider {
694
744
  `[${this.name}] empty stream (no message_start) — retry ${attemptIndex + 1}/${MAX_MIDSTREAM_RETRIES}\n`,
695
745
  );
696
746
  } catch {}
747
+ await _midstreamSleepWithAbort(midstreamBackoffFor(attemptIndex + 1), totalSignal);
697
748
  continue;
698
749
  }
699
750
  if ((err?.truncatedStream === true || err?.code === 'TRUNCATED_STREAM')
@@ -708,6 +759,7 @@ export class AnthropicProvider {
708
759
  `[${this.name}] truncated stream — retry ${attemptIndex + 1}/${MAX_MIDSTREAM_RETRIES}\n`,
709
760
  );
710
761
  } catch {}
762
+ await _midstreamSleepWithAbort(midstreamBackoffFor(attemptIndex + 1), totalSignal);
711
763
  continue;
712
764
  }
713
765
  const classifier = _classifyMidstreamError(err, midState);
@@ -720,6 +772,7 @@ export class AnthropicProvider {
720
772
  `[${this.name}] mid-stream recovered: retry ${attemptIndex + 1}/${MAX_MIDSTREAM_RETRIES} (cause: ${classifier})\n`,
721
773
  );
722
774
  } catch {}
775
+ await _midstreamSleepWithAbort(midstreamBackoffFor(attemptIndex + 1), totalSignal);
723
776
  continue;
724
777
  }
725
778
  if (attemptIndex > 0 && firstAttemptError) {