@causyne/agent 0.0.0-stage → 1.10.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 (274) hide show
  1. package/LICENSE +102 -0
  2. package/README.md +31 -2
  3. package/SECURITY.md +24 -0
  4. package/bin/causyne.js +13 -0
  5. package/docs/ARCHITECTURE.md +63 -0
  6. package/docs/PROVENANCE.md +29 -0
  7. package/docs/SECURITY_REVIEW.md +89 -0
  8. package/docs/THIRD_PARTY_NOTICES.md +49 -0
  9. package/native-skills/ai-engineering/context-window-management/SKILL.md +43 -0
  10. package/native-skills/ai-engineering/llm-evaluation/SKILL.md +46 -0
  11. package/native-skills/ai-engineering/model-cost-and-latency/SKILL.md +46 -0
  12. package/native-skills/ai-engineering/prompt-construction/SKILL.md +42 -0
  13. package/native-skills/ai-engineering/retrieval-augmented-generation/SKILL.md +42 -0
  14. package/native-skills/ai-engineering/structured-output-and-schemas/SKILL.md +45 -0
  15. package/native-skills/ai-engineering/tool-calling-agents/SKILL.md +43 -0
  16. package/native-skills/android/app-performance-profiling/SKILL.md +42 -0
  17. package/native-skills/android/background-work-scheduling/SKILL.md +47 -0
  18. package/native-skills/android/compose-state-and-recomposition/SKILL.md +43 -0
  19. package/native-skills/android/gradle-build-configuration/SKILL.md +41 -0
  20. package/native-skills/android/lifecycle-and-leaks/SKILL.md +46 -0
  21. package/native-skills/android/play-store-release/SKILL.md +43 -0
  22. package/native-skills/android/room-and-persistence/SKILL.md +40 -0
  23. package/native-skills/backend/authentication-and-sessions/SKILL.md +45 -0
  24. package/native-skills/backend/background-jobs-and-queues/SKILL.md +42 -0
  25. package/native-skills/backend/caching-strategy/SKILL.md +44 -0
  26. package/native-skills/backend/graceful-shutdown/SKILL.md +42 -0
  27. package/native-skills/backend/rate-limiting-and-quotas/SKILL.md +40 -0
  28. package/native-skills/backend/rest-api-design/SKILL.md +40 -0
  29. package/native-skills/backend/webhook-delivery-and-receipt/SKILL.md +43 -0
  30. package/native-skills/cloud/cost-optimisation/SKILL.md +46 -0
  31. package/native-skills/cloud/iam-least-privilege/SKILL.md +43 -0
  32. package/native-skills/cloud/infrastructure-as-code/SKILL.md +43 -0
  33. package/native-skills/cloud/multi-region-and-failover/SKILL.md +43 -0
  34. package/native-skills/cloud/serverless-patterns/SKILL.md +47 -0
  35. package/native-skills/data/data-quality-monitoring/SKILL.md +51 -0
  36. package/native-skills/data/etl-pipeline-reliability/SKILL.md +43 -0
  37. package/native-skills/data/exploratory-analysis/SKILL.md +43 -0
  38. package/native-skills/data/metric-definition/SKILL.md +42 -0
  39. package/native-skills/data/notebook-to-production/SKILL.md +43 -0
  40. package/native-skills/database/backup-and-recovery/SKILL.md +43 -0
  41. package/native-skills/database/connection-pooling/SKILL.md +42 -0
  42. package/native-skills/database/data-integrity-and-constraints/SKILL.md +42 -0
  43. package/native-skills/database/query-optimisation/SKILL.md +38 -0
  44. package/native-skills/database/safe-migrations/SKILL.md +47 -0
  45. package/native-skills/database/schema-design/SKILL.md +38 -0
  46. package/native-skills/database/transactions-and-isolation/SKILL.md +41 -0
  47. package/native-skills/design/colour-system-construction/SKILL.md +38 -0
  48. package/native-skills/design/design-direction-brief/SKILL.md +41 -0
  49. package/native-skills/design/motion-and-transitions/SKILL.md +38 -0
  50. package/native-skills/design/responsive-strategy/SKILL.md +39 -0
  51. package/native-skills/design/spacing-and-rhythm/SKILL.md +38 -0
  52. package/native-skills/design/type-scale-and-hierarchy/SKILL.md +39 -0
  53. package/native-skills/design/visual-qa-loop/SKILL.md +42 -0
  54. package/native-skills/devops/docker-multi-stage-builds/SKILL.md +60 -0
  55. package/native-skills/devops/github-actions-ci/SKILL.md +60 -0
  56. package/native-skills/devops/incident-triage/SKILL.md +38 -0
  57. package/native-skills/devops/kubernetes-deployment-hardening/SKILL.md +64 -0
  58. package/native-skills/devops/observability-baseline/SKILL.md +38 -0
  59. package/native-skills/devops/secret-management/SKILL.md +40 -0
  60. package/native-skills/devops/terraform-state-and-modules/SKILL.md +36 -0
  61. package/native-skills/devops/zero-downtime-deploys/SKILL.md +40 -0
  62. package/native-skills/git/bisect-regression-hunt/SKILL.md +43 -0
  63. package/native-skills/git/branch-strategy/SKILL.md +39 -0
  64. package/native-skills/git/commit-hygiene/SKILL.md +41 -0
  65. package/native-skills/git/history-recovery/SKILL.md +44 -0
  66. package/native-skills/git/large-repository-surgery/SKILL.md +46 -0
  67. package/native-skills/git/merge-conflict-resolution/SKILL.md +42 -0
  68. package/native-skills/media/cinematic-3d-shot/SKILL.md +104 -0
  69. package/native-skills/mobile/app-startup-optimisation/SKILL.md +41 -0
  70. package/native-skills/mobile/cross-platform-tradeoffs/SKILL.md +42 -0
  71. package/native-skills/mobile/deep-linking/SKILL.md +39 -0
  72. package/native-skills/mobile/mobile-storage-security/SKILL.md +45 -0
  73. package/native-skills/mobile/offline-first-sync/SKILL.md +42 -0
  74. package/native-skills/mobile/push-notifications/SKILL.md +38 -0
  75. package/native-skills/performance/algorithmic-complexity/SKILL.md +43 -0
  76. package/native-skills/performance/concurrency-and-parallelism/SKILL.md +42 -0
  77. package/native-skills/performance/load-testing/SKILL.md +42 -0
  78. package/native-skills/performance/memory-and-leak-hunting/SKILL.md +41 -0
  79. package/native-skills/performance/network-and-payload-efficiency/SKILL.md +42 -0
  80. package/native-skills/performance/profiling-methodology/SKILL.md +46 -0
  81. package/native-skills/python/async-correctness/SKILL.md +40 -0
  82. package/native-skills/python/cli-tool-construction/SKILL.md +44 -0
  83. package/native-skills/python/data-processing-pipelines/SKILL.md +38 -0
  84. package/native-skills/python/environment-isolation/SKILL.md +41 -0
  85. package/native-skills/python/packaging-and-distribution/SKILL.md +56 -0
  86. package/native-skills/python/pytest-suite-design/SKILL.md +39 -0
  87. package/native-skills/python/type-hints-and-static-checking/SKILL.md +42 -0
  88. package/native-skills/security/authorization-model/SKILL.md +42 -0
  89. package/native-skills/security/crypto-usage/SKILL.md +48 -0
  90. package/native-skills/security/dependency-supply-chain/SKILL.md +42 -0
  91. package/native-skills/security/input-validation-and-injection/SKILL.md +44 -0
  92. package/native-skills/security/secure-file-uploads/SKILL.md +46 -0
  93. package/native-skills/security/security-headers-and-csp/SKILL.md +44 -0
  94. package/native-skills/security/threat-modelling/SKILL.md +46 -0
  95. package/native-skills/testing/end-to-end-test-stability/SKILL.md +42 -0
  96. package/native-skills/testing/property-based-testing/SKILL.md +46 -0
  97. package/native-skills/testing/regression-test-from-a-bug/SKILL.md +41 -0
  98. package/native-skills/testing/snapshot-and-visual-regression/SKILL.md +42 -0
  99. package/native-skills/testing/test-doubles-and-mocking/SKILL.md +43 -0
  100. package/native-skills/testing/test-strategy-and-pyramid/SKILL.md +39 -0
  101. package/native-skills/testing/writing-good-assertions/SKILL.md +42 -0
  102. package/native-skills/web/accessible-components/SKILL.md +45 -0
  103. package/native-skills/web/api-client-resilience/SKILL.md +39 -0
  104. package/native-skills/web/bundle-size-reduction/SKILL.md +38 -0
  105. package/native-skills/web/core-web-vitals/SKILL.md +40 -0
  106. package/native-skills/web/css-layout-systems/SKILL.md +41 -0
  107. package/native-skills/web/form-validation-ux/SKILL.md +39 -0
  108. package/native-skills/web/react-state-architecture/SKILL.md +41 -0
  109. package/native-skills/web/ssr-hydration-correctness/SKILL.md +45 -0
  110. package/package.json +68 -4
  111. package/src/agentLoop.js +2458 -0
  112. package/src/appChannel.js +109 -0
  113. package/src/approvalGrants.js +66 -0
  114. package/src/auditLog.js +52 -0
  115. package/src/autoUpdate.js +12 -0
  116. package/src/capabilities.js +36 -0
  117. package/src/checkpoints.js +1199 -0
  118. package/src/cliMode.js +638 -0
  119. package/src/commands.js +805 -0
  120. package/src/compiledSkills.js +540 -0
  121. package/src/config.js +130 -0
  122. package/src/contextManager.js +498 -0
  123. package/src/cortex/autoCondenser.js +192 -0
  124. package/src/cortex/provisioner.js +27 -0
  125. package/src/cortex/vectorDb.js +447 -0
  126. package/src/designPalettes.js +99 -0
  127. package/src/devMode.js +6 -0
  128. package/src/embodimentSession.js +180 -0
  129. package/src/entrypoint.js +194 -0
  130. package/src/execSafe.js +21 -0
  131. package/src/gitSnapshot.js +378 -0
  132. package/src/mcp/jsonRpc.js +165 -0
  133. package/src/mcp/mcpClient.js +136 -0
  134. package/src/mcp/mcpRegistry.js +207 -0
  135. package/src/memory.js +62 -0
  136. package/src/memoryManager.js +124 -0
  137. package/src/modelCatalogue.js +132 -0
  138. package/src/modelIds.js +42 -0
  139. package/src/modelRouter.js +145 -0
  140. package/src/outputStore.js +246 -0
  141. package/src/perception.js +124 -0
  142. package/src/planCache.js +437 -0
  143. package/src/procSignal.js +32 -0
  144. package/src/providers/anthropic.js +408 -0
  145. package/src/providers/base.js +328 -0
  146. package/src/providers/dashscope-byok.js +86 -0
  147. package/src/providers/deepseek.js +80 -0
  148. package/src/providers/factory.js +37 -0
  149. package/src/providers/gemini.js +346 -0
  150. package/src/providers/moonshot.js +73 -0
  151. package/src/providers/muse.js +169 -0
  152. package/src/providers/ollama.js +104 -0
  153. package/src/providers/openai-compatible.js +84 -0
  154. package/src/providers/openai.js +20 -0
  155. package/src/providers/qwen-byok.js +76 -0
  156. package/src/providers/xai.js +73 -0
  157. package/src/provisioning/capabilityConsent.js +24 -0
  158. package/src/provisioning/capabilityProvisioner.js +271 -0
  159. package/src/provisioning/capabilityTools.js +29 -0
  160. package/src/reporting.js +373 -0
  161. package/src/sandbox.js +529 -0
  162. package/src/sessionStore.js +279 -0
  163. package/src/skillsManager.js +288 -0
  164. package/src/slopDetector.js +307 -0
  165. package/src/taskBuffer.js +45 -0
  166. package/src/taskShape.js +241 -0
  167. package/src/textDiff.js +150 -0
  168. package/src/tokenTracker.js +560 -0
  169. package/src/tool-scripts/ax_snapshot.applescript +39 -0
  170. package/src/tool-scripts/ax_snapshot.ps1 +10 -0
  171. package/src/tool-scripts/desktop_act.applescript +58 -0
  172. package/src/tool-scripts/resolve_automator.py +119 -0
  173. package/src/toolPacks.js +524 -0
  174. package/src/tools/archive.js +63 -0
  175. package/src/tools/astryxOracle.js +78 -0
  176. package/src/tools/axSnapshot.js +165 -0
  177. package/src/tools/blenderAutomator.js +349 -0
  178. package/src/tools/browserAutomator.js +252 -0
  179. package/src/tools/checkSystemSpecs.js +64 -0
  180. package/src/tools/clipboard.js +36 -0
  181. package/src/tools/codeSearchAst.js +101 -0
  182. package/src/tools/codeSearcher.js +230 -0
  183. package/src/tools/codeTools.js +262 -0
  184. package/src/tools/codebaseSearch.js +75 -0
  185. package/src/tools/createSkill.js +129 -0
  186. package/src/tools/deferredMcpSearch.js +246 -0
  187. package/src/tools/desktopAct.js +208 -0
  188. package/src/tools/desktopOpen.js +34 -0
  189. package/src/tools/dynamicLoader.js +44 -0
  190. package/src/tools/executeProceduralMedia.js +93 -0
  191. package/src/tools/fsDelete.js +51 -0
  192. package/src/tools/fsEdit.js +105 -0
  193. package/src/tools/fsFind.js +128 -0
  194. package/src/tools/fsList.js +77 -0
  195. package/src/tools/fsMove.js +57 -0
  196. package/src/tools/fsRead.js +87 -0
  197. package/src/tools/fsSearch.js +198 -0
  198. package/src/tools/fsWrite.js +41 -0
  199. package/src/tools/gameAiBehaviorTree.js +208 -0
  200. package/src/tools/gameAnimationRig.js +194 -0
  201. package/src/tools/gameAssetFetcher.js +372 -0
  202. package/src/tools/gameAssetHarvester.js +210 -0
  203. package/src/tools/gameAudioSynth.js +267 -0
  204. package/src/tools/gameBuilder.js +886 -0
  205. package/src/tools/gameCanvasAudit.js +101 -0
  206. package/src/tools/gameCinematicSequencer.js +151 -0
  207. package/src/tools/gameDataForge.js +124 -0
  208. package/src/tools/gameDialogueTreeGen.js +166 -0
  209. package/src/tools/gameEconomyBalancer.js +161 -0
  210. package/src/tools/gameEntitySpawner.js +207 -0
  211. package/src/tools/gameLoreForge.js +153 -0
  212. package/src/tools/gameQuestArcBuilder.js +152 -0
  213. package/src/tools/gameSceneBuilder.js +158 -0
  214. package/src/tools/gameShaderForge.js +181 -0
  215. package/src/tools/gameTilemapGenerator.js +164 -0
  216. package/src/tools/gameUiHudBuilder.js +292 -0
  217. package/src/tools/gameVfxEmitterGen.js +108 -0
  218. package/src/tools/gameWebRuntimeInspector.js +91 -0
  219. package/src/tools/generateMediaViaApi.js +358 -0
  220. package/src/tools/gitExec.js +54 -0
  221. package/src/tools/godotAutomator.js +437 -0
  222. package/src/tools/manageLocalAiDependencies.js +220 -0
  223. package/src/tools/memoryWrite.js +99 -0
  224. package/src/tools/multiEdit.js +107 -0
  225. package/src/tools/palmierAutomator.js +104 -0
  226. package/src/tools/pdfReader.js +112 -0
  227. package/src/tools/persistentShell.js +148 -0
  228. package/src/tools/printer.js +316 -0
  229. package/src/tools/procTerm.js +91 -0
  230. package/src/tools/processList.js +52 -0
  231. package/src/tools/readNativeSkill.js +39 -0
  232. package/src/tools/registry.js +277 -0
  233. package/src/tools/resolveAutomator.js +102 -0
  234. package/src/tools/screenshot.js +75 -0
  235. package/src/tools/subagentOrchestrator.js +305 -0
  236. package/src/tools/sysRun.js +125 -0
  237. package/src/tools/todoWrite.js +75 -0
  238. package/src/tools/toolDispatch.js +127 -0
  239. package/src/tools/toolForge.js +124 -0
  240. package/src/tools/updatePlan.js +88 -0
  241. package/src/tools/viewImage.js +82 -0
  242. package/src/tools/visionInspector.js +154 -0
  243. package/src/tools/webFetch.js +74 -0
  244. package/src/tools/webSearch.js +82 -0
  245. package/src/tui/ask.js +164 -0
  246. package/src/tui/banner.js +158 -0
  247. package/src/tui/block.js +152 -0
  248. package/src/tui/chatRenderer.js +953 -0
  249. package/src/tui/completion.js +424 -0
  250. package/src/tui/dialogs.js +606 -0
  251. package/src/tui/diff.js +296 -0
  252. package/src/tui/history.js +135 -0
  253. package/src/tui/home.js +217 -0
  254. package/src/tui/invocation.js +105 -0
  255. package/src/tui/keys.js +443 -0
  256. package/src/tui/lineEditor.js +256 -0
  257. package/src/tui/overlay.js +256 -0
  258. package/src/tui/prompt.js +1258 -0
  259. package/src/tui/selectList.js +335 -0
  260. package/src/tui/stage.js +330 -0
  261. package/src/tui/stageSurface.js +609 -0
  262. package/src/tui/statusLine.js +254 -0
  263. package/src/tui/text.js +345 -0
  264. package/src/tui/theme.js +393 -0
  265. package/src/tui/toolDisplay.js +176 -0
  266. package/src/tui/transcript.js +235 -0
  267. package/src/tui/viewport.js +337 -0
  268. package/src/uncertainty.js +271 -0
  269. package/src/userSkills.js +265 -0
  270. package/src/utils/cli_ui.js +0 -0
  271. package/src/utils/inquirer.js +12 -0
  272. package/src/utils/paths.js +37 -0
  273. package/src/version.js +38 -0
  274. package/src/vfeTracker.js +220 -0
@@ -0,0 +1,408 @@
1
+ import { canonicalModelId } from '../modelIds.js';
2
+ import { DEFAULT_MODEL_BY_PROVIDER } from '../modelCatalogue.js';
3
+ import { LLMProvider } from './base.js';
4
+
5
+ // Models that support server-side tool search (defer_loading + tool_reference
6
+ // expansion). On these, Causyne hands Anthropic the complete 69-tool catalogue
7
+ // and lets the API keep deferred schemas out of the context window entirely —
8
+ // full capability, none of the token cost. Older models fall back to the
9
+ // client-side progressive disclosure in src/toolPacks.js.
10
+ const TOOL_SEARCH_MODELS = [
11
+ 'claude-fable-5',
12
+ 'claude-mythos-5',
13
+ 'claude-opus-5',
14
+ 'claude-opus-4-8',
15
+ 'claude-opus-4-7',
16
+ 'claude-opus-4-6',
17
+ 'claude-sonnet-4-6',
18
+ 'claude-opus-4-5',
19
+ 'claude-sonnet-4-5',
20
+ 'claude-haiku-4-5'
21
+ ];
22
+
23
+ // Sampling parameters (temperature / top_p / top_k) were removed from these
24
+ // models and now return a 400. Sending temperature unconditionally meant every
25
+ // request on the current frontier line-up failed outright — the very models
26
+ // listed in TOOL_SEARCH_MODELS above. Depth is controlled with effort instead.
27
+ const NO_SAMPLING_MODELS = [
28
+ 'claude-fable-5',
29
+ 'claude-mythos-5',
30
+ 'claude-opus-5',
31
+ 'claude-opus-4-8',
32
+ 'claude-opus-4-7',
33
+ 'claude-sonnet-5'
34
+ ];
35
+
36
+ // Models that accept an operator instruction as a `system` entry inside
37
+ // messages[] rather than only in the top-level system block. Keeping a
38
+ // mid-conversation nudge in sequence preserves both its meaning and the cached
39
+ // prefix; hoisting it to the front changed when the model thought it applied.
40
+ const MID_CONVERSATION_SYSTEM_MODELS = [
41
+ 'claude-fable-5',
42
+ 'claude-mythos-5',
43
+ 'claude-opus-5',
44
+ 'claude-opus-4-8'
45
+ ];
46
+
47
+ // Models that take adaptive thinking. On these, `budget_tokens` is REJECTED
48
+ // with a 400 — the fixed-budget parameter many agents still send was removed,
49
+ // and depth is controlled with `output_config.effort` instead.
50
+ //
51
+ // Two of these behave differently on omission, which is the whole reason this
52
+ // list exists: Claude Opus 5 and Fable 5 think by default, but Opus 4.8/4.7,
53
+ // Sonnet 5 and the 4.6 pair run with NO thinking unless `{type:'adaptive'}` is
54
+ // sent explicitly. Causyne was sending nothing, so on those models the single
55
+ // largest lever on frontier-model quality was simply switched off — directly
56
+ // which prevents the configured model from using its intended reasoning mode.
57
+ const ADAPTIVE_THINKING_MODELS = [
58
+ 'claude-fable-5',
59
+ 'claude-mythos-5',
60
+ 'claude-opus-5',
61
+ 'claude-opus-4-8',
62
+ 'claude-opus-4-7',
63
+ 'claude-opus-4-6',
64
+ 'claude-sonnet-5',
65
+ 'claude-sonnet-4-6'
66
+ ];
67
+
68
+ // Models that think by default, so adaptive need not be requested. Sending it
69
+ // anyway is harmless; sending {type:'disabled'} to them is what changes
70
+ // behaviour, and on Fable 5 that is a 400.
71
+ const THINKS_BY_DEFAULT_MODELS = [
72
+ 'claude-fable-5',
73
+ 'claude-mythos-5',
74
+ 'claude-opus-5'
75
+ ];
76
+
77
+ // Models that reject any explicit thinking configuration at all.
78
+ const THINKING_ALWAYS_ON_MODELS = [
79
+ 'claude-fable-5',
80
+ 'claude-mythos-5'
81
+ ];
82
+
83
+ // `output_config.effort`. Opus 4.5 accepts only low/medium/high; the adaptive
84
+ // family adds xhigh and max. Sonnet 4.5 and Haiku 4.5 reject effort outright.
85
+ const EFFORT_MODELS = [...ADAPTIVE_THINKING_MODELS, 'claude-opus-4-5'];
86
+ const LIMITED_EFFORT_MODELS = ['claude-opus-4-5', 'claude-opus-4-6', 'claude-sonnet-4-6'];
87
+ const EFFORT_LEVELS = ['low', 'medium', 'high', 'xhigh', 'max'];
88
+
89
+ function modelMatches(model, list) {
90
+ const normalized = (model || '').toLowerCase();
91
+ return list.some(supported => normalized.startsWith(supported));
92
+ }
93
+
94
+ export class AnthropicProvider extends LLMProvider {
95
+ constructor(config) {
96
+ super(config);
97
+ this.baseUrl = 'https://api.anthropic.com/v1/messages';
98
+ this.model = canonicalModelId(config.model || DEFAULT_MODEL_BY_PROVIDER['anthropic']);
99
+ this.visionModels = ['*'];
100
+ // Anthropic reads images on every model but accepts no audio input at all.
101
+ this.audioModels = [];
102
+ this.imagePayloadFormat = 'anthropic';
103
+ this.enableToolSearch = config.enableToolSearch !== false;
104
+ this.enableCaching = config.enablePromptCaching !== false;
105
+ // Agent tasks routinely run far longer than the 5-minute default cache
106
+ // lifetime. Re-writing a ~5,000-token prefix at 1.25x every time the window
107
+ // lapses costs more than writing it once at 2.0x with a 1-hour lifetime, so
108
+ // the extended TTL is the cheaper default for this workload.
109
+ this.cacheTtl = config.promptCacheTtl || '1h';
110
+
111
+ // 'low' | 'medium' | 'high' | 'xhigh' | 'max' | 'off'. Unset means: let the
112
+ // API pick its own default depth, and still turn thinking ON for the models
113
+ // where omission would silently disable it.
114
+ this.reasoningEffort = config.reasoningEffort || null;
115
+ // Surface the reasoning summary to the user. Off by default — it is extra
116
+ // output tokens on every turn, and this project runs on the user's own key.
117
+ this.showReasoning = config.showReasoning === true;
118
+ // Only consulted for pre-adaptive models.
119
+ this.reasoningBudgetTokens = config.reasoningBudgetTokens || null;
120
+ }
121
+
122
+ cacheControl() {
123
+ return this.cacheTtl === '5m'
124
+ ? { type: 'ephemeral' }
125
+ : { type: 'ephemeral', ttl: this.cacheTtl };
126
+ }
127
+
128
+ supportsToolSearch() {
129
+ if (!this.enableToolSearch) return false;
130
+ return modelMatches(this.model, TOOL_SEARCH_MODELS);
131
+ }
132
+
133
+ /** True when this model rejects temperature / top_p / top_k with a 400. */
134
+ rejectsSamplingParameters() {
135
+ return modelMatches(this.model, NO_SAMPLING_MODELS);
136
+ }
137
+
138
+ /** True when this model takes `thinking: {type:'adaptive'}`. */
139
+ supportsAdaptiveThinking() {
140
+ return modelMatches(this.model, ADAPTIVE_THINKING_MODELS);
141
+ }
142
+
143
+ /**
144
+ * Resolve the configured reasoning effort to something this model accepts.
145
+ * Returns null when effort must not be sent at all.
146
+ */
147
+ resolveEffort() {
148
+ if (!modelMatches(this.model, EFFORT_MODELS)) return null;
149
+ const requested = String(this.reasoningEffort || '').toLowerCase();
150
+ if (!EFFORT_LEVELS.includes(requested)) return null;
151
+ if (modelMatches(this.model, LIMITED_EFFORT_MODELS) && (requested === 'xhigh' || requested === 'max')) {
152
+ // These models stop at 'high'; asking for more is a 400, so clamp rather
153
+ // than fail the whole request over a preference.
154
+ return 'high';
155
+ }
156
+ return requested;
157
+ }
158
+
159
+ /**
160
+ * The `thinking` parameter for this model, or null to omit it.
161
+ *
162
+ * Omission is meaningful and model-dependent, which is why this is a method
163
+ * and not a constant: on Fable 5 any explicit value is a 400; on Opus 5
164
+ * omitting still thinks; on Opus 4.8/4.7 and the Sonnets omitting means no
165
+ * thinking at all.
166
+ */
167
+ buildThinking() {
168
+ if (modelMatches(this.model, THINKING_ALWAYS_ON_MODELS)) return null;
169
+
170
+ if (this.reasoningEffort === 'off') {
171
+ // Only meaningful where thinking would otherwise be on. Note the Opus 5
172
+ // caveat: disabling is accepted only at effort 'high' or below.
173
+ if (!modelMatches(this.model, THINKS_BY_DEFAULT_MODELS)) return null;
174
+ const effort = this.resolveEffort();
175
+ if (effort === 'xhigh' || effort === 'max') return null;
176
+ return { type: 'disabled' };
177
+ }
178
+
179
+ if (this.supportsAdaptiveThinking()) {
180
+ const thinking = { type: 'adaptive' };
181
+
182
+ // long silence instead of the agent's reasoning.
183
+ if (this.showReasoning) thinking.display = 'summarized';
184
+ return thinking;
185
+ }
186
+
187
+ // Pre-adaptive models still use a fixed budget. It must be below max_tokens
188
+ // and at least 1024, or the request errors.
189
+ if (this.reasoningBudgetTokens) {
190
+ return { type: 'enabled', budget_tokens: this.reasoningBudgetTokens };
191
+ }
192
+ return null;
193
+ }
194
+
195
+ /** True when a mid-conversation operator instruction may stay in sequence. */
196
+ supportsMidConversationSystem() {
197
+ return modelMatches(this.model, MID_CONVERSATION_SYSTEM_MODELS);
198
+ }
199
+
200
+ async chat(messages, options = {}) {
201
+ // Anthropic separates the system prompt from the message list.
202
+ const systemBlocks = [];
203
+ const anthropicMessages = [];
204
+
205
+ // Anthropic accepts at most 4 cache_control breakpoints per request. The
206
+ // agent loop anchors up to three stable system blocks and one is reserved
207
+ // for the last tool, so system anchors are capped at 3 here — an extra
208
+ // anchor added upstream can never push the request over the limit (a 400).
209
+ const MAX_SYSTEM_CACHE_BREAKPOINTS = 3;
210
+ let systemCacheBreakpoints = 0;
211
+
212
+ const allowInlineSystem = this.supportsMidConversationSystem();
213
+
214
+ for (const msg of messages) {
215
+ if (msg.role === 'system') {
216
+ // The leading system stack is the prompt itself and always belongs in
217
+ // the top-level `system` field. A system message that arrives *after*
218
+ // the conversation has started is an operator nudge; hoisting it to the
219
+ // front moved it out of the sequence it was meant to apply to. On models
220
+ // that accept it, keep it in place — it must follow a user turn and sit
221
+ // last, which is exactly where the agent loop emits it.
222
+ const isMidConversation = anthropicMessages.length > 0;
223
+ const lastEmitted = anthropicMessages[anthropicMessages.length - 1];
224
+ if (isMidConversation && allowInlineSystem && lastEmitted?.role === 'user') {
225
+ anthropicMessages.push({ role: 'system', content: String(msg.content) });
226
+ continue;
227
+ }
228
+
229
+ const block = { type: 'text', text: String(msg.content) };
230
+ // Stable system blocks are cache anchors; everything volatile is ordered
231
+ // after them by the agent loop, which is what makes the prefix cacheable.
232
+ // Capped so the reserved tool breakpoint always fits within Anthropic's 4.
233
+ if (msg.cacheAnchor && this.enableCaching && systemCacheBreakpoints < MAX_SYSTEM_CACHE_BREAKPOINTS) {
234
+ block.cache_control = this.cacheControl();
235
+ systemCacheBreakpoints++;
236
+ }
237
+ systemBlocks.push(block);
238
+ } else if (msg.role === 'tool') {
239
+ // Translate OpenAI tool response → Anthropic tool_result block
240
+ const toolResultBlock = {
241
+ type: 'tool_result',
242
+ tool_use_id: msg.tool_call_id,
243
+ content: msg.content
244
+ };
245
+ // Anthropic requires consecutive tool_results to be grouped into a single 'user' message
246
+ const lastMsg = anthropicMessages[anthropicMessages.length - 1];
247
+ if (lastMsg && lastMsg.role === 'user' && Array.isArray(lastMsg.content) && lastMsg.content[0]?.type === 'tool_result') {
248
+ lastMsg.content.push(toolResultBlock);
249
+ } else {
250
+ anthropicMessages.push({ role: 'user', content: [toolResultBlock] });
251
+ }
252
+ } else if (msg.role === 'assistant' && msg.tool_calls) {
253
+ // Translate OpenAI assistant tool_calls → Anthropic tool_use content blocks
254
+ const contentBlocks = [];
255
+ // Thinking blocks must be echoed back UNCHANGED and FIRST when the turn
256
+ // they belong to carries tool_use. Dropping them is what breaks a
257
+ // thinking-enabled tool loop; other models ignore them silently, so
258
+ // replaying them is safe in every direction.
259
+ if (Array.isArray(msg.thinking_blocks)) {
260
+ contentBlocks.push(...msg.thinking_blocks);
261
+ }
262
+ if (msg.content) {
263
+ contentBlocks.push({ type: 'text', text: msg.content });
264
+ }
265
+ for (const tc of msg.tool_calls) {
266
+ contentBlocks.push({
267
+ type: 'tool_use',
268
+ id: tc.id,
269
+ name: tc.function.name,
270
+ input: typeof tc.function.arguments === 'string' ? JSON.parse(tc.function.arguments || '{}') : (tc.function.arguments || {})
271
+ });
272
+ }
273
+ anthropicMessages.push({ role: 'assistant', content: contentBlocks });
274
+ } else {
275
+ // These are the loop's own bookkeeping. The API rejects unknown message
276
+ // properties, so every one of them has to be stripped here.
277
+ const { cacheAnchor, thinking_blocks, reasoning_content, ...clean } = msg;
278
+ anthropicMessages.push(clean);
279
+ }
280
+ }
281
+
282
+ const payload = {
283
+ model: this.model,
284
+ system: systemBlocks,
285
+ messages: anthropicMessages,
286
+ // 4096 truncated long tool-heavy turns mid-thought and forced a retry.
287
+ max_tokens: options.maxTokens || 16000
288
+ };
289
+
290
+ if (!this.rejectsSamplingParameters()) {
291
+ payload.temperature = options.temperature || 0.1;
292
+ }
293
+
294
+ const thinking = this.buildThinking();
295
+ if (thinking) payload.thinking = thinking;
296
+
297
+ const effort = this.resolveEffort();
298
+ // effort lives inside output_config, not at the top level.
299
+ if (effort) payload.output_config = { ...(payload.output_config || {}), effort };
300
+
301
+ if (options.tools && options.tools.length > 0) {
302
+ const useToolSearch = this.supportsToolSearch();
303
+ const coreToolCount = Number.isInteger(options.coreToolCount) ? options.coreToolCount : options.tools.length;
304
+
305
+ const converted = options.tools.map((t, index) => {
306
+ const toolObj = t.function ? t.function : t;
307
+ const tool = {
308
+ name: toolObj.name,
309
+ description: toolObj.description || '',
310
+ input_schema: toolObj.parameters || toolObj.input_schema || { type: 'object', properties: {} }
311
+ };
312
+ // Beyond the core toolset, let the API hold schemas server-side and
313
+ // surface them only when Claude searches for them.
314
+ if (useToolSearch && index >= coreToolCount) {
315
+ tool.defer_loading = true;
316
+ }
317
+ return tool;
318
+ });
319
+
320
+ if (useToolSearch) {
321
+ payload.tools = [
322
+ { type: 'tool_search_tool_regex_20251119', name: 'tool_search_tool_regex' },
323
+ ...converted
324
+ ];
325
+ } else {
326
+ payload.tools = converted;
327
+ }
328
+
329
+ // Cache breakpoint on the last non-deferred tool. A deferred tool cannot
330
+ // also carry cache_control — the API rejects that combination.
331
+ if (this.enableCaching) {
332
+ const anchorIndex = useToolSearch
333
+ ? Math.min(coreToolCount, payload.tools.length - 1)
334
+ : payload.tools.length - 1;
335
+ const anchor = payload.tools[anchorIndex];
336
+ if (anchor && !anchor.defer_loading && !anchor.type) {
337
+ anchor.cache_control = this.cacheControl();
338
+ }
339
+ }
340
+ }
341
+
342
+ const response = await fetch(this.baseUrl, {
343
+ method: 'POST',
344
+ headers: {
345
+ 'Content-Type': 'application/json',
346
+ 'x-api-key': this.apiKey,
347
+ 'anthropic-version': '2023-06-01'
348
+ },
349
+ body: JSON.stringify(payload)
350
+ });
351
+
352
+ if (!response.ok) {
353
+ const errorData = await response.text();
354
+ throw new Error(`Anthropic API Error (${response.status}): ${errorData}`);
355
+ }
356
+
357
+ const data = await response.json();
358
+
359
+ // Extract text content and tool calls
360
+ let content = '';
361
+ let toolCalls = [];
362
+ const thinkingBlocks = [];
363
+ let reasoningContent = '';
364
+
365
+ for (const block of data.content) {
366
+ if (block.type === 'thinking' || block.type === 'redacted_thinking') {
367
+ // Kept verbatim for the echo above. The readable summary, when the user
368
+ // asked for one, is surfaced separately.
369
+ thinkingBlocks.push(block);
370
+ if (typeof block.thinking === 'string') reasoningContent += block.thinking;
371
+ } else if (block.type === 'text') {
372
+ content += block.text;
373
+ } else if (block.type === 'tool_use') {
374
+ toolCalls.push({
375
+ id: block.id,
376
+ type: 'function',
377
+ function: {
378
+ name: block.name,
379
+ arguments: JSON.stringify(block.input)
380
+ }
381
+ });
382
+ }
383
+ // server_tool_use and tool_search_tool_result blocks are handled entirely
384
+ // by the API: the discovered tools are expanded before Claude sees them,
385
+ // and we must never return a tool_result for a srvtoolu_ id.
386
+ }
387
+
388
+ return {
389
+ content: content.trim(),
390
+ toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
391
+ thinkingBlocks: thinkingBlocks.length > 0 ? thinkingBlocks : undefined,
392
+ reasoningContent: reasoningContent.trim() || undefined,
393
+ usage: {
394
+ // Cache reads are billed at 0.1x and cache writes at 1.25x, so they are
395
+ // reported separately for the token tracker to price correctly.
396
+ promptTokens:
397
+ (data.usage?.input_tokens || 0) +
398
+ (data.usage?.cache_read_input_tokens || 0) +
399
+ (data.usage?.cache_creation_input_tokens || 0),
400
+ completionTokens: data.usage?.output_tokens || 0,
401
+ cachedPromptTokens: data.usage?.cache_read_input_tokens || 0,
402
+ // A 1-hour cache write is billed at 2.0x, a 5-minute one at 1.25x.
403
+ cacheWriteMultiplier: this.cacheTtl === '5m' ? 1.25 : 2.0,
404
+ cacheWriteTokens: data.usage?.cache_creation_input_tokens || 0
405
+ }
406
+ };
407
+ }
408
+ }