@deepstrike/sdk 0.2.52 → 0.2.61
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/README.md +28 -28
- package/dist/agent-ir.d.ts +103 -0
- package/dist/agent-ir.js +134 -0
- package/dist/agent.d.ts +67 -0
- package/dist/agent.js +36 -0
- package/dist/collaboration/harness.js +1 -1
- package/dist/collaboration/modes/creator-verifier.d.ts +2 -7
- package/dist/collaboration/modes/creator-verifier.js +4 -6
- package/dist/collaboration/pool.d.ts +8 -20
- package/dist/collaboration/pool.js +27 -97
- package/dist/compat/anthropic/mcp.d.ts +15 -0
- package/dist/compat/anthropic/mcp.js +10 -0
- package/dist/compat/openai/agent.d.ts +34 -0
- package/dist/compat/openai/agent.js +24 -0
- package/dist/governance.d.ts +1 -17
- package/dist/governance.js +1 -34
- package/dist/guardrail.d.ts +6 -0
- package/dist/guardrail.js +1 -0
- package/dist/handoff-target.d.ts +12 -0
- package/dist/handoff-target.js +1 -0
- package/dist/harness/manifest.js +0 -4
- package/dist/index.d.ts +19 -7
- package/dist/index.js +8 -5
- package/dist/kernel.d.ts +2 -20
- package/dist/knowledge/public.d.ts +29 -0
- package/dist/knowledge/public.js +1 -0
- package/dist/mcp-server.d.ts +28 -0
- package/dist/mcp-server.js +1 -0
- package/dist/memory/agent.d.ts +2 -2
- package/dist/memory/agent.js +2 -2
- package/dist/memory/durable.d.ts +16 -0
- package/dist/memory/durable.js +46 -0
- package/dist/memory/in-memory-store.d.ts +9 -7
- package/dist/memory/in-memory-store.js +8 -2
- package/dist/memory/protocols.d.ts +22 -4
- package/dist/memory/public.d.ts +4 -3
- package/dist/memory/public.js +3 -2
- package/dist/os/public.d.ts +1 -1
- package/dist/os/public.js +1 -1
- package/dist/providers/anthropic-adapter.d.ts +59 -0
- package/dist/providers/anthropic-adapter.js +530 -0
- package/dist/providers/anthropic-compatible.d.ts +2 -3
- package/dist/providers/anthropic-compatible.js +8 -5
- package/dist/providers/anthropic.d.ts +20 -23
- package/dist/providers/anthropic.js +176 -395
- package/dist/providers/base.d.ts +2 -2
- package/dist/providers/base.js +50 -8
- package/dist/providers/capability-router.d.ts +29 -0
- package/dist/providers/capability-router.js +43 -0
- package/dist/providers/catalog.d.ts +16 -4
- package/dist/providers/catalog.js +112 -36
- package/dist/providers/content-normalization.d.ts +57 -0
- package/dist/providers/content-normalization.js +238 -0
- package/dist/providers/content-policy.d.ts +16 -0
- package/dist/providers/content-policy.js +39 -0
- package/dist/providers/credentials.d.ts +83 -0
- package/dist/providers/credentials.js +190 -0
- package/dist/providers/endpoints.d.ts +137 -0
- package/dist/providers/endpoints.js +128 -0
- package/dist/providers/factories.js +25 -9
- package/dist/providers/gemini-adapter.d.ts +33 -0
- package/dist/providers/gemini-adapter.js +264 -0
- package/dist/providers/gemini.d.ts +16 -3
- package/dist/providers/gemini.js +97 -195
- package/dist/providers/model-catalog.d.ts +37 -0
- package/dist/providers/model-catalog.js +62 -0
- package/dist/providers/model-registry.d.ts +119 -0
- package/dist/providers/model-registry.js +379 -0
- package/dist/providers/ollama-adapter.d.ts +65 -0
- package/dist/providers/ollama-adapter.js +188 -0
- package/dist/providers/ollama.d.ts +9 -4
- package/dist/providers/ollama.js +96 -109
- package/dist/providers/openai-chat-dialects.d.ts +154 -0
- package/dist/providers/openai-chat-dialects.js +179 -0
- package/dist/providers/openai-chat.d.ts +46 -18
- package/dist/providers/openai-chat.js +416 -51
- package/dist/providers/openai-responses-adapter.d.ts +42 -0
- package/dist/providers/openai-responses-adapter.js +343 -0
- package/dist/providers/openai-responses.d.ts +19 -33
- package/dist/providers/openai-responses.js +164 -264
- package/dist/providers/openai.d.ts +29 -76
- package/dist/providers/openai.js +195 -292
- package/dist/providers/protocol-adapter.d.ts +39 -0
- package/dist/providers/protocol-adapter.js +13 -0
- package/dist/providers/protocol-capabilities.d.ts +34 -0
- package/dist/providers/protocol-capabilities.js +44 -0
- package/dist/providers/provider-error.d.ts +31 -0
- package/dist/providers/provider-error.js +153 -0
- package/dist/providers/public.d.ts +26 -3
- package/dist/providers/public.js +13 -1
- package/dist/providers/registry.d.ts +7 -6
- package/dist/providers/registry.js +47 -20
- package/dist/providers/request-plan.d.ts +89 -0
- package/dist/providers/request-plan.js +199 -0
- package/dist/providers/usage-normalizer.d.ts +48 -0
- package/dist/providers/usage-normalizer.js +139 -0
- package/dist/providers/vendor-profiles.d.ts +2 -15
- package/dist/providers/vendor-profiles.js +14 -60
- package/dist/runtime/canonical-kernel-step.d.ts +1 -2
- package/dist/runtime/canonical-kernel-step.js +47 -12
- package/dist/runtime/context-policy.d.ts +10 -12
- package/dist/runtime/context-policy.js +6 -8
- package/dist/runtime/durable-content.d.ts +50 -0
- package/dist/runtime/durable-content.js +159 -0
- package/dist/runtime/execution-plane.d.ts +2 -2
- package/dist/runtime/execution-plane.js +2 -2
- package/dist/runtime/kernel-event-log.js +0 -1
- package/dist/runtime/kernel-step.d.ts +0 -1
- package/dist/runtime/kernel-step.js +4 -2
- package/dist/runtime/mcp-proxy-plane.d.ts +25 -1
- package/dist/runtime/mcp-proxy-plane.js +44 -6
- package/dist/runtime/output-schema.d.ts +1 -2
- package/dist/runtime/provider-replay.d.ts +5 -1
- package/dist/runtime/provider-replay.js +26 -27
- package/dist/runtime/reactive-session.d.ts +1 -1
- package/dist/runtime/reactive-session.js +2 -3
- package/dist/runtime/run-group.d.ts +1 -1
- package/dist/runtime/runner.d.ts +31 -45
- package/dist/runtime/runner.js +178 -63
- package/dist/runtime/session-log.d.ts +8 -1
- package/dist/runtime/session-log.js +42 -2
- package/dist/runtime/session-repair.d.ts +1 -1
- package/dist/runtime/session-repair.js +1 -1
- package/dist/runtime/sub-agent-orchestrator.d.ts +1 -1
- package/dist/runtime/sub-agent-orchestrator.js +8 -11
- package/dist/runtime/workflow-control-flow.d.ts +0 -4
- package/dist/runtime/workflow-control-flow.js +0 -16
- package/dist/session.d.ts +11 -0
- package/dist/session.js +1 -0
- package/dist/skill.d.ts +17 -0
- package/dist/skill.js +16 -0
- package/dist/skills/loader.d.ts +3 -0
- package/dist/tools/errors.d.ts +1 -3
- package/dist/tools/errors.js +1 -3
- package/dist/tools/index.d.ts +3 -0
- package/dist/types/agent.d.ts +21 -9
- package/dist/types/agent.js +30 -4
- package/dist/types.d.ts +135 -17
- package/package.json +4 -4
- package/dist/providers/deepseek.d.ts +0 -46
- package/dist/providers/deepseek.js +0 -97
- package/dist/providers/glm.d.ts +0 -25
- package/dist/providers/glm.js +0 -48
- package/dist/providers/kimi.d.ts +0 -23
- package/dist/providers/kimi.js +0 -30
- package/dist/providers/minimax.d.ts +0 -49
- package/dist/providers/minimax.js +0 -98
- package/dist/providers/profiles.d.ts +0 -1992
- package/dist/providers/profiles.js +0 -796
- package/dist/providers/qwen.d.ts +0 -38
- package/dist/providers/qwen.js +0 -97
|
@@ -1,30 +1,35 @@
|
|
|
1
1
|
import Anthropic from "@anthropic-ai/sdk";
|
|
2
2
|
import { assistantReplayKey } from "../runtime/provider-replay.js";
|
|
3
3
|
import { withServerRuntimeGuard } from "../runtime/server.js";
|
|
4
|
-
import { CircuitBreaker
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"claude-opus-4-0": { maxTurns: 50 },
|
|
10
|
-
"claude-sonnet-4-6": { maxTurns: 25 },
|
|
11
|
-
"claude-sonnet-4-0": { maxTurns: 25 },
|
|
12
|
-
"claude-haiku-4-5": { maxTurns: 15 },
|
|
13
|
-
"claude-3-5-haiku-latest": { maxTurns: 15 },
|
|
14
|
-
};
|
|
4
|
+
import { CircuitBreaker } from "./base.js";
|
|
5
|
+
import { AnthropicMessagesAdapter, } from "./anthropic-adapter.js";
|
|
6
|
+
import { normalizeCanonicalAdapterInput, } from "./content-normalization.js";
|
|
7
|
+
import { endpointProfiles } from "./endpoints.js";
|
|
8
|
+
import { circuitOpenError, classifyProviderError } from "./provider-error.js";
|
|
15
9
|
export class AnthropicProvider {
|
|
16
10
|
client;
|
|
17
11
|
circuit;
|
|
18
12
|
maxRetries;
|
|
19
13
|
baseDelay;
|
|
20
14
|
model;
|
|
15
|
+
adapter = new AnthropicMessagesAdapter();
|
|
21
16
|
nativeAssistantBlocks = new Map();
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
17
|
+
resolvedRuntimePolicy;
|
|
18
|
+
directNativeTokenCounting;
|
|
19
|
+
defaultTextualToolCallPolicy;
|
|
20
|
+
resolvedRuntime;
|
|
21
|
+
constructor(config) {
|
|
22
|
+
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
23
|
+
throw new TypeError("AnthropicProvider requires a configuration object");
|
|
24
|
+
}
|
|
25
|
+
if (typeof config.apiKey !== "string" || config.apiKey.length === 0) {
|
|
26
|
+
throw new TypeError("AnthropicProvider requires a non-empty apiKey");
|
|
27
|
+
}
|
|
28
|
+
const c = {
|
|
29
|
+
model: "claude-sonnet-4-6",
|
|
30
|
+
retry: { maxRetries: 3, baseDelay: 1000 },
|
|
31
|
+
...config,
|
|
32
|
+
};
|
|
28
33
|
this.model = c.model ?? "claude-sonnet-4-6";
|
|
29
34
|
this.client = withServerRuntimeGuard(() => new Anthropic({
|
|
30
35
|
...(c.authMode === "bearer"
|
|
@@ -35,11 +40,17 @@ export class AnthropicProvider {
|
|
|
35
40
|
this.circuit = new CircuitBreaker();
|
|
36
41
|
this.maxRetries = c.retry?.maxRetries ?? 3;
|
|
37
42
|
this.baseDelay = c.retry?.baseDelay ?? 1000;
|
|
43
|
+
this.resolvedRuntimePolicy = c.runtimePolicy ?? {};
|
|
44
|
+
const configuredBaseURL = c.baseURL?.replace(/\/+$/, "");
|
|
45
|
+
const officialBaseURL = endpointProfiles["anthropic.messages"].baseURL.replace(/\/+$/, "");
|
|
46
|
+
this.directNativeTokenCounting = configuredBaseURL === undefined
|
|
47
|
+
|| configuredBaseURL === officialBaseURL;
|
|
48
|
+
this.defaultTextualToolCallPolicy = this.directNativeTokenCounting ? "off" : "reject";
|
|
38
49
|
}
|
|
39
50
|
runtimePolicy() {
|
|
40
|
-
return
|
|
51
|
+
return this.resolvedRuntimePolicy;
|
|
41
52
|
}
|
|
42
|
-
/** Identity advertised in the descriptor; overridden by Anthropic-compatible vendors
|
|
53
|
+
/** Identity advertised in the descriptor; overridden by Anthropic-compatible vendors. */
|
|
43
54
|
providerName() {
|
|
44
55
|
return "anthropic";
|
|
45
56
|
}
|
|
@@ -59,419 +70,189 @@ export class AnthropicProvider {
|
|
|
59
70
|
},
|
|
60
71
|
};
|
|
61
72
|
}
|
|
73
|
+
bindResolvedRuntime(resolved) {
|
|
74
|
+
if (resolved.identity.protocol !== "anthropic-messages"
|
|
75
|
+
|| resolved.identity.providerId !== this.providerName()
|
|
76
|
+
|| resolved.identity.modelId !== this.model) {
|
|
77
|
+
throw new Error("AnthropicProvider received a mismatched resolved runtime");
|
|
78
|
+
}
|
|
79
|
+
this.resolvedRuntime = resolved;
|
|
80
|
+
}
|
|
62
81
|
peekProviderReplay(message) {
|
|
63
82
|
const blocks = this.nativeAssistantBlocks.get(assistantReplayKey(message));
|
|
64
|
-
return blocks?.length ? { native_blocks: blocks } : undefined;
|
|
83
|
+
return blocks?.length ? { protocol: "anthropic-messages", native_blocks: blocks } : undefined;
|
|
65
84
|
}
|
|
66
85
|
seedProviderReplay(message, replay) {
|
|
67
|
-
if (replay.native_blocks?.length) {
|
|
86
|
+
if (replay.protocol === "anthropic-messages" && replay.native_blocks?.length) {
|
|
68
87
|
this.nativeAssistantBlocks.set(assistantReplayKey(message), replay.native_blocks);
|
|
69
|
-
return;
|
|
70
88
|
}
|
|
71
|
-
// Legacy log without persisted native blocks: reconstruct neutral
|
|
72
|
-
// text + tool_use blocks from the transcript so a tool-use turn can be
|
|
73
|
-
// replayed. Thinking blocks were never persisted, so they are not recovered.
|
|
74
|
-
const blocks = reconstructAnthropicBlocks(message);
|
|
75
|
-
if (blocks.length)
|
|
76
|
-
this.nativeAssistantBlocks.set(assistantReplayKey(message), blocks);
|
|
77
89
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
90
|
+
adapterInput(context, tools, extensions) {
|
|
91
|
+
const providerId = this.providerName();
|
|
92
|
+
const endpoint = Object.values(endpointProfiles).find(profile => profile.providerId === providerId && profile.protocol === "anthropic-messages") ?? endpointProfiles["anthropic.messages"];
|
|
93
|
+
const resolved = this.resolvedRuntime ?? {
|
|
94
|
+
identity: {
|
|
95
|
+
providerId,
|
|
96
|
+
modelId: this.model,
|
|
97
|
+
endpointId: endpoint.id,
|
|
98
|
+
protocol: "anthropic-messages",
|
|
99
|
+
},
|
|
100
|
+
model: {
|
|
101
|
+
id: `${providerId}/${this.model}`,
|
|
102
|
+
providerId,
|
|
103
|
+
kind: "generation",
|
|
104
|
+
intrinsic: {},
|
|
105
|
+
},
|
|
106
|
+
endpoint,
|
|
107
|
+
adapter: this,
|
|
108
|
+
effectiveCapabilities: compatibilityCapabilities(),
|
|
109
|
+
};
|
|
110
|
+
return normalizeCanonicalAdapterInput({
|
|
111
|
+
context,
|
|
112
|
+
tools,
|
|
113
|
+
resolved,
|
|
114
|
+
extensions: {
|
|
115
|
+
textualToolCallPolicy: this.defaultTextualToolCallPolicy,
|
|
116
|
+
...extensions,
|
|
117
|
+
},
|
|
118
|
+
replayForMessage: message => this.peekProviderReplay(message),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
buildPlan(context, tools, extensions) {
|
|
122
|
+
const input = this.adapterInput(context, tools, extensions);
|
|
123
|
+
return { input, plan: this.adapter.buildRequest(input) };
|
|
96
124
|
}
|
|
97
125
|
async complete(context, tools, extensions) {
|
|
126
|
+
const provider = this.providerName();
|
|
98
127
|
if (this.circuit.isOpen())
|
|
99
|
-
throw
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
128
|
+
throw circuitOpenError(provider);
|
|
129
|
+
let input;
|
|
130
|
+
let plan;
|
|
131
|
+
try {
|
|
132
|
+
;
|
|
133
|
+
({ input, plan } = this.buildPlan(context, tools, extensions));
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
throw classifyProviderError(provider, error);
|
|
137
|
+
}
|
|
105
138
|
let lastErr;
|
|
106
|
-
for (let
|
|
139
|
+
for (let attempt = 0; attempt < this.maxRetries; attempt++) {
|
|
107
140
|
try {
|
|
108
|
-
const
|
|
109
|
-
...requestExtensions,
|
|
110
|
-
model: this.model,
|
|
111
|
-
max_tokens: typeof extensions?.max_tokens === "number" ? extensions.max_tokens : 8096,
|
|
112
|
-
...(system ? { system } : {}),
|
|
113
|
-
messages: msgs,
|
|
114
|
-
...(tools.length ? { tools: this.buildTools(tools, !Array.isArray(system), strategy) } : {}),
|
|
115
|
-
}, extensions);
|
|
141
|
+
const raw = await this.createMessage(plan.params, plan.transport);
|
|
116
142
|
this.circuit.recordSuccess();
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (block.type === "text")
|
|
121
|
-
content += block.text;
|
|
122
|
-
else if (block.type === "tool_use") {
|
|
123
|
-
const tc = normalizeToolCall(block.id, block.name, block.input);
|
|
124
|
-
if (tc)
|
|
125
|
-
toolCalls.push(tc);
|
|
126
|
-
}
|
|
143
|
+
const decoded = this.adapter.decodeComplete(raw, { input });
|
|
144
|
+
if (decoded.replay?.native_blocks) {
|
|
145
|
+
this.rememberNativeBlocks(decoded.message, decoded.replay.native_blocks);
|
|
127
146
|
}
|
|
128
|
-
|
|
129
|
-
this.rememberNativeBlocks(message, resp.content);
|
|
130
|
-
return message;
|
|
147
|
+
return decoded.message;
|
|
131
148
|
}
|
|
132
|
-
catch (
|
|
133
|
-
lastErr =
|
|
149
|
+
catch (error) {
|
|
150
|
+
lastErr = error;
|
|
134
151
|
this.circuit.recordFailure();
|
|
135
|
-
if (
|
|
136
|
-
await new Promise(
|
|
152
|
+
if (attempt < this.maxRetries - 1) {
|
|
153
|
+
await new Promise(resolve => setTimeout(resolve, this.baseDelay * 2 ** attempt));
|
|
154
|
+
}
|
|
137
155
|
}
|
|
138
156
|
}
|
|
139
|
-
throw lastErr;
|
|
157
|
+
throw classifyProviderError(provider, lastErr);
|
|
140
158
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
const nativeBlocks = {};
|
|
154
|
-
let finalText = "";
|
|
155
|
-
const finalToolCalls = [];
|
|
156
|
-
const stream = this.streamMessage({
|
|
157
|
-
...requestExtensions,
|
|
158
|
-
model: this.model,
|
|
159
|
-
max_tokens: typeof extensions?.max_tokens === "number" ? extensions.max_tokens : 8096,
|
|
159
|
+
/** Native measurement belongs to the verified official endpoint, not the wire protocol. */
|
|
160
|
+
async countTokens(context, tools, extensions) {
|
|
161
|
+
const enabled = this.resolvedRuntime
|
|
162
|
+
? this.resolvedRuntime.effectiveCapabilities.nativeTokenCounting.state === "supported"
|
|
163
|
+
: this.providerName() === "anthropic" && this.directNativeTokenCounting;
|
|
164
|
+
if (!enabled) {
|
|
165
|
+
throw new Error(`Native token counting is unavailable on ${this.providerName()} Anthropic-compatible endpoint`);
|
|
166
|
+
}
|
|
167
|
+
const { plan } = this.buildPlan(context, tools, extensions);
|
|
168
|
+
const { model, system, messages, tools: requestTools } = plan.params;
|
|
169
|
+
const response = await this.client.messages.countTokens({
|
|
170
|
+
model,
|
|
160
171
|
...(system ? { system } : {}),
|
|
161
|
-
messages
|
|
162
|
-
...(
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
outputTokens = Math.max(outputTokens, usage.output_tokens ?? 0);
|
|
179
|
-
// inputTokens is the FULL prompt size (uncached + cache read + cache
|
|
180
|
-
// write). The kernel reads it as the authoritative prompt size for
|
|
181
|
-
// context-pressure/compaction — excluding cached tokens would make a
|
|
182
|
-
// cache-heavy turn look tiny and suppress compaction until a 413.
|
|
183
|
-
const inputTokens = uncachedInput + cacheReadTokens + cacheCreationTokens;
|
|
184
|
-
const bySlot = estimateCacheReadBySlot(cacheReadTokens, slotBp);
|
|
185
|
-
// stop_reason is only present on message_delta (the closing frame). `max_tokens` drives
|
|
186
|
-
// the kernel's output-cap recovery; other reasons (end_turn/tool_use) are informational.
|
|
187
|
-
const stopReason = evt.delta?.stop_reason;
|
|
188
|
-
yield {
|
|
189
|
-
type: "usage",
|
|
190
|
-
totalTokens: inputTokens + outputTokens,
|
|
191
|
-
inputTokens,
|
|
192
|
-
outputTokens,
|
|
193
|
-
cacheReadInputTokens: cacheReadTokens,
|
|
194
|
-
cacheCreationInputTokens: cacheCreationTokens,
|
|
195
|
-
...(bySlot ? { cacheReadInputTokensBySlot: bySlot } : {}),
|
|
196
|
-
...(stopReason ? { stopReason } : {}),
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
else if (evt.type === "content_block_start") {
|
|
201
|
-
nativeBlocks[evt.index] = { ...evt.content_block };
|
|
202
|
-
if (evt.content_block.type === "tool_use") {
|
|
203
|
-
toolBlocks[evt.index] = { id: evt.content_block.id, name: evt.content_block.name, argsBuf: "" };
|
|
204
|
-
}
|
|
172
|
+
messages,
|
|
173
|
+
...(requestTools ? { tools: requestTools } : {}),
|
|
174
|
+
});
|
|
175
|
+
return {
|
|
176
|
+
inputTokens: response.input_tokens,
|
|
177
|
+
source: { kind: "native", provider: "anthropic" },
|
|
178
|
+
confidence: "exact",
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
async *stream(context, tools, extensions, _state, signal) {
|
|
182
|
+
const provider = this.providerName();
|
|
183
|
+
try {
|
|
184
|
+
const { input, plan } = this.buildPlan(context, tools, extensions);
|
|
185
|
+
const state = this.adapter.createStreamState({ input });
|
|
186
|
+
for await (const chunk of this.streamMessage(plan.params, plan.transport, signal)) {
|
|
187
|
+
for (const event of this.adapter.pushStreamChunk(chunk, state).events)
|
|
188
|
+
yield event;
|
|
205
189
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
yield { type: "text_delta", delta: d.text };
|
|
212
|
-
}
|
|
213
|
-
else if (d.type === "thinking_delta") {
|
|
214
|
-
nativeBlocks[evt.index] = { ...nativeBlocks[evt.index], thinking: String(nativeBlocks[evt.index]?.thinking ?? "") + d.thinking };
|
|
215
|
-
yield { type: "thinking_delta", delta: d.thinking };
|
|
216
|
-
}
|
|
217
|
-
else if (d.type === "signature_delta") {
|
|
218
|
-
nativeBlocks[evt.index] = { ...nativeBlocks[evt.index], signature: String(nativeBlocks[evt.index]?.signature ?? "") + d.signature };
|
|
219
|
-
}
|
|
220
|
-
else if (d.type === "input_json_delta" && toolBlocks[evt.index]) {
|
|
221
|
-
toolBlocks[evt.index].argsBuf += d.partial_json;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
else if (evt.type === "content_block_stop" && toolBlocks[evt.index] !== undefined) {
|
|
225
|
-
const tb = toolBlocks[evt.index];
|
|
226
|
-
delete toolBlocks[evt.index];
|
|
227
|
-
let args = {};
|
|
228
|
-
try {
|
|
229
|
-
args = JSON.parse(tb.argsBuf || "{}");
|
|
230
|
-
}
|
|
231
|
-
catch {
|
|
232
|
-
args = {};
|
|
233
|
-
}
|
|
234
|
-
nativeBlocks[evt.index] = { ...nativeBlocks[evt.index], input: args };
|
|
235
|
-
finalToolCalls.push({ id: tb.id, name: tb.name, arguments: JSON.stringify(args) });
|
|
236
|
-
yield { type: "tool_call", id: tb.id, name: tb.name, arguments: args };
|
|
190
|
+
const final = this.adapter.finishStream(state);
|
|
191
|
+
for (const event of final.events)
|
|
192
|
+
yield event;
|
|
193
|
+
if (final.replay?.native_blocks) {
|
|
194
|
+
this.rememberNativeBlocks({ content: state.finalText, toolCalls: state.finalToolCalls }, final.replay.native_blocks);
|
|
237
195
|
}
|
|
238
196
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
return omitExtensionKeys(extensions, ["model", "messages", "system", "tools", "max_tokens", "stream"]);
|
|
243
|
-
}
|
|
244
|
-
hasBetas(extensions) {
|
|
245
|
-
const betas = extensions?.betas;
|
|
246
|
-
return Array.isArray(betas) && betas.length > 0;
|
|
197
|
+
catch (error) {
|
|
198
|
+
throw classifyProviderError(provider, error);
|
|
199
|
+
}
|
|
247
200
|
}
|
|
248
|
-
createMessage(params,
|
|
249
|
-
return
|
|
201
|
+
createMessage(params, transport) {
|
|
202
|
+
return (transport === "beta"
|
|
250
203
|
? this.client.beta.messages.create(params)
|
|
251
|
-
: this.client.messages.create(params);
|
|
204
|
+
: this.client.messages.create(params));
|
|
252
205
|
}
|
|
253
|
-
streamMessage(params,
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
: this.client.messages.stream(params, opts));
|
|
206
|
+
streamMessage(params, transport, signal) {
|
|
207
|
+
const options = signal ? { signal } : undefined;
|
|
208
|
+
return (transport === "beta"
|
|
209
|
+
? this.client.beta.messages.stream(params, options)
|
|
210
|
+
: this.client.messages.stream(params, options));
|
|
259
211
|
}
|
|
212
|
+
// White-box test seams. Request construction itself belongs to the adapter.
|
|
260
213
|
buildSystem(context, strategy) {
|
|
261
|
-
|
|
262
|
-
// (stable only), or 2 blocks (stable + knowledge). The first turn `systemKnowledge`
|
|
263
|
-
// appears, the block count rises 1→2, which is a one-time prompt-cache invalidation
|
|
264
|
-
// (the knowledge prefix didn't exist to cache before). It is byte-stable thereafter;
|
|
265
|
-
// dynamic per-turn knowledge belongs in the uncached tail, not this block. An empty
|
|
266
|
-
// knowledge string is intentionally never emitted (the API rejects empty text blocks).
|
|
267
|
-
if (!context.systemStable && !context.systemKnowledge) {
|
|
268
|
-
return context.systemText || undefined;
|
|
269
|
-
}
|
|
270
|
-
// System cache_control is emitted under "default" and "system-only". Other strategies
|
|
271
|
-
// keep the text-block structure for protocol parity but omit cache_control.
|
|
272
|
-
const emitOnSystemBlocks = strategy === "default" || strategy === "system-only";
|
|
273
|
-
const cc = { type: "ephemeral" };
|
|
274
|
-
const blocks = [];
|
|
275
|
-
if (context.systemStable) {
|
|
276
|
-
blocks.push({ type: "text", text: context.systemStable, ...(emitOnSystemBlocks ? { cache_control: cc } : {}) });
|
|
277
|
-
}
|
|
278
|
-
if (context.systemKnowledge) {
|
|
279
|
-
blocks.push({ type: "text", text: context.systemKnowledge, ...(emitOnSystemBlocks ? { cache_control: cc } : {}) });
|
|
280
|
-
}
|
|
281
|
-
return blocks.length ? blocks : undefined;
|
|
214
|
+
return this.buildPlan(context, [], { cacheBreakpointStrategy: strategy }).plan.params.system;
|
|
282
215
|
}
|
|
283
216
|
buildMessages(context, strategy) {
|
|
284
|
-
|
|
285
|
-
// Cache breakpoints anchor on the stable history; the volatile State turn is
|
|
286
|
-
// appended AFTER them as the uncached tail (so the history prefix re-reads
|
|
287
|
-
// across turns). On un-rebuilt bindings stateTurn is absent and the state is
|
|
288
|
-
// already inside `turns` — rendered as-is above. `frozenPrefixLen` (P1-E) pins
|
|
289
|
-
// the deep breakpoint at the compaction boundary; absent ⇒ rolling-pair fallback.
|
|
290
|
-
applyMessageCacheControl(msgs, context.frozenPrefixLen, strategy);
|
|
291
|
-
if (context.stateTurn) {
|
|
292
|
-
// Render through toAnthropicMessages so assistant tool_use blocks and
|
|
293
|
-
// tool-role tool_result parts are serialized correctly — toAnthropicContent
|
|
294
|
-
// only handles contentParts/content and would silently drop toolCalls.
|
|
295
|
-
const stateMsgs = toAnthropicMessages([context.stateTurn], message => this.nativeAssistantBlocks.get(assistantReplayKey(message)));
|
|
296
|
-
msgs.push(...stateMsgs);
|
|
297
|
-
}
|
|
298
|
-
if (msgs.length === 0) {
|
|
299
|
-
msgs.push({ role: "user", content: "Proceed." });
|
|
300
|
-
}
|
|
301
|
-
return msgs;
|
|
217
|
+
return this.buildPlan(context, [], { cacheBreakpointStrategy: strategy }).plan.params.messages;
|
|
302
218
|
}
|
|
303
219
|
rememberNativeBlocks(message, blocks) {
|
|
304
220
|
if (!blocks.length)
|
|
305
221
|
return;
|
|
306
|
-
if (!message.toolCalls?.length && !blocks.some(
|
|
222
|
+
if (!message.toolCalls?.length && !blocks.some(block => block.type === "thinking"))
|
|
307
223
|
return;
|
|
308
224
|
this.nativeAssistantBlocks.set(assistantReplayKey(message), blocks);
|
|
309
225
|
}
|
|
310
226
|
}
|
|
311
|
-
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
return { system: sysBp, tools: toolBp, messages: msgBp };
|
|
342
|
-
}
|
|
343
|
-
/**
|
|
344
|
-
* I1: split the response's `cache_read_input_tokens` evenly across the slots that carried a
|
|
345
|
-
* cache_control breakpoint on the request. Returns undefined when there's no cache read or no
|
|
346
|
-
* contributing slot — in those cases the consumer is better off seeing the field absent than
|
|
347
|
-
* seeing all zeros. The remainder (if the total doesn't divide evenly) lands on the first
|
|
348
|
-
* contributing slot to keep the sum exact.
|
|
349
|
-
*/
|
|
350
|
-
function estimateCacheReadBySlot(cacheRead, slotBp) {
|
|
351
|
-
if (cacheRead <= 0)
|
|
352
|
-
return undefined;
|
|
353
|
-
const count = (slotBp.system ? 1 : 0) + (slotBp.tools ? 1 : 0) + (slotBp.messages ? 1 : 0);
|
|
354
|
-
if (count === 0)
|
|
355
|
-
return undefined;
|
|
356
|
-
const share = Math.floor(cacheRead / count);
|
|
357
|
-
const remainder = cacheRead - share * count;
|
|
358
|
-
const out = {};
|
|
359
|
-
let firstDone = false;
|
|
360
|
-
const give = () => {
|
|
361
|
-
if (!firstDone) {
|
|
362
|
-
firstDone = true;
|
|
363
|
-
return share + remainder;
|
|
364
|
-
}
|
|
365
|
-
return share;
|
|
227
|
+
function compatibilityCapabilities() {
|
|
228
|
+
const unknown = { state: "unknown", evidence: [] };
|
|
229
|
+
const unsupported = { state: "unsupported", evidence: ["protocol"] };
|
|
230
|
+
return {
|
|
231
|
+
inputModalities: {
|
|
232
|
+
text: unknown,
|
|
233
|
+
image: unknown,
|
|
234
|
+
audio: unsupported,
|
|
235
|
+
video: unsupported,
|
|
236
|
+
file: unsupported,
|
|
237
|
+
},
|
|
238
|
+
outputModalities: {
|
|
239
|
+
text: unknown,
|
|
240
|
+
image: unsupported,
|
|
241
|
+
audio: unsupported,
|
|
242
|
+
embedding: unsupported,
|
|
243
|
+
},
|
|
244
|
+
tools: unknown,
|
|
245
|
+
reasoning: unknown,
|
|
246
|
+
parallelToolCalls: unknown,
|
|
247
|
+
structuredOutput: unsupported,
|
|
248
|
+
promptCaching: unknown,
|
|
249
|
+
nativeTokenCounting: unknown,
|
|
250
|
+
mediaForms: {
|
|
251
|
+
imageUrl: unknown,
|
|
252
|
+
imageBase64: unknown,
|
|
253
|
+
fileId: unsupported,
|
|
254
|
+
audioUrl: unsupported,
|
|
255
|
+
audioBase64: unsupported,
|
|
256
|
+
},
|
|
366
257
|
};
|
|
367
|
-
if (slotBp.system)
|
|
368
|
-
out.system = give();
|
|
369
|
-
if (slotBp.tools)
|
|
370
|
-
out.tools = give();
|
|
371
|
-
if (slotBp.messages)
|
|
372
|
-
out.messages = give();
|
|
373
|
-
return out;
|
|
374
|
-
}
|
|
375
|
-
/** Anthropic accepts at most this many cache_control breakpoints per request. */
|
|
376
|
-
const MAX_CACHE_BREAKPOINTS = 4;
|
|
377
|
-
/**
|
|
378
|
-
* Number of rolling cache breakpoints to spend on the message history. Anthropic
|
|
379
|
-
* allows 4 cache_control breakpoints total; the static system/tools prefix
|
|
380
|
-
* consumes up to 2 (systemStable + systemKnowledge), leaving 2 for the history.
|
|
381
|
-
*/
|
|
382
|
-
const MESSAGE_CACHE_BREAKPOINTS = 2;
|
|
383
|
-
/**
|
|
384
|
-
* Regression guard: fail loudly if the static (system + tools) breakpoints plus
|
|
385
|
-
* the rolling message budget could exceed Anthropic's hard limit, instead of
|
|
386
|
-
* letting the API reject the request with an opaque 400. Uses the worst-case
|
|
387
|
-
* message count (`MESSAGE_CACHE_BREAKPOINTS`), so it can only fire if a future
|
|
388
|
-
* change adds a system partition or raises the message budget.
|
|
389
|
-
*/
|
|
390
|
-
function assertCacheBudget(system, toolCount) {
|
|
391
|
-
const systemBreakpoints = Array.isArray(system) ? system.length : 0;
|
|
392
|
-
const toolBreakpoints = toolCount > 0 && !Array.isArray(system) ? 1 : 0;
|
|
393
|
-
const worstCase = systemBreakpoints + toolBreakpoints + MESSAGE_CACHE_BREAKPOINTS;
|
|
394
|
-
if (worstCase > MAX_CACHE_BREAKPOINTS) {
|
|
395
|
-
throw new Error(`Anthropic cache_control budget exceeded: ${systemBreakpoints} system + ${toolBreakpoints} tool + ${MESSAGE_CACHE_BREAKPOINTS} message > ${MAX_CACHE_BREAKPOINTS}`);
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
/**
|
|
399
|
-
* Place the (≤2) message-history cache breakpoints. The final message always gets
|
|
400
|
-
* one — it writes the current full prefix for the next turn to read. The second is
|
|
401
|
-
* placed by one of two strategies:
|
|
402
|
-
*
|
|
403
|
-
* • **Deep anchor (P1-E)** — when `frozenPrefixLen` marks a distinct frozen prefix
|
|
404
|
-
* (the compaction boundary), pin the second breakpoint there. It is byte-stable
|
|
405
|
-
* across turns, so `[0..frozen]` is re-read cheaply every turn and is immune to
|
|
406
|
-
* the 20-block lookback miss that strikes heavy tool turns (>20 blocks/turn); the
|
|
407
|
-
* tail breakpoint then writes only the incremental `[frozen..tail]`.
|
|
408
|
-
* • **Rolling fallback** — otherwise (older binding / no compaction yet / whole
|
|
409
|
-
* render hot), roll the second breakpoint to the nearest preceding user turn, the
|
|
410
|
-
* previous turn's read anchor (Anthropic's 20-block lookback bridges light turns).
|
|
411
|
-
*
|
|
412
|
-
* Without any of this the cached prefix stops at the end of `system` and every turn
|
|
413
|
-
* re-bills the entire tool-result history at full price (~quadratic cumulative cost).
|
|
414
|
-
* cache_control attaches to the last content block of each target, promoting a bare
|
|
415
|
-
* string body to a text block.
|
|
416
|
-
*/
|
|
417
|
-
function applyMessageCacheControl(msgs, frozenPrefixLen, strategy) {
|
|
418
|
-
if (!msgs.length)
|
|
419
|
-
return;
|
|
420
|
-
// Message-level cache_control is emitted under "default" and "frozen-prefix" only.
|
|
421
|
-
// "tools-only", "system-only", and "none" skip the history entirely.
|
|
422
|
-
if (strategy === "tools-only" || strategy === "system-only" || strategy === "none")
|
|
423
|
-
return;
|
|
424
|
-
const targets = new Set([msgs.length - 1]);
|
|
425
|
-
if (typeof frozenPrefixLen === "number" && frozenPrefixLen >= 1 && frozenPrefixLen < msgs.length) {
|
|
426
|
-
// Deep anchor at the frozen-prefix boundary (last frozen turn). Fixed between compactions.
|
|
427
|
-
targets.add(frozenPrefixLen - 1);
|
|
428
|
-
}
|
|
429
|
-
else if (strategy === "default") {
|
|
430
|
-
// Rolling fallback is part of the default strategy only — `"frozen-prefix"` deliberately
|
|
431
|
-
// skips it so a verify can isolate the deep-anchor contribution from the rolling pair.
|
|
432
|
-
for (let i = msgs.length - 2; i >= 0 && targets.size < MESSAGE_CACHE_BREAKPOINTS; i--) {
|
|
433
|
-
if (msgs[i].role === "user")
|
|
434
|
-
targets.add(i);
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
for (const idx of targets)
|
|
438
|
-
markLastBlockCacheable(msgs[idx]);
|
|
439
|
-
}
|
|
440
|
-
/** Attach an ephemeral cache breakpoint to a message's final content block. */
|
|
441
|
-
function markLastBlockCacheable(msg) {
|
|
442
|
-
const cache_control = { type: "ephemeral" };
|
|
443
|
-
if (typeof msg.content === "string") {
|
|
444
|
-
if (!msg.content)
|
|
445
|
-
return; // don't synthesize an empty (API-rejected) text block
|
|
446
|
-
msg.content = [{ type: "text", text: msg.content, cache_control }];
|
|
447
|
-
return;
|
|
448
|
-
}
|
|
449
|
-
if (Array.isArray(msg.content) && msg.content.length) {
|
|
450
|
-
const last = msg.content[msg.content.length - 1];
|
|
451
|
-
last.cache_control = cache_control;
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
/**
|
|
455
|
-
* Reconstruct Anthropic assistant content blocks from a neutral transcript when
|
|
456
|
-
* no provider replay was persisted. Only meaningful for tool-use turns: a plain
|
|
457
|
-
* text turn needs no native blocks to replay.
|
|
458
|
-
*/
|
|
459
|
-
function reconstructAnthropicBlocks(message) {
|
|
460
|
-
const toolCalls = message.toolCalls ?? [];
|
|
461
|
-
if (!toolCalls.length)
|
|
462
|
-
return [];
|
|
463
|
-
const blocks = [];
|
|
464
|
-
if (message.content)
|
|
465
|
-
blocks.push({ type: "text", text: message.content });
|
|
466
|
-
for (const tc of toolCalls) {
|
|
467
|
-
let input = {};
|
|
468
|
-
try {
|
|
469
|
-
input = JSON.parse(tc.arguments || "{}");
|
|
470
|
-
}
|
|
471
|
-
catch {
|
|
472
|
-
input = {};
|
|
473
|
-
}
|
|
474
|
-
blocks.push({ type: "tool_use", id: tc.id, name: tc.name, input });
|
|
475
|
-
}
|
|
476
|
-
return blocks;
|
|
477
258
|
}
|