@deepstrike/sdk 0.2.20 → 0.2.22
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/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/memory/in-memory-store.d.ts +37 -0
- package/dist/memory/in-memory-store.js +50 -0
- package/dist/providers/anthropic.js +43 -15
- package/dist/types.d.ts +28 -0
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -60,6 +60,7 @@ export type { RegisteredTool } from "./tools/index.js";
|
|
|
60
60
|
export { scanSkillDir, readSkillFile } from "./skills/loader.js";
|
|
61
61
|
export type { SkillMetadata } from "./skills/loader.js";
|
|
62
62
|
export { WorkingMemory } from "./memory/working.js";
|
|
63
|
+
export { InMemoryDreamStore } from "./memory/in-memory-store.js";
|
|
63
64
|
export type { DreamStore, DreamResult, SessionData, SessionMessage, MemoryEntry, CurationResult, CurationStats, MemoryWriteRequest, MemoryQuery, MemoryRetrieval, MemoryMetadata, MemoryKind, } from "./memory/protocols.js";
|
|
64
65
|
export type { KnowledgeSource } from "./knowledge/source.js";
|
|
65
66
|
export { ScheduledPrompt } from "./signals/scheduled.js";
|
|
@@ -71,7 +72,7 @@ export { Governance, governancePolicyToKernelEvent } from "./governance.js";
|
|
|
71
72
|
export type { GovernanceVerdict, GovernancePolicy, GovernanceConstraint } from "./governance.js";
|
|
72
73
|
export { SinglePassHarness, EvalLoopHarness, HarnessLoop } from "./harness/harness.js";
|
|
73
74
|
export type { HarnessRequest, HarnessOutcome, HarnessLoopOptions, QualityGate } from "./harness/harness.js";
|
|
74
|
-
export type { Message, ToolCall, ToolResult, ToolSchema, ContentPart, TextPart, ImagePart, AudioPart, StreamEvent, TextDelta, ThinkingDelta, ToolCallEvent, ToolChunk, ToolDeltaEvent, ToolSuspendEvent, ToolResultEvent, DoneEvent, ErrorEvent, PermissionRequestEvent, PermissionResolvedEvent, PermissionResponse, LLMProvider, RetryConfig, TokenUsage, ProviderToolSpec, ProviderRunState, ProviderReplay, RenderedContext, ReplayabilityAssessment, } from "./types.js";
|
|
75
|
+
export type { Message, ToolCall, ToolResult, ToolSchema, ContentPart, TextPart, ImagePart, AudioPart, StreamEvent, TextDelta, ThinkingDelta, ToolCallEvent, ToolChunk, ToolDeltaEvent, ToolSuspendEvent, ToolResultEvent, DoneEvent, ErrorEvent, PermissionRequestEvent, PermissionResolvedEvent, PermissionResponse, LLMProvider, RetryConfig, TokenUsage, ProviderToolSpec, ProviderRunState, ProviderReplay, RenderedContext, ReplayabilityAssessment, CacheBreakpointStrategy, } from "./types.js";
|
|
75
76
|
export type { AgentCapabilityFilter, AgentIdentity, AgentIsolation, AgentRunSpec, AgentProcessChangedObservation, ContextInheritance, KernelAgentRole, LoopResult, MilestoneCheckResult, MilestoneContract, MilestonePhase, MilestonePolicy, SubAgentResult, TerminationReason, WorkflowSpec, WorkflowNodeSpec, WorkflowTaskSpec, WorkflowSpawnInfo, } from "./types/agent.js";
|
|
76
77
|
export { agentIdentitySub, agentRunSpecToKernel, milestoneCheckFail, milestoneCheckPass, milestoneCheckResultToKernel, subAgentResultToKernel, workflowSpecToKernel, workflowNodeSpecToKernel, submitWorkflowNodesToKernel, submitWorkflowToKernel, submitWorkflowNodesTool, startWorkflowTool, fanoutSynthesize, generateAndFilter, verifyRules, genEval, } from "./types/agent.js";
|
|
77
78
|
export type { AcceptanceCriterion, VerificationContract, ContractCheckResult, } from "./collaboration/contract.js";
|
package/dist/index.js
CHANGED
|
@@ -42,6 +42,7 @@ export { tool, streamingTool, executeTools, readFile, validateToolArguments } fr
|
|
|
42
42
|
export { scanSkillDir, readSkillFile } from "./skills/loader.js";
|
|
43
43
|
// ── Memory ─────────────────────────────────────────────────────────────────
|
|
44
44
|
export { WorkingMemory } from "./memory/working.js";
|
|
45
|
+
export { InMemoryDreamStore } from "./memory/in-memory-store.js";
|
|
45
46
|
export { ScheduledPrompt } from "./signals/scheduled.js";
|
|
46
47
|
export { SignalGateway } from "./signals/gateway.js";
|
|
47
48
|
// ── Safety & Governance ────────────────────────────────────────────────────
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `InMemoryDreamStore` — a lightweight `DreamStore` implementation backed by per-agent `Map`s.
|
|
3
|
+
*
|
|
4
|
+
* Originally lived as `MockDreamStore` in the SDK's test helpers; promoted here so benchmarks,
|
|
5
|
+
* examples, and downstream consumers can use it without copying the boilerplate.
|
|
6
|
+
*
|
|
7
|
+
* Use cases:
|
|
8
|
+
* - Benchmark A/B variants where memory is on/off (preload via constructor).
|
|
9
|
+
* - Unit tests that exercise `Agent.dream()` or the `memory_query` path without disk I/O.
|
|
10
|
+
* - Local development / CI where a persistent memory store isn't needed.
|
|
11
|
+
*
|
|
12
|
+
* The `search()` impl is intentionally trivial — it returns the first `topK` memories for the
|
|
13
|
+
* agent regardless of `query`. The kernel ranks by score before deciding what to surface, so the
|
|
14
|
+
* order memories were inserted is what callers see. For semantic search, plug in a real store.
|
|
15
|
+
*/
|
|
16
|
+
import type { CurationResult, DreamStore, MemoryEntry, SessionData } from "./protocols.js";
|
|
17
|
+
export declare class InMemoryDreamStore implements DreamStore {
|
|
18
|
+
private readonly initialMemories;
|
|
19
|
+
private sessions;
|
|
20
|
+
private memories;
|
|
21
|
+
/** Sessions persisted via `saveSession`; exposed for test assertions. */
|
|
22
|
+
readonly savedSessions: SessionData[];
|
|
23
|
+
/**
|
|
24
|
+
* @param initialMemories Optional seed memories applied to every agent that asks for memories
|
|
25
|
+
* for the first time. Useful for benchmark scenarios that preload a fact.
|
|
26
|
+
*/
|
|
27
|
+
constructor(initialMemories?: MemoryEntry[]);
|
|
28
|
+
/** Pre-populate sessions for a specific agent (test/benchmark setup). */
|
|
29
|
+
addSession(agentId: string, session: SessionData): void;
|
|
30
|
+
/** Pre-populate memories for a specific agent (test/benchmark setup). */
|
|
31
|
+
addMemories(agentId: string, entries: MemoryEntry[]): void;
|
|
32
|
+
loadSessions(agentId: string): Promise<SessionData[]>;
|
|
33
|
+
loadMemories(agentId: string): Promise<MemoryEntry[]>;
|
|
34
|
+
commit(agentId: string, result: CurationResult, existing: MemoryEntry[]): Promise<void>;
|
|
35
|
+
search(agentId: string, _query: string, topK?: number): Promise<MemoryEntry[]>;
|
|
36
|
+
saveSession(data: SessionData): Promise<void>;
|
|
37
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export class InMemoryDreamStore {
|
|
2
|
+
initialMemories;
|
|
3
|
+
sessions = new Map();
|
|
4
|
+
memories = new Map();
|
|
5
|
+
/** Sessions persisted via `saveSession`; exposed for test assertions. */
|
|
6
|
+
savedSessions = [];
|
|
7
|
+
/**
|
|
8
|
+
* @param initialMemories Optional seed memories applied to every agent that asks for memories
|
|
9
|
+
* for the first time. Useful for benchmark scenarios that preload a fact.
|
|
10
|
+
*/
|
|
11
|
+
constructor(initialMemories = []) {
|
|
12
|
+
this.initialMemories = initialMemories;
|
|
13
|
+
}
|
|
14
|
+
/** Pre-populate sessions for a specific agent (test/benchmark setup). */
|
|
15
|
+
addSession(agentId, session) {
|
|
16
|
+
const list = this.sessions.get(agentId) ?? [];
|
|
17
|
+
list.push(session);
|
|
18
|
+
this.sessions.set(agentId, list);
|
|
19
|
+
}
|
|
20
|
+
/** Pre-populate memories for a specific agent (test/benchmark setup). */
|
|
21
|
+
addMemories(agentId, entries) {
|
|
22
|
+
this.memories.set(agentId, [...(this.memories.get(agentId) ?? []), ...entries]);
|
|
23
|
+
}
|
|
24
|
+
async loadSessions(agentId) {
|
|
25
|
+
return this.sessions.get(agentId) ?? [];
|
|
26
|
+
}
|
|
27
|
+
async loadMemories(agentId) {
|
|
28
|
+
if (this.memories.has(agentId))
|
|
29
|
+
return this.memories.get(agentId);
|
|
30
|
+
if (this.initialMemories.length > 0) {
|
|
31
|
+
this.memories.set(agentId, [...this.initialMemories]);
|
|
32
|
+
return this.memories.get(agentId);
|
|
33
|
+
}
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
async commit(agentId, result, existing) {
|
|
37
|
+
const kept = existing.filter((_, i) => !result.toRemoveIndices.includes(i));
|
|
38
|
+
this.memories.set(agentId, [...kept, ...result.toAdd]);
|
|
39
|
+
}
|
|
40
|
+
async search(agentId, _query, topK = 5) {
|
|
41
|
+
const all = await this.loadMemories(agentId);
|
|
42
|
+
return all.slice(0, topK);
|
|
43
|
+
}
|
|
44
|
+
async saveSession(data) {
|
|
45
|
+
this.savedSessions.push(data);
|
|
46
|
+
const list = this.sessions.get(data.agentId) ?? [];
|
|
47
|
+
list.push(data);
|
|
48
|
+
this.sessions.set(data.agentId, list);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -77,19 +77,24 @@ export class AnthropicProvider {
|
|
|
77
77
|
* (tools render before system), so a redundant tool breakpoint would only burn
|
|
78
78
|
* one of Anthropic's 4 cache_control slots — slots the message history needs.
|
|
79
79
|
*/
|
|
80
|
-
buildTools(tools, anchorCache) {
|
|
80
|
+
buildTools(tools, anchorCache, strategy) {
|
|
81
|
+
// Tool cache_control is emitted under "default" and "tools-only". "system-only",
|
|
82
|
+
// "frozen-prefix", and "none" all skip it.
|
|
83
|
+
const emitOnLastTool = anchorCache &&
|
|
84
|
+
(strategy === "default" || strategy === "tools-only");
|
|
81
85
|
return tools.map((t, i) => ({
|
|
82
86
|
name: t.name,
|
|
83
87
|
description: t.description,
|
|
84
88
|
input_schema: JSON.parse(t.parameters),
|
|
85
|
-
...(
|
|
89
|
+
...(emitOnLastTool && i === tools.length - 1 ? { cache_control: { type: "ephemeral" } } : {}),
|
|
86
90
|
}));
|
|
87
91
|
}
|
|
88
92
|
async complete(context, tools, extensions) {
|
|
89
93
|
if (this.circuit.isOpen())
|
|
90
94
|
throw new Error("Circuit breaker open");
|
|
91
|
-
const
|
|
92
|
-
const
|
|
95
|
+
const strategy = resolveCacheBreakpointStrategy(extensions);
|
|
96
|
+
const system = this.buildSystem(context, strategy);
|
|
97
|
+
const msgs = this.buildMessages(context, strategy);
|
|
93
98
|
assertCacheBudget(system, tools.length);
|
|
94
99
|
const requestExtensions = this.requestExtensions(extensions);
|
|
95
100
|
let lastErr;
|
|
@@ -101,7 +106,7 @@ export class AnthropicProvider {
|
|
|
101
106
|
max_tokens: typeof extensions?.max_tokens === "number" ? extensions.max_tokens : 8096,
|
|
102
107
|
...(system ? { system } : {}),
|
|
103
108
|
messages: msgs,
|
|
104
|
-
...(tools.length ? { tools: this.buildTools(tools, !Array.isArray(system)) } : {}),
|
|
109
|
+
...(tools.length ? { tools: this.buildTools(tools, !Array.isArray(system), strategy) } : {}),
|
|
105
110
|
}, extensions);
|
|
106
111
|
this.circuit.recordSuccess();
|
|
107
112
|
let content = "";
|
|
@@ -129,8 +134,9 @@ export class AnthropicProvider {
|
|
|
129
134
|
throw lastErr;
|
|
130
135
|
}
|
|
131
136
|
async *stream(context, tools, extensions, _state, signal) {
|
|
132
|
-
const
|
|
133
|
-
const
|
|
137
|
+
const strategy = resolveCacheBreakpointStrategy(extensions);
|
|
138
|
+
const system = this.buildSystem(context, strategy);
|
|
139
|
+
const msgs = this.buildMessages(context, strategy);
|
|
134
140
|
assertCacheBudget(system, tools.length);
|
|
135
141
|
const requestExtensions = this.requestExtensions(extensions);
|
|
136
142
|
const toolBlocks = {};
|
|
@@ -143,7 +149,7 @@ export class AnthropicProvider {
|
|
|
143
149
|
max_tokens: typeof extensions?.max_tokens === "number" ? extensions.max_tokens : 8096,
|
|
144
150
|
...(system ? { system } : {}),
|
|
145
151
|
messages: msgs,
|
|
146
|
-
...(tools.length ? { tools: this.buildTools(tools, !Array.isArray(system)) } : {}),
|
|
152
|
+
...(tools.length ? { tools: this.buildTools(tools, !Array.isArray(system), strategy) } : {}),
|
|
147
153
|
}, extensions, signal);
|
|
148
154
|
let uncachedInput = 0;
|
|
149
155
|
let cacheReadTokens = 0;
|
|
@@ -235,7 +241,7 @@ export class AnthropicProvider {
|
|
|
235
241
|
? this.client.beta.messages.stream(params, opts)
|
|
236
242
|
: this.client.messages.stream(params, opts));
|
|
237
243
|
}
|
|
238
|
-
buildSystem(context) {
|
|
244
|
+
buildSystem(context, strategy) {
|
|
239
245
|
// B3 note: the system shape is content-driven — 0 blocks (string), 1 block
|
|
240
246
|
// (stable only), or 2 blocks (stable + knowledge). The first turn `systemKnowledge`
|
|
241
247
|
// appears, the block count rises 1→2, which is a one-time prompt-cache invalidation
|
|
@@ -245,23 +251,27 @@ export class AnthropicProvider {
|
|
|
245
251
|
if (!context.systemStable && !context.systemKnowledge) {
|
|
246
252
|
return context.systemText || undefined;
|
|
247
253
|
}
|
|
254
|
+
// System cache_control is emitted under "default" and "system-only". Other strategies
|
|
255
|
+
// keep the text-block structure for protocol parity but omit cache_control.
|
|
256
|
+
const emitOnSystemBlocks = strategy === "default" || strategy === "system-only";
|
|
257
|
+
const cc = { type: "ephemeral" };
|
|
248
258
|
const blocks = [];
|
|
249
259
|
if (context.systemStable) {
|
|
250
|
-
blocks.push({ type: "text", text: context.systemStable,
|
|
260
|
+
blocks.push({ type: "text", text: context.systemStable, ...(emitOnSystemBlocks ? { cache_control: cc } : {}) });
|
|
251
261
|
}
|
|
252
262
|
if (context.systemKnowledge) {
|
|
253
|
-
blocks.push({ type: "text", text: context.systemKnowledge,
|
|
263
|
+
blocks.push({ type: "text", text: context.systemKnowledge, ...(emitOnSystemBlocks ? { cache_control: cc } : {}) });
|
|
254
264
|
}
|
|
255
265
|
return blocks.length ? blocks : undefined;
|
|
256
266
|
}
|
|
257
|
-
buildMessages(context) {
|
|
267
|
+
buildMessages(context, strategy) {
|
|
258
268
|
const msgs = toAnthropicMessages(context.turns, message => this.nativeAssistantBlocks.get(assistantReplayKey(message)));
|
|
259
269
|
// Cache breakpoints anchor on the stable history; the volatile State turn is
|
|
260
270
|
// appended AFTER them as the uncached tail (so the history prefix re-reads
|
|
261
271
|
// across turns). On un-rebuilt bindings stateTurn is absent and the state is
|
|
262
272
|
// already inside `turns` — rendered as-is above. `frozenPrefixLen` (P1-E) pins
|
|
263
273
|
// the deep breakpoint at the compaction boundary; absent ⇒ rolling-pair fallback.
|
|
264
|
-
applyMessageCacheControl(msgs, context.frozenPrefixLen);
|
|
274
|
+
applyMessageCacheControl(msgs, context.frozenPrefixLen, strategy);
|
|
265
275
|
if (context.stateTurn) {
|
|
266
276
|
// Render through toAnthropicMessages so assistant tool_use blocks and
|
|
267
277
|
// tool-role tool_result parts are serialized correctly — toAnthropicContent
|
|
@@ -282,6 +292,18 @@ export class AnthropicProvider {
|
|
|
282
292
|
this.nativeAssistantBlocks.set(assistantReplayKey(message), blocks);
|
|
283
293
|
}
|
|
284
294
|
}
|
|
295
|
+
/** Recognised cache-breakpoint strategy values; any other input (incl. undefined) falls to `"default"`. */
|
|
296
|
+
const CACHE_BREAKPOINT_STRATEGIES = new Set([
|
|
297
|
+
"default", "tools-only", "system-only", "frozen-prefix", "none",
|
|
298
|
+
]);
|
|
299
|
+
/** Pull `cacheBreakpointStrategy` from per-call extensions; unrecognised values → `"default"`. */
|
|
300
|
+
function resolveCacheBreakpointStrategy(extensions) {
|
|
301
|
+
const raw = extensions?.cacheBreakpointStrategy;
|
|
302
|
+
if (typeof raw === "string" && CACHE_BREAKPOINT_STRATEGIES.has(raw)) {
|
|
303
|
+
return raw;
|
|
304
|
+
}
|
|
305
|
+
return "default";
|
|
306
|
+
}
|
|
285
307
|
/** Anthropic accepts at most this many cache_control breakpoints per request. */
|
|
286
308
|
const MAX_CACHE_BREAKPOINTS = 4;
|
|
287
309
|
/**
|
|
@@ -324,15 +346,21 @@ function assertCacheBudget(system, toolCount) {
|
|
|
324
346
|
* cache_control attaches to the last content block of each target, promoting a bare
|
|
325
347
|
* string body to a text block.
|
|
326
348
|
*/
|
|
327
|
-
function applyMessageCacheControl(msgs, frozenPrefixLen) {
|
|
349
|
+
function applyMessageCacheControl(msgs, frozenPrefixLen, strategy) {
|
|
328
350
|
if (!msgs.length)
|
|
329
351
|
return;
|
|
352
|
+
// Message-level cache_control is emitted under "default" and "frozen-prefix" only.
|
|
353
|
+
// "tools-only", "system-only", and "none" skip the history entirely.
|
|
354
|
+
if (strategy === "tools-only" || strategy === "system-only" || strategy === "none")
|
|
355
|
+
return;
|
|
330
356
|
const targets = new Set([msgs.length - 1]);
|
|
331
357
|
if (typeof frozenPrefixLen === "number" && frozenPrefixLen >= 1 && frozenPrefixLen < msgs.length) {
|
|
332
358
|
// Deep anchor at the frozen-prefix boundary (last frozen turn). Fixed between compactions.
|
|
333
359
|
targets.add(frozenPrefixLen - 1);
|
|
334
360
|
}
|
|
335
|
-
else {
|
|
361
|
+
else if (strategy === "default") {
|
|
362
|
+
// Rolling fallback is part of the default strategy only — `"frozen-prefix"` deliberately
|
|
363
|
+
// skips it so a verify can isolate the deep-anchor contribution from the rolling pair.
|
|
336
364
|
for (let i = msgs.length - 2; i >= 0 && targets.size < MESSAGE_CACHE_BREAKPOINTS; i--) {
|
|
337
365
|
if (msgs[i].role === "user")
|
|
338
366
|
targets.add(i);
|
package/dist/types.d.ts
CHANGED
|
@@ -210,6 +210,34 @@ export interface RetryConfig {
|
|
|
210
210
|
*/
|
|
211
211
|
export type ProviderRunState = Record<string, unknown>;
|
|
212
212
|
export type ProviderProtocol = "anthropic-messages" | "openai-chat" | "openai-responses" | "gemini";
|
|
213
|
+
/**
|
|
214
|
+
* Strategy for placing Anthropic-protocol `cache_control` breakpoints across a request's
|
|
215
|
+
* static prefix (tools + system blocks) and rolling history (messages). Pass via the
|
|
216
|
+
* `extensions.cacheBreakpointStrategy` extension on every provider call; the runner already
|
|
217
|
+
* flows `RuntimeOptions.extensions` through, so setting it once on the runner propagates
|
|
218
|
+
* to every Anthropic-protocol call.
|
|
219
|
+
*
|
|
220
|
+
* Values:
|
|
221
|
+
* - `"default"` — current production behavior: a breakpoint on the last tool (when system
|
|
222
|
+
* is rendered as a string), one on each system block (when system blocks are present),
|
|
223
|
+
* and the rolling message pair (last message + frozen-prefix anchor or last preceding
|
|
224
|
+
* user turn).
|
|
225
|
+
* - `"tools-only"` — breakpoint on the last tool only. System blocks and history go
|
|
226
|
+
* uncached. Useful to isolate the tools-prefix cache contribution.
|
|
227
|
+
* - `"system-only"` — breakpoints on system blocks only. No tool, no history caching.
|
|
228
|
+
* Useful to isolate the system-prefix cache contribution.
|
|
229
|
+
* - `"frozen-prefix"` — breakpoints on the message history only, anchored at
|
|
230
|
+
* `frozenPrefixLen` (the compaction boundary, P1-E). Falls back to the last-message
|
|
231
|
+
* breakpoint when no frozen prefix is set. No tools, no system caching. Useful to
|
|
232
|
+
* stress-test the P1-E deep-anchor design.
|
|
233
|
+
* - `"none"` — no `cache_control` anywhere. The baseline for cache-hit attribution.
|
|
234
|
+
*
|
|
235
|
+
* Strategies that disable some breakpoints still keep the structural shape (e.g. system
|
|
236
|
+
* blocks remain text blocks rather than collapsing into a single string), so the only
|
|
237
|
+
* Δ between variants is which blocks carry `cache_control`. Unrecognised strings fall
|
|
238
|
+
* back to `"default"`.
|
|
239
|
+
*/
|
|
240
|
+
export type CacheBreakpointStrategy = "default" | "tools-only" | "system-only" | "frozen-prefix" | "none";
|
|
213
241
|
export interface ProviderDescriptor {
|
|
214
242
|
provider: string;
|
|
215
243
|
protocol: ProviderProtocol;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepstrike/sdk",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.22",
|
|
4
4
|
"description": "DeepStrike Node.js SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@anthropic-ai/sdk": "^0.99.0",
|
|
23
|
-
"@deepstrike/core": "0.2.
|
|
23
|
+
"@deepstrike/core": "0.2.22",
|
|
24
24
|
"@google/generative-ai": "^0.24.1",
|
|
25
25
|
"openai": "^5.23.2"
|
|
26
26
|
},
|