mixdog 0.8.1 → 0.9.1

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