@deepstrike/wasm 0.2.22 → 0.2.26
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/governance.d.ts +9 -0
- package/dist/governance.js +25 -0
- package/dist/harness/index.d.ts +13 -0
- package/dist/harness/index.js +26 -17
- package/dist/index.d.ts +5 -3
- package/dist/index.js +1 -0
- package/dist/providers/anthropic.js +46 -1
- package/dist/runtime/execution-plane.js +25 -5
- package/dist/runtime/runner.d.ts +12 -0
- package/dist/runtime/runner.js +362 -294
- package/dist/runtime/types/agent.js +14 -1
- package/dist/tools/errors.d.ts +42 -0
- package/dist/tools/errors.js +97 -0
- package/dist/tools/index.d.ts +8 -1
- package/dist/tools/index.js +2 -1
- package/dist/types.d.ts +15 -0
- package/package.json +2 -2
package/dist/governance.d.ts
CHANGED
|
@@ -37,7 +37,16 @@ export interface GovernancePolicy {
|
|
|
37
37
|
windowMs: number;
|
|
38
38
|
}[];
|
|
39
39
|
constraints?: GovernanceConstraint[];
|
|
40
|
+
/** I5: when true (default), the runner pre-filters denied tools out of the schema. */
|
|
41
|
+
surfaceDeniedInSystem?: boolean;
|
|
40
42
|
}
|
|
43
|
+
/** I5: bucket tools into allowed/denied per the policy. Pure. Mirrors Node. */
|
|
44
|
+
export declare function governanceFilterSchema<T extends {
|
|
45
|
+
name: string;
|
|
46
|
+
}>(tools: T[], policy: GovernancePolicy | undefined): {
|
|
47
|
+
allowed: T[];
|
|
48
|
+
denied: string[];
|
|
49
|
+
};
|
|
41
50
|
export type GovernanceConstraint = {
|
|
42
51
|
kind: "required";
|
|
43
52
|
tool: string;
|
package/dist/governance.js
CHANGED
|
@@ -51,6 +51,31 @@ export class Governance {
|
|
|
51
51
|
return this._inner.evaluate(toolName, argsJson);
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
|
+
/** I5: bucket tools into allowed/denied per the policy. Pure. Mirrors Node. */
|
|
55
|
+
export function governanceFilterSchema(tools, policy) {
|
|
56
|
+
if (!policy)
|
|
57
|
+
return { allowed: tools, denied: [] };
|
|
58
|
+
const vetoes = new Set(policy.vetoes ?? []);
|
|
59
|
+
const allowed = [];
|
|
60
|
+
const denied = [];
|
|
61
|
+
const matches = (pat, name) => pat === name || (pat.endsWith("*") && name.startsWith(pat.slice(0, -1)));
|
|
62
|
+
for (const tool of tools) {
|
|
63
|
+
if (vetoes.has(tool.name)) {
|
|
64
|
+
denied.push(tool.name);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
let action = policy.defaultAction ?? "allow";
|
|
68
|
+
for (const r of policy.rules ?? []) {
|
|
69
|
+
if (matches(r.pattern, tool.name))
|
|
70
|
+
action = r.action;
|
|
71
|
+
}
|
|
72
|
+
if (action === "deny")
|
|
73
|
+
denied.push(tool.name);
|
|
74
|
+
else
|
|
75
|
+
allowed.push(tool);
|
|
76
|
+
}
|
|
77
|
+
return { allowed, denied };
|
|
78
|
+
}
|
|
54
79
|
export function governancePolicyToKernelEvent(policy) {
|
|
55
80
|
return {
|
|
56
81
|
kind: "load_governance_policy",
|
package/dist/harness/index.d.ts
CHANGED
|
@@ -3,6 +3,10 @@ export interface Criterion {
|
|
|
3
3
|
text: string;
|
|
4
4
|
required: boolean;
|
|
5
5
|
weight?: number;
|
|
6
|
+
/** I3.3 (A4): optional stable id from the host's contract layer; threaded to verdictFn. */
|
|
7
|
+
id?: string;
|
|
8
|
+
/** I3.3 (A4): host hint — host has a deterministic check for this criterion. */
|
|
9
|
+
machineCheckable?: boolean;
|
|
6
10
|
}
|
|
7
11
|
export interface CriterionResult {
|
|
8
12
|
criterion: string;
|
|
@@ -62,13 +66,22 @@ export declare class SinglePassHarness {
|
|
|
62
66
|
constructor(runner: RuntimeRunner);
|
|
63
67
|
run(request: HarnessRequest): Promise<HarnessOutcome>;
|
|
64
68
|
}
|
|
69
|
+
/** I3.2 (A2/A3): host-supplied judgment — see Node `VerdictFn` for the full contract. Mirrors. */
|
|
70
|
+
export type VerdictFn = (ctx: {
|
|
71
|
+
goal: string;
|
|
72
|
+
criteria: Criterion[];
|
|
73
|
+
attempt: number;
|
|
74
|
+
result: string;
|
|
75
|
+
}) => Verdict | undefined | Promise<Verdict | undefined>;
|
|
65
76
|
export interface HarnessLoopOptions {
|
|
66
77
|
maxAttempts?: number;
|
|
78
|
+
verdictFn?: VerdictFn;
|
|
67
79
|
}
|
|
68
80
|
export declare class HarnessLoop {
|
|
69
81
|
private runner;
|
|
70
82
|
private evalProvider;
|
|
71
83
|
private maxAttempts;
|
|
84
|
+
private verdictFn?;
|
|
72
85
|
constructor(runner: RuntimeRunner, evalProvider: import("../types.js").LLMProvider, options?: HarnessLoopOptions);
|
|
73
86
|
runStreaming(request: HarnessRequest): AsyncIterable<HarnessEvent>;
|
|
74
87
|
}
|
package/dist/harness/index.js
CHANGED
|
@@ -23,10 +23,12 @@ export class HarnessLoop {
|
|
|
23
23
|
runner;
|
|
24
24
|
evalProvider;
|
|
25
25
|
maxAttempts;
|
|
26
|
+
verdictFn;
|
|
26
27
|
constructor(runner, evalProvider, options = {}) {
|
|
27
28
|
this.runner = runner;
|
|
28
29
|
this.evalProvider = evalProvider;
|
|
29
30
|
this.maxAttempts = options.maxAttempts ?? 3;
|
|
31
|
+
this.verdictFn = options.verdictFn;
|
|
30
32
|
}
|
|
31
33
|
async *runStreaming(request) {
|
|
32
34
|
const kernel = await import("@deepstrike/wasm-kernel");
|
|
@@ -61,24 +63,31 @@ export class HarnessLoop {
|
|
|
61
63
|
}
|
|
62
64
|
}
|
|
63
65
|
yield { type: "supervising" };
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
66
|
+
// I3.2 (A2/A3): host-supplied verdictFn short-circuits the LLM eval. Undefined ⇒ defer.
|
|
67
|
+
let verdict;
|
|
68
|
+
if (this.verdictFn) {
|
|
69
|
+
verdict = await this.verdictFn({ goal: request.goal, criteria, attempt, result: lastResult });
|
|
70
|
+
}
|
|
71
|
+
if (!verdict) {
|
|
72
|
+
// #6 (0.5.0): eval/verdict compute is the kernel's stateless free functions (was EvalPipeline).
|
|
73
|
+
const evalMsgs = kernel.buildEvalMessages(request.goal, criteria, lastResult, attempt, true);
|
|
74
|
+
let evalText = "";
|
|
75
|
+
const evalContext = {
|
|
76
|
+
systemText: "",
|
|
77
|
+
turns: evalMsgs,
|
|
78
|
+
};
|
|
79
|
+
for await (const evt of this.evalProvider.stream(evalContext, [], undefined)) {
|
|
80
|
+
if (evt.type === "text_delta")
|
|
81
|
+
evalText += evt.delta;
|
|
82
|
+
}
|
|
83
|
+
const parsed = kernel.parseVerdict(evalText);
|
|
84
|
+
verdict = {
|
|
85
|
+
passed: parsed.passed,
|
|
86
|
+
overallScore: parsed.overallScore,
|
|
87
|
+
feedback: parsed.feedback,
|
|
88
|
+
details: (parsed.details ?? []),
|
|
89
|
+
};
|
|
74
90
|
}
|
|
75
|
-
const parsed = kernel.parseVerdict(evalText);
|
|
76
|
-
const verdict = {
|
|
77
|
-
passed: parsed.passed,
|
|
78
|
-
overallScore: parsed.overallScore,
|
|
79
|
-
feedback: parsed.feedback,
|
|
80
|
-
details: (parsed.details ?? []),
|
|
81
|
-
};
|
|
82
91
|
if (verdict.passed) {
|
|
83
92
|
yield { type: "done", verdict, iterations: lastIterations, totalTokens: lastTotalTokens, status: lastStatus };
|
|
84
93
|
return;
|
package/dist/index.d.ts
CHANGED
|
@@ -12,15 +12,17 @@ export type { GovernanceVerdict } from "./governance.js";
|
|
|
12
12
|
export { AnthropicProvider } from "./providers/anthropic.js";
|
|
13
13
|
export { OpenAIProvider, QwenProvider, DeepSeekProvider, MiniMaxProvider, KimiProvider } from "./providers/openai.js";
|
|
14
14
|
export { tool, executeTools } from "./tools/index.js";
|
|
15
|
-
export type { RegisteredTool } from "./tools/index.js";
|
|
15
|
+
export type { RegisteredTool, ToolExecContext } from "./tools/index.js";
|
|
16
|
+
export { safeTool, ok, fail, ToolError, formatToolError } from "./tools/errors.js";
|
|
17
|
+
export type { ToolEnvelope, ToolEnvelopeOk, ToolEnvelopeFail } from "./tools/errors.js";
|
|
16
18
|
export { WorkingMemory } from "./memory/index.js";
|
|
17
19
|
export { InMemoryDreamStore } from "./memory/in-memory-store.js";
|
|
18
20
|
export type { DreamStore, DreamResult, SessionStore, SessionData, SessionMessage, MemoryEntry, CurationResult, CurationStats, } from "./memory/index.js";
|
|
19
21
|
export type { KnowledgeSource } from "./knowledge/index.js";
|
|
20
22
|
export { SinglePassHarness, HarnessLoop } from "./harness/index.js";
|
|
21
|
-
export type { HarnessRequest, HarnessOutcome, HarnessLoopOptions } from "./harness/index.js";
|
|
23
|
+
export type { HarnessRequest, HarnessOutcome, HarnessLoopOptions, CriterionResult, HarnessEvent, VerdictFn } from "./harness/index.js";
|
|
22
24
|
export { ScheduledPrompt } from "./signals/index.js";
|
|
23
25
|
export type { RuntimeSignal, SignalSource } from "./signals/index.js";
|
|
24
26
|
export { PermissionManager, PermissionMode } from "./safety/index.js";
|
|
25
27
|
export type { PermissionDecision } from "./safety/index.js";
|
|
26
|
-
export type { Message, ToolCall, ToolResult, ToolSchema, RenderedContext, ProviderRunState, StreamEvent, TextDelta, ThinkingDelta, ToolCallEvent, ToolResultEvent, DoneEvent, ErrorEvent, PermissionRequestEvent, PermissionResolvedEvent, PermissionResponse, LLMProvider, CacheBreakpointStrategy, } from "./types.js";
|
|
28
|
+
export type { Message, ToolCall, ToolResult, ToolSchema, RenderedContext, ProviderRunState, StreamEvent, TextDelta, ThinkingDelta, ToolCallEvent, ToolResultEvent, ToolAuditFailedEvent, DoneEvent, ErrorEvent, PermissionRequestEvent, PermissionResolvedEvent, PermissionResponse, LLMProvider, CacheBreakpointStrategy, } from "./types.js";
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,7 @@ export { Governance } from "./governance.js";
|
|
|
7
7
|
export { AnthropicProvider } from "./providers/anthropic.js";
|
|
8
8
|
export { OpenAIProvider, QwenProvider, DeepSeekProvider, MiniMaxProvider, KimiProvider } from "./providers/openai.js";
|
|
9
9
|
export { tool, executeTools } from "./tools/index.js";
|
|
10
|
+
export { safeTool, ok, fail, ToolError, formatToolError } from "./tools/errors.js";
|
|
10
11
|
export { WorkingMemory } from "./memory/index.js";
|
|
11
12
|
export { InMemoryDreamStore } from "./memory/in-memory-store.js";
|
|
12
13
|
export { SinglePassHarness, HarnessLoop } from "./harness/index.js";
|
|
@@ -23,6 +23,46 @@ function resolveCacheBreakpointStrategy(extensions) {
|
|
|
23
23
|
}
|
|
24
24
|
return "default";
|
|
25
25
|
}
|
|
26
|
+
/** I1: which slots of the outgoing request carry a `cache_control` breakpoint — used to pro-rata
|
|
27
|
+
* attribute the response's `cache_read_input_tokens` (a single scalar). Mirrors Node. */
|
|
28
|
+
function countCacheControlSlots(system, builtTools, msgs) {
|
|
29
|
+
const sysBp = Array.isArray(system) && system.some(b => b?.cache_control != null);
|
|
30
|
+
const toolBp = !!builtTools && builtTools.some(t => t?.cache_control != null);
|
|
31
|
+
let msgBp = false;
|
|
32
|
+
for (const m of msgs) {
|
|
33
|
+
if (Array.isArray(m.content)) {
|
|
34
|
+
if (m.content.some(b => b?.cache_control != null)) {
|
|
35
|
+
msgBp = true;
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return { system: sysBp, tools: toolBp, messages: msgBp };
|
|
41
|
+
}
|
|
42
|
+
/** I1: split `cache_read_input_tokens` evenly across contributing slots. Remainder lands on the
|
|
43
|
+
* first contributing slot to keep the sum exact. Mirrors Node. */
|
|
44
|
+
function estimateCacheReadBySlot(cacheRead, slotBp) {
|
|
45
|
+
if (cacheRead <= 0)
|
|
46
|
+
return undefined;
|
|
47
|
+
const count = (slotBp.system ? 1 : 0) + (slotBp.tools ? 1 : 0) + (slotBp.messages ? 1 : 0);
|
|
48
|
+
if (count === 0)
|
|
49
|
+
return undefined;
|
|
50
|
+
const share = Math.floor(cacheRead / count);
|
|
51
|
+
const remainder = cacheRead - share * count;
|
|
52
|
+
const out = {};
|
|
53
|
+
let firstDone = false;
|
|
54
|
+
const give = () => { if (!firstDone) {
|
|
55
|
+
firstDone = true;
|
|
56
|
+
return share + remainder;
|
|
57
|
+
} return share; };
|
|
58
|
+
if (slotBp.system)
|
|
59
|
+
out.system = give();
|
|
60
|
+
if (slotBp.tools)
|
|
61
|
+
out.tools = give();
|
|
62
|
+
if (slotBp.messages)
|
|
63
|
+
out.messages = give();
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
26
66
|
/**
|
|
27
67
|
* Roll cache breakpoints across the conversation tail so the message-history
|
|
28
68
|
* prefix is written once and re-read on later turns (without this the cached
|
|
@@ -143,13 +183,16 @@ export class AnthropicProvider {
|
|
|
143
183
|
msgs.push(...stateMsgs);
|
|
144
184
|
}
|
|
145
185
|
assertCacheBudget(system, tools.length);
|
|
186
|
+
const builtTools = tools.length ? buildAnthropicTools(tools, !Array.isArray(system), strategy) : undefined;
|
|
187
|
+
// I1: capture which slots will carry cache_control for pro-rata attribution at usage time.
|
|
188
|
+
const slotBp = countCacheControlSlots(system, builtTools, msgs);
|
|
146
189
|
const body = {
|
|
147
190
|
model: this.model,
|
|
148
191
|
max_tokens: this.maxTokens,
|
|
149
192
|
messages: msgs,
|
|
150
193
|
stream: true,
|
|
151
194
|
...(system ? { system } : {}),
|
|
152
|
-
...(
|
|
195
|
+
...(builtTools ? { tools: builtTools } : {}),
|
|
153
196
|
};
|
|
154
197
|
if (extensions?.enable_thinking) {
|
|
155
198
|
body.thinking = { type: "enabled", budget_tokens: 8000 };
|
|
@@ -206,6 +249,7 @@ export class AnthropicProvider {
|
|
|
206
249
|
// the kernel reads it as the authoritative context size.
|
|
207
250
|
const inputTokens = uncachedInput + cacheReadTokens + cacheCreationTokens;
|
|
208
251
|
if (inputTokens > 0 || outputTokens > 0) {
|
|
252
|
+
const bySlot = estimateCacheReadBySlot(cacheReadTokens, slotBp);
|
|
209
253
|
yield {
|
|
210
254
|
type: "usage",
|
|
211
255
|
totalTokens: inputTokens + outputTokens,
|
|
@@ -213,6 +257,7 @@ export class AnthropicProvider {
|
|
|
213
257
|
outputTokens,
|
|
214
258
|
cacheReadInputTokens: cacheReadTokens,
|
|
215
259
|
cacheCreationInputTokens: cacheCreationTokens,
|
|
260
|
+
...(bySlot ? { cacheReadInputTokensBySlot: bySlot } : {}),
|
|
216
261
|
};
|
|
217
262
|
}
|
|
218
263
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { LargeResultSpool } from "./large-result-spool.js";
|
|
2
|
+
import { formatToolError } from "../tools/errors.js";
|
|
2
3
|
function stripFrontmatter(content) {
|
|
3
4
|
const s = content.trimStart();
|
|
4
5
|
if (!s.startsWith("---"))
|
|
@@ -71,19 +72,38 @@ export class LocalExecutionPlane {
|
|
|
71
72
|
yield { type: "tool_result", callId: call.id, name: call.name, content: `unknown tool: ${call.name}`, isError: true, isFatal: false, errorKind: "recoverable" };
|
|
72
73
|
continue;
|
|
73
74
|
}
|
|
75
|
+
// Per-call `audit` helper: failures collected here are surfaced as `tool_audit_failed`
|
|
76
|
+
// events rather than flipping the main tool result to `isError: true`.
|
|
77
|
+
const auditFailures = [];
|
|
78
|
+
const callCtx = {
|
|
79
|
+
...(ctx.cwd !== undefined ? { cwd: ctx.cwd } : {}),
|
|
80
|
+
audit: async (label, fn) => {
|
|
81
|
+
try {
|
|
82
|
+
await fn();
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
auditFailures.push({ label, error: formatToolError(err) });
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
};
|
|
74
89
|
try {
|
|
75
90
|
const args = JSON.parse(call.arguments || "{}");
|
|
76
|
-
// M3/G4: pass the run context for tool-ABI parity with Node/Python
|
|
77
|
-
|
|
78
|
-
const
|
|
91
|
+
// M3/G4: pass the run context (incl. `cwd`, `audit`) for tool-ABI parity with Node/Python.
|
|
92
|
+
const output = await registered.execute(args, callCtx);
|
|
93
|
+
for (const f of auditFailures) {
|
|
94
|
+
yield { type: "tool_audit_failed", callId: call.id, name: call.name, label: f.label, error: f.error };
|
|
95
|
+
}
|
|
79
96
|
yield { type: "tool_result", callId: call.id, name: call.name, content: String(output), isError: false };
|
|
80
97
|
}
|
|
81
98
|
catch (err) {
|
|
99
|
+
for (const f of auditFailures) {
|
|
100
|
+
yield { type: "tool_audit_failed", callId: call.id, name: call.name, label: f.label, error: f.error };
|
|
101
|
+
}
|
|
82
102
|
yield {
|
|
83
103
|
type: "tool_result",
|
|
84
104
|
callId: call.id,
|
|
85
105
|
name: call.name,
|
|
86
|
-
content:
|
|
106
|
+
content: formatToolError(err),
|
|
87
107
|
isError: true,
|
|
88
108
|
isFatal: Boolean(err?.isFatal),
|
|
89
109
|
errorKind: err?.errorKind,
|
|
@@ -134,7 +154,7 @@ export async function resolvePermissionRequest(request, ctx) {
|
|
|
134
154
|
return {
|
|
135
155
|
approved: false,
|
|
136
156
|
responder: "permission_handler",
|
|
137
|
-
reason: `permission handler failed: ${
|
|
157
|
+
reason: `permission handler failed: ${formatToolError(err)}`,
|
|
138
158
|
};
|
|
139
159
|
}
|
|
140
160
|
}
|
package/dist/runtime/runner.d.ts
CHANGED
|
@@ -51,6 +51,12 @@ export interface TurnMetrics {
|
|
|
51
51
|
activeSkill?: string;
|
|
52
52
|
inputTokens: number;
|
|
53
53
|
cacheReadTokens: number;
|
|
54
|
+
/** I1: pro-rata per-slot attribution of `cacheReadTokens` (Anthropic only). Mirrors Node. */
|
|
55
|
+
cacheReadTokensBySlot?: {
|
|
56
|
+
system?: number;
|
|
57
|
+
tools?: number;
|
|
58
|
+
messages?: number;
|
|
59
|
+
};
|
|
54
60
|
cacheCreationTokens: number;
|
|
55
61
|
}
|
|
56
62
|
export interface RuntimeOptions {
|
|
@@ -67,6 +73,12 @@ export interface RuntimeOptions {
|
|
|
67
73
|
maxTurns?: number;
|
|
68
74
|
timeoutMs?: number;
|
|
69
75
|
agentId?: string;
|
|
76
|
+
/** I4: optional run-start memory pre-fetch hook (mirrors Node SDK). Called once per run before
|
|
77
|
+
* the first LLM turn; each returned query string becomes a dreamStore search; hits page into
|
|
78
|
+
* the knowledge partition before turn 1. Requires dreamStore + agentId. */
|
|
79
|
+
preQueryMemory?: (ctx: {
|
|
80
|
+
goal: string;
|
|
81
|
+
}) => Promise<string[] | undefined> | string[] | undefined;
|
|
70
82
|
systemPrompt?: string;
|
|
71
83
|
initialMemory?: string[];
|
|
72
84
|
/** Skill name → markdown body (WASM has no filesystem). */
|