@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.
- package/LICENSE +102 -0
- package/README.md +31 -2
- package/SECURITY.md +24 -0
- package/bin/causyne.js +13 -0
- package/docs/ARCHITECTURE.md +63 -0
- package/docs/PROVENANCE.md +29 -0
- package/docs/SECURITY_REVIEW.md +89 -0
- package/docs/THIRD_PARTY_NOTICES.md +49 -0
- package/native-skills/ai-engineering/context-window-management/SKILL.md +43 -0
- package/native-skills/ai-engineering/llm-evaluation/SKILL.md +46 -0
- package/native-skills/ai-engineering/model-cost-and-latency/SKILL.md +46 -0
- package/native-skills/ai-engineering/prompt-construction/SKILL.md +42 -0
- package/native-skills/ai-engineering/retrieval-augmented-generation/SKILL.md +42 -0
- package/native-skills/ai-engineering/structured-output-and-schemas/SKILL.md +45 -0
- package/native-skills/ai-engineering/tool-calling-agents/SKILL.md +43 -0
- package/native-skills/android/app-performance-profiling/SKILL.md +42 -0
- package/native-skills/android/background-work-scheduling/SKILL.md +47 -0
- package/native-skills/android/compose-state-and-recomposition/SKILL.md +43 -0
- package/native-skills/android/gradle-build-configuration/SKILL.md +41 -0
- package/native-skills/android/lifecycle-and-leaks/SKILL.md +46 -0
- package/native-skills/android/play-store-release/SKILL.md +43 -0
- package/native-skills/android/room-and-persistence/SKILL.md +40 -0
- package/native-skills/backend/authentication-and-sessions/SKILL.md +45 -0
- package/native-skills/backend/background-jobs-and-queues/SKILL.md +42 -0
- package/native-skills/backend/caching-strategy/SKILL.md +44 -0
- package/native-skills/backend/graceful-shutdown/SKILL.md +42 -0
- package/native-skills/backend/rate-limiting-and-quotas/SKILL.md +40 -0
- package/native-skills/backend/rest-api-design/SKILL.md +40 -0
- package/native-skills/backend/webhook-delivery-and-receipt/SKILL.md +43 -0
- package/native-skills/cloud/cost-optimisation/SKILL.md +46 -0
- package/native-skills/cloud/iam-least-privilege/SKILL.md +43 -0
- package/native-skills/cloud/infrastructure-as-code/SKILL.md +43 -0
- package/native-skills/cloud/multi-region-and-failover/SKILL.md +43 -0
- package/native-skills/cloud/serverless-patterns/SKILL.md +47 -0
- package/native-skills/data/data-quality-monitoring/SKILL.md +51 -0
- package/native-skills/data/etl-pipeline-reliability/SKILL.md +43 -0
- package/native-skills/data/exploratory-analysis/SKILL.md +43 -0
- package/native-skills/data/metric-definition/SKILL.md +42 -0
- package/native-skills/data/notebook-to-production/SKILL.md +43 -0
- package/native-skills/database/backup-and-recovery/SKILL.md +43 -0
- package/native-skills/database/connection-pooling/SKILL.md +42 -0
- package/native-skills/database/data-integrity-and-constraints/SKILL.md +42 -0
- package/native-skills/database/query-optimisation/SKILL.md +38 -0
- package/native-skills/database/safe-migrations/SKILL.md +47 -0
- package/native-skills/database/schema-design/SKILL.md +38 -0
- package/native-skills/database/transactions-and-isolation/SKILL.md +41 -0
- package/native-skills/design/colour-system-construction/SKILL.md +38 -0
- package/native-skills/design/design-direction-brief/SKILL.md +41 -0
- package/native-skills/design/motion-and-transitions/SKILL.md +38 -0
- package/native-skills/design/responsive-strategy/SKILL.md +39 -0
- package/native-skills/design/spacing-and-rhythm/SKILL.md +38 -0
- package/native-skills/design/type-scale-and-hierarchy/SKILL.md +39 -0
- package/native-skills/design/visual-qa-loop/SKILL.md +42 -0
- package/native-skills/devops/docker-multi-stage-builds/SKILL.md +60 -0
- package/native-skills/devops/github-actions-ci/SKILL.md +60 -0
- package/native-skills/devops/incident-triage/SKILL.md +38 -0
- package/native-skills/devops/kubernetes-deployment-hardening/SKILL.md +64 -0
- package/native-skills/devops/observability-baseline/SKILL.md +38 -0
- package/native-skills/devops/secret-management/SKILL.md +40 -0
- package/native-skills/devops/terraform-state-and-modules/SKILL.md +36 -0
- package/native-skills/devops/zero-downtime-deploys/SKILL.md +40 -0
- package/native-skills/git/bisect-regression-hunt/SKILL.md +43 -0
- package/native-skills/git/branch-strategy/SKILL.md +39 -0
- package/native-skills/git/commit-hygiene/SKILL.md +41 -0
- package/native-skills/git/history-recovery/SKILL.md +44 -0
- package/native-skills/git/large-repository-surgery/SKILL.md +46 -0
- package/native-skills/git/merge-conflict-resolution/SKILL.md +42 -0
- package/native-skills/media/cinematic-3d-shot/SKILL.md +104 -0
- package/native-skills/mobile/app-startup-optimisation/SKILL.md +41 -0
- package/native-skills/mobile/cross-platform-tradeoffs/SKILL.md +42 -0
- package/native-skills/mobile/deep-linking/SKILL.md +39 -0
- package/native-skills/mobile/mobile-storage-security/SKILL.md +45 -0
- package/native-skills/mobile/offline-first-sync/SKILL.md +42 -0
- package/native-skills/mobile/push-notifications/SKILL.md +38 -0
- package/native-skills/performance/algorithmic-complexity/SKILL.md +43 -0
- package/native-skills/performance/concurrency-and-parallelism/SKILL.md +42 -0
- package/native-skills/performance/load-testing/SKILL.md +42 -0
- package/native-skills/performance/memory-and-leak-hunting/SKILL.md +41 -0
- package/native-skills/performance/network-and-payload-efficiency/SKILL.md +42 -0
- package/native-skills/performance/profiling-methodology/SKILL.md +46 -0
- package/native-skills/python/async-correctness/SKILL.md +40 -0
- package/native-skills/python/cli-tool-construction/SKILL.md +44 -0
- package/native-skills/python/data-processing-pipelines/SKILL.md +38 -0
- package/native-skills/python/environment-isolation/SKILL.md +41 -0
- package/native-skills/python/packaging-and-distribution/SKILL.md +56 -0
- package/native-skills/python/pytest-suite-design/SKILL.md +39 -0
- package/native-skills/python/type-hints-and-static-checking/SKILL.md +42 -0
- package/native-skills/security/authorization-model/SKILL.md +42 -0
- package/native-skills/security/crypto-usage/SKILL.md +48 -0
- package/native-skills/security/dependency-supply-chain/SKILL.md +42 -0
- package/native-skills/security/input-validation-and-injection/SKILL.md +44 -0
- package/native-skills/security/secure-file-uploads/SKILL.md +46 -0
- package/native-skills/security/security-headers-and-csp/SKILL.md +44 -0
- package/native-skills/security/threat-modelling/SKILL.md +46 -0
- package/native-skills/testing/end-to-end-test-stability/SKILL.md +42 -0
- package/native-skills/testing/property-based-testing/SKILL.md +46 -0
- package/native-skills/testing/regression-test-from-a-bug/SKILL.md +41 -0
- package/native-skills/testing/snapshot-and-visual-regression/SKILL.md +42 -0
- package/native-skills/testing/test-doubles-and-mocking/SKILL.md +43 -0
- package/native-skills/testing/test-strategy-and-pyramid/SKILL.md +39 -0
- package/native-skills/testing/writing-good-assertions/SKILL.md +42 -0
- package/native-skills/web/accessible-components/SKILL.md +45 -0
- package/native-skills/web/api-client-resilience/SKILL.md +39 -0
- package/native-skills/web/bundle-size-reduction/SKILL.md +38 -0
- package/native-skills/web/core-web-vitals/SKILL.md +40 -0
- package/native-skills/web/css-layout-systems/SKILL.md +41 -0
- package/native-skills/web/form-validation-ux/SKILL.md +39 -0
- package/native-skills/web/react-state-architecture/SKILL.md +41 -0
- package/native-skills/web/ssr-hydration-correctness/SKILL.md +45 -0
- package/package.json +68 -4
- package/src/agentLoop.js +2458 -0
- package/src/appChannel.js +109 -0
- package/src/approvalGrants.js +66 -0
- package/src/auditLog.js +52 -0
- package/src/autoUpdate.js +12 -0
- package/src/capabilities.js +36 -0
- package/src/checkpoints.js +1199 -0
- package/src/cliMode.js +638 -0
- package/src/commands.js +805 -0
- package/src/compiledSkills.js +540 -0
- package/src/config.js +130 -0
- package/src/contextManager.js +498 -0
- package/src/cortex/autoCondenser.js +192 -0
- package/src/cortex/provisioner.js +27 -0
- package/src/cortex/vectorDb.js +447 -0
- package/src/designPalettes.js +99 -0
- package/src/devMode.js +6 -0
- package/src/embodimentSession.js +180 -0
- package/src/entrypoint.js +194 -0
- package/src/execSafe.js +21 -0
- package/src/gitSnapshot.js +378 -0
- package/src/mcp/jsonRpc.js +165 -0
- package/src/mcp/mcpClient.js +136 -0
- package/src/mcp/mcpRegistry.js +207 -0
- package/src/memory.js +62 -0
- package/src/memoryManager.js +124 -0
- package/src/modelCatalogue.js +132 -0
- package/src/modelIds.js +42 -0
- package/src/modelRouter.js +145 -0
- package/src/outputStore.js +246 -0
- package/src/perception.js +124 -0
- package/src/planCache.js +437 -0
- package/src/procSignal.js +32 -0
- package/src/providers/anthropic.js +408 -0
- package/src/providers/base.js +328 -0
- package/src/providers/dashscope-byok.js +86 -0
- package/src/providers/deepseek.js +80 -0
- package/src/providers/factory.js +37 -0
- package/src/providers/gemini.js +346 -0
- package/src/providers/moonshot.js +73 -0
- package/src/providers/muse.js +169 -0
- package/src/providers/ollama.js +104 -0
- package/src/providers/openai-compatible.js +84 -0
- package/src/providers/openai.js +20 -0
- package/src/providers/qwen-byok.js +76 -0
- package/src/providers/xai.js +73 -0
- package/src/provisioning/capabilityConsent.js +24 -0
- package/src/provisioning/capabilityProvisioner.js +271 -0
- package/src/provisioning/capabilityTools.js +29 -0
- package/src/reporting.js +373 -0
- package/src/sandbox.js +529 -0
- package/src/sessionStore.js +279 -0
- package/src/skillsManager.js +288 -0
- package/src/slopDetector.js +307 -0
- package/src/taskBuffer.js +45 -0
- package/src/taskShape.js +241 -0
- package/src/textDiff.js +150 -0
- package/src/tokenTracker.js +560 -0
- package/src/tool-scripts/ax_snapshot.applescript +39 -0
- package/src/tool-scripts/ax_snapshot.ps1 +10 -0
- package/src/tool-scripts/desktop_act.applescript +58 -0
- package/src/tool-scripts/resolve_automator.py +119 -0
- package/src/toolPacks.js +524 -0
- package/src/tools/archive.js +63 -0
- package/src/tools/astryxOracle.js +78 -0
- package/src/tools/axSnapshot.js +165 -0
- package/src/tools/blenderAutomator.js +349 -0
- package/src/tools/browserAutomator.js +252 -0
- package/src/tools/checkSystemSpecs.js +64 -0
- package/src/tools/clipboard.js +36 -0
- package/src/tools/codeSearchAst.js +101 -0
- package/src/tools/codeSearcher.js +230 -0
- package/src/tools/codeTools.js +262 -0
- package/src/tools/codebaseSearch.js +75 -0
- package/src/tools/createSkill.js +129 -0
- package/src/tools/deferredMcpSearch.js +246 -0
- package/src/tools/desktopAct.js +208 -0
- package/src/tools/desktopOpen.js +34 -0
- package/src/tools/dynamicLoader.js +44 -0
- package/src/tools/executeProceduralMedia.js +93 -0
- package/src/tools/fsDelete.js +51 -0
- package/src/tools/fsEdit.js +105 -0
- package/src/tools/fsFind.js +128 -0
- package/src/tools/fsList.js +77 -0
- package/src/tools/fsMove.js +57 -0
- package/src/tools/fsRead.js +87 -0
- package/src/tools/fsSearch.js +198 -0
- package/src/tools/fsWrite.js +41 -0
- package/src/tools/gameAiBehaviorTree.js +208 -0
- package/src/tools/gameAnimationRig.js +194 -0
- package/src/tools/gameAssetFetcher.js +372 -0
- package/src/tools/gameAssetHarvester.js +210 -0
- package/src/tools/gameAudioSynth.js +267 -0
- package/src/tools/gameBuilder.js +886 -0
- package/src/tools/gameCanvasAudit.js +101 -0
- package/src/tools/gameCinematicSequencer.js +151 -0
- package/src/tools/gameDataForge.js +124 -0
- package/src/tools/gameDialogueTreeGen.js +166 -0
- package/src/tools/gameEconomyBalancer.js +161 -0
- package/src/tools/gameEntitySpawner.js +207 -0
- package/src/tools/gameLoreForge.js +153 -0
- package/src/tools/gameQuestArcBuilder.js +152 -0
- package/src/tools/gameSceneBuilder.js +158 -0
- package/src/tools/gameShaderForge.js +181 -0
- package/src/tools/gameTilemapGenerator.js +164 -0
- package/src/tools/gameUiHudBuilder.js +292 -0
- package/src/tools/gameVfxEmitterGen.js +108 -0
- package/src/tools/gameWebRuntimeInspector.js +91 -0
- package/src/tools/generateMediaViaApi.js +358 -0
- package/src/tools/gitExec.js +54 -0
- package/src/tools/godotAutomator.js +437 -0
- package/src/tools/manageLocalAiDependencies.js +220 -0
- package/src/tools/memoryWrite.js +99 -0
- package/src/tools/multiEdit.js +107 -0
- package/src/tools/palmierAutomator.js +104 -0
- package/src/tools/pdfReader.js +112 -0
- package/src/tools/persistentShell.js +148 -0
- package/src/tools/printer.js +316 -0
- package/src/tools/procTerm.js +91 -0
- package/src/tools/processList.js +52 -0
- package/src/tools/readNativeSkill.js +39 -0
- package/src/tools/registry.js +277 -0
- package/src/tools/resolveAutomator.js +102 -0
- package/src/tools/screenshot.js +75 -0
- package/src/tools/subagentOrchestrator.js +305 -0
- package/src/tools/sysRun.js +125 -0
- package/src/tools/todoWrite.js +75 -0
- package/src/tools/toolDispatch.js +127 -0
- package/src/tools/toolForge.js +124 -0
- package/src/tools/updatePlan.js +88 -0
- package/src/tools/viewImage.js +82 -0
- package/src/tools/visionInspector.js +154 -0
- package/src/tools/webFetch.js +74 -0
- package/src/tools/webSearch.js +82 -0
- package/src/tui/ask.js +164 -0
- package/src/tui/banner.js +158 -0
- package/src/tui/block.js +152 -0
- package/src/tui/chatRenderer.js +953 -0
- package/src/tui/completion.js +424 -0
- package/src/tui/dialogs.js +606 -0
- package/src/tui/diff.js +296 -0
- package/src/tui/history.js +135 -0
- package/src/tui/home.js +217 -0
- package/src/tui/invocation.js +105 -0
- package/src/tui/keys.js +443 -0
- package/src/tui/lineEditor.js +256 -0
- package/src/tui/overlay.js +256 -0
- package/src/tui/prompt.js +1258 -0
- package/src/tui/selectList.js +335 -0
- package/src/tui/stage.js +330 -0
- package/src/tui/stageSurface.js +609 -0
- package/src/tui/statusLine.js +254 -0
- package/src/tui/text.js +345 -0
- package/src/tui/theme.js +393 -0
- package/src/tui/toolDisplay.js +176 -0
- package/src/tui/transcript.js +235 -0
- package/src/tui/viewport.js +337 -0
- package/src/uncertainty.js +271 -0
- package/src/userSkills.js +265 -0
- package/src/utils/cli_ui.js +0 -0
- package/src/utils/inquirer.js +12 -0
- package/src/utils/paths.js +37 -0
- package/src/version.js +38 -0
- 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
|
+
}
|