@deepstrike/wasm 0.2.74 → 0.2.76

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/agent.js CHANGED
@@ -2,6 +2,7 @@ import { RuntimeRunner } from "./runtime/runner.js";
2
2
  import { LocalExecutionPlane } from "./runtime/execution-plane.js";
3
3
  import { InMemorySessionLog } from "./runtime/session-log.js";
4
4
  import { extractJsonValue, validateAgainstSchema } from "./runtime/output-schema.js";
5
+ import { checkMemoryWrite } from "./memory/authority.js";
5
6
  function statusOf(reason) {
6
7
  if (["completed", "success", "done"].includes(reason))
7
8
  return "completed";
@@ -170,6 +171,10 @@ export class Agent {
170
171
  created_at: now, updated_at: now, recall_count: 0, confidence: input.confidence ?? 1,
171
172
  links: [], pinned: input.pinned ?? false, ...(input.ttlDays === undefined ? {} : { ttl_days: input.ttlDays }),
172
173
  };
174
+ // §22.13 · the kernel's write rule, not a copy of it, decides whether this record may be stored.
175
+ const refusal = await checkMemoryWrite(undefined, record.name, record.content);
176
+ if (refusal)
177
+ throw new Error(`agent "${this.name}" memory write denied: ${refusal}`);
173
178
  await this.memory.put(record);
174
179
  return record;
175
180
  }
@@ -12,16 +12,11 @@ export interface GovernancePolicy {
12
12
  windowMs: number;
13
13
  }[];
14
14
  constraints?: GovernanceConstraint[];
15
- /** I5: when true (default), the runner pre-filters denied tools out of the schema. */
15
+ /** I5: when true (default), the kernel withholds statically denied tools (vetoes and `deny`
16
+ * rules, evaluated exactly as the call gate evaluates them) from the provider surface and names
17
+ * them once in the knowledge slot. Mirrors Node. */
16
18
  surfaceDeniedInSystem?: boolean;
17
19
  }
18
- /** I5: bucket tools into allowed/denied per the policy. Pure. Mirrors Node. */
19
- export declare function governanceFilterSchema<T extends {
20
- name: string;
21
- }>(tools: T[], policy: GovernancePolicy | undefined): {
22
- allowed: T[];
23
- denied: string[];
24
- };
25
20
  export type GovernanceConstraint = {
26
21
  kind: "required";
27
22
  tool: string;
@@ -38,5 +33,34 @@ export type GovernanceConstraint = {
38
33
  min?: number;
39
34
  max?: number;
40
35
  };
41
- export declare function governancePolicyToKernelEvent(policy: GovernancePolicy): Record<string, unknown>;
36
+ export declare function governancePolicyToKernelEvent(policy: GovernancePolicy): Record<string, unknown> & {
37
+ rate_limits: Array<{
38
+ tool: string;
39
+ max_calls: number;
40
+ window_ms: number;
41
+ }>;
42
+ };
43
+ /** A §13.2 live-policy patch replacing the governance posture, for `RuntimeRunner.applyPolicyPatch`. */
44
+ export declare function governancePolicyPatch(policy: GovernancePolicy): {
45
+ kind: "replace_governance_policy";
46
+ policy: Record<string, unknown>;
47
+ };
48
+ /** A §13.2 live policy patch in kernel vocabulary — the closed set of policies that may change
49
+ * while an operation runs. Use `governancePolicyPatch` to build the governance variant. */
50
+ export type LivePolicyPatch = {
51
+ kind: "replace_signal_policy";
52
+ policy: Record<string, unknown>;
53
+ } | {
54
+ kind: "replace_governance_policy";
55
+ policy: Record<string, unknown>;
56
+ } | {
57
+ kind: "replace_recovery_policy";
58
+ policy: Record<string, unknown>;
59
+ } | {
60
+ kind: "tighten_resource_quota";
61
+ max_concurrent_subagents?: number;
62
+ max_total_subagents?: number;
63
+ max_spawn_depth?: number;
64
+ max_workflow_nodes?: number;
65
+ };
42
66
  export {};
@@ -1,29 +1,4 @@
1
1
  // ─── Declarative policy (in-kernel gate) ──────────────────────────────────────
2
- /** I5: bucket tools into allowed/denied per the policy. Pure. Mirrors Node. */
3
- export function governanceFilterSchema(tools, policy) {
4
- if (!policy)
5
- return { allowed: tools, denied: [] };
6
- const vetoes = new Set(policy.vetoes ?? []);
7
- const allowed = [];
8
- const denied = [];
9
- const matches = (pat, name) => pat === name || (pat.endsWith("*") && name.startsWith(pat.slice(0, -1)));
10
- for (const tool of tools) {
11
- if (vetoes.has(tool.name)) {
12
- denied.push(tool.name);
13
- continue;
14
- }
15
- let action = policy.defaultAction ?? "allow";
16
- for (const r of policy.rules ?? []) {
17
- if (matches(r.pattern, tool.name))
18
- action = r.action;
19
- }
20
- if (action === "deny")
21
- denied.push(tool.name);
22
- else
23
- allowed.push(tool);
24
- }
25
- return { allowed, denied };
26
- }
27
2
  export function governancePolicyToKernelEvent(policy) {
28
3
  return {
29
4
  kind: "load_governance_policy",
@@ -36,9 +11,31 @@ export function governancePolicyToKernelEvent(policy) {
36
11
  window_ms: rl.windowMs,
37
12
  })),
38
13
  constraints: (policy.constraints ?? []).map(c => c.kind === "enum"
39
- ? { kind: "enum", tool: c.tool, path: c.path, values: c.values }
14
+ ? { kind: "enum", tool: c.tool, param_path: c.path, values: c.values }
40
15
  : c.kind === "range"
41
- ? { kind: "range", tool: c.tool, path: c.path, ...(c.min !== undefined ? { min: c.min } : {}), ...(c.max !== undefined ? { max: c.max } : {}) }
42
- : { kind: "required", tool: c.tool, path: c.path }),
16
+ ? {
17
+ kind: "range", tool: c.tool, param_path: c.path,
18
+ // The wire carries fixed-point micro-units so a bound replays byte-identically.
19
+ ...(c.min !== undefined ? { min_micros: toMicros(c.min) } : {}),
20
+ ...(c.max !== undefined ? { max_micros: toMicros(c.max) } : {}),
21
+ }
22
+ : { kind: "required", tool: c.tool, param_path: c.path }),
23
+ hide_denied_tools: policy.surfaceDeniedInSystem !== false,
43
24
  };
44
25
  }
26
+ /** A §13.2 live-policy patch replacing the governance posture, for `RuntimeRunner.applyPolicyPatch`. */
27
+ export function governancePolicyPatch(policy) {
28
+ const { kind: _kind, ...wire } = governancePolicyToKernelEvent(policy);
29
+ return {
30
+ kind: "replace_governance_policy",
31
+ policy: {
32
+ ...wire,
33
+ rate_limits: wire.rate_limits.map(limit => ({ ...limit, window_ms: String(limit.window_ms) })),
34
+ },
35
+ };
36
+ }
37
+ function toMicros(value) {
38
+ if (!Number.isFinite(value))
39
+ throw new RangeError(`governance range bound must be finite, got ${value}`);
40
+ return Math.round(value * 1_000_000);
41
+ }
package/dist/index.d.ts CHANGED
@@ -22,7 +22,8 @@ export { OpenAIProvider, qwen, deepseek, minimax, kimi } from "./providers/opena
22
22
  export type { OpenAIProviderOptions, BackendProviderOptions } from "./providers/openai.js";
23
23
  export { ProviderError, classifyProviderError } from "./providers/provider-error.js";
24
24
  export type { ProviderErrorKind, ProviderErrorOptions } from "./providers/provider-error.js";
25
- export type { GovernancePolicy, GovernanceConstraint } from "./governance.js";
25
+ export type { GovernancePolicy, GovernanceConstraint, LivePolicyPatch } from "./governance.js";
26
+ export { governancePolicyPatch } from "./governance.js";
26
27
  export { tool, executeTools } from "./tools/index.js";
27
28
  export type { RegisteredTool, ToolExecContext } from "./tools/index.js";
28
29
  export { safeTool, ok, fail, ToolError, formatToolError } from "./tools/errors.js";
package/dist/index.js CHANGED
@@ -14,6 +14,7 @@ export { McpExecutionPlane } from "./runtime/mcp-transport.js";
14
14
  export { AnthropicProvider } from "./providers/anthropic.js";
15
15
  export { OpenAIProvider, qwen, deepseek, minimax, kimi } from "./providers/openai.js";
16
16
  export { ProviderError, classifyProviderError } from "./providers/provider-error.js";
17
+ export { governancePolicyPatch } from "./governance.js";
17
18
  export { tool, executeTools } from "./tools/index.js";
18
19
  export { safeTool, ok, fail, ToolError, formatToolError } from "./tools/errors.js";
19
20
  export { WorkingMemory } from "./memory/index.js";
@@ -0,0 +1,30 @@
1
+ import type { MemoryRecall, MemoryRecallLifecycle } from "./index.js";
2
+ /**
3
+ * §22.13 · the kernel's memory authority for a host with no live operation.
4
+ *
5
+ * Inside a run, every memory decision is a kernel transition (`admit_memory_write`, and the
6
+ * `memory_recalled` / `promotion_suggested` facts a query resolution journals). With no run — an
7
+ * explicit `remember`, a session extract after the run ended — the host asks the same kernel
8
+ * functions statelessly. The SDK keeps no copy of the validation rule and never computes a recall
9
+ * count itself.
10
+ */
11
+ /** The canonical wire `MemoryPolicy` (the `u64` threshold travels as a decimal string). */
12
+ export interface KernelMemoryPolicyWire {
13
+ stale_warning_days?: number;
14
+ retrieval_top_k?: number;
15
+ validation_enabled?: boolean;
16
+ max_content_bytes?: number;
17
+ max_name_length?: number;
18
+ promotion_recall_threshold?: string;
19
+ }
20
+ export interface MemoryPromotion {
21
+ record_id: string;
22
+ recall_count: number;
23
+ }
24
+ /** Would the kernel admit this write? Resolves to the refusal reason, or `undefined` when admitted. */
25
+ export declare function checkMemoryWrite(policy: KernelMemoryPolicyWire | undefined, name: string, content: string): Promise<string | undefined>;
26
+ /** Derive one recall's lifecycle from the counts the store reported it held. */
27
+ export declare function deriveMemoryRecall(policy: KernelMemoryPolicyWire | undefined, recalledAt: number, hits: MemoryRecall[]): Promise<{
28
+ recalls: MemoryRecallLifecycle[];
29
+ promotions: MemoryPromotion[];
30
+ }>;
@@ -0,0 +1,34 @@
1
+ import { getKernel } from "../runtime/kernel.js";
2
+ async function ask(request) {
3
+ const kernel = await getKernel();
4
+ return JSON.parse(kernel.memoryAuthorityJson(JSON.stringify(request)));
5
+ }
6
+ /** Would the kernel admit this write? Resolves to the refusal reason, or `undefined` when admitted. */
7
+ export async function checkMemoryWrite(policy, name, content) {
8
+ const answer = await ask({
9
+ op: "check_write",
10
+ ...(policy ? { policy } : {}),
11
+ name,
12
+ content_bytes: new TextEncoder().encode(content).byteLength,
13
+ });
14
+ return answer.admitted === true ? undefined : String(answer.error ?? "memory write refused");
15
+ }
16
+ /** Derive one recall's lifecycle from the counts the store reported it held. */
17
+ export async function deriveMemoryRecall(policy, recalledAt, hits) {
18
+ if (hits.length === 0)
19
+ return { recalls: [], promotions: [] };
20
+ const answer = await ask({
21
+ op: "derive_recall",
22
+ ...(policy ? { policy } : {}),
23
+ recalled_at: Math.max(0, Math.trunc(recalledAt)),
24
+ recalls: hits.map(hit => ({
25
+ record_id: hit.record.record_id,
26
+ recall_count: Math.max(0, Math.trunc(hit.record.recall_count ?? 0)),
27
+ ...(hit.record.pinned ? { pinned: true } : {}),
28
+ })),
29
+ });
30
+ return {
31
+ recalls: (answer.recalls ?? []),
32
+ promotions: (answer.promotions ?? []),
33
+ };
34
+ }
@@ -1,5 +1,6 @@
1
1
  import { requestSnapshot } from "./prepared-request.js";
2
2
  import { assistantReplayKey, collectStreamMessage, toAnthropicMessages } from "./base.js";
3
+ import { malformedToolArguments, toolArgumentsText } from "../runtime/tool-arguments.js";
3
4
  /** Anthropic accepts at most this many cache_control breakpoints per request. */
4
5
  const MAX_CACHE_BREAKPOINTS = 4;
5
6
  /** Rolling cache breakpoints reserved for the message history (system uses ≤2). */
@@ -317,8 +318,8 @@ export class AnthropicProvider {
317
318
  args = {};
318
319
  }
319
320
  nativeBlocks[idx] = { ...nativeBlocks[idx], input: args };
320
- finalToolCalls.push({ id: tb.id, name: tb.name, arguments: JSON.stringify(args) });
321
- yield { type: "tool_call", id: tb.id, name: tb.name, arguments: args };
321
+ finalToolCalls.push({ id: tb.id, name: tb.name, arguments: toolArgumentsText(tb.argsBuf) });
322
+ yield { type: "tool_call", id: tb.id, name: tb.name, arguments: args, ...malformedToolArguments(tb.argsBuf) };
322
323
  }
323
324
  }
324
325
  }
@@ -1,5 +1,6 @@
1
1
  import { requestSnapshot } from "./prepared-request.js";
2
2
  import { collectStreamMessage, toOpenAIMessages } from "./base.js";
3
+ import { malformedToolArguments } from "../runtime/tool-arguments.js";
3
4
  const DEEPSEEK_REASONERS = new Set(["deepseek-reasoner", "deepseek-r1"]);
4
5
  const MINIMAX_REASONERS = new Set(["MiniMax-M1", "minimax-m1"]);
5
6
  // OpenAI-compatible provider — works for OpenAI, Qwen (DashScope), DeepSeek, MiniMax, Kimi
@@ -107,7 +108,7 @@ export class OpenAIProvider {
107
108
  catch {
108
109
  args = {};
109
110
  }
110
- yield { type: "tool_call", id: tb.id, name: tb.name, arguments: args };
111
+ yield { type: "tool_call", id: tb.id, name: tb.name, arguments: args, ...malformedToolArguments(tb.argsBuf) };
111
112
  }
112
113
  }
113
114
  prepareRequest(context, tools, extensions, state) {
@@ -43,6 +43,12 @@ export declare class CanonicalKernelRejectedError extends Error {
43
43
  readonly fault: Record<string, unknown>;
44
44
  constructor(faultJson: string);
45
45
  }
46
+ /**
47
+ * Whether a run-loop failure was the kernel refusing a malformed input (`invalid_arg`) rather than
48
+ * any other error. Decided by the fault the kernel named, or a binding's argument-conversion
49
+ * status — never by searching the message text.
50
+ */
51
+ export declare function isInvalidInputError(error: unknown): boolean;
46
52
  /**
47
53
  * A record is already authoritative once the journal append returns. A later native commit failure
48
54
  * is therefore a rebuild boundary, never an abort boundary.
@@ -68,6 +74,13 @@ export declare class CanonicalKernelHost {
68
74
  readonly journal: KernelJournal;
69
75
  readonly operationId: string;
70
76
  constructor(kernel: CanonicalKernelInstance, journal: KernelJournal, operationId: string);
77
+ /**
78
+ * The journal holds one outbound envelope slot per operation, so two transitions of the same
79
+ * operation must never overlap: the second would overwrite the first's staged bytes before its
80
+ * append was acknowledged. Every entry point that stages or drains goes through this queue.
81
+ */
82
+ private exclusiveTail;
83
+ private exclusive;
71
84
  transition(input: CanonicalKernelInput, options?: CanonicalTransitionOptions): Promise<CanonicalTransition>;
72
85
  /** Restore the latest installed checkpoint and its authoritative record tail in place. */
73
86
  restore(): Promise<CanonicalRestoreCost>;
@@ -94,6 +107,10 @@ export interface CanonicalRunnerRuntimeOptions {
94
107
  preview: string;
95
108
  }>;
96
109
  }
110
+ /** Positional ids for anonymous appended nodes. A known DAG size yields `wf-node{base+i}`, the same
111
+ * id the kernel gives the node internally; an unknown size (after a restore) yields a batch-scoped
112
+ * id that cannot collide with anything already declared. */
113
+ export type WorkflowNodeIdBase = number | string;
97
114
  export declare function sha256(value: string): string;
98
115
  /**
99
116
  * Canonical operation runtime used by the WASM host.
@@ -108,9 +125,17 @@ export declare class CanonicalRunnerRuntime {
108
125
  private started;
109
126
  private turns;
110
127
  private lastAction;
128
+ /** Live policy revision (§13.2): 0 at genesis, then each `live_policy_changed`. Unknown after a
129
+ * restore, when a patch must name the revision it expects explicitly. */
130
+ private policyRevision;
131
+ /** Kernel-minted attempt per launched task, learned from each launch acknowledgement. */
132
+ private readonly taskAttempts;
111
133
  private readonly newMessages;
112
134
  private readonly hostObservations;
113
135
  private spawnedTasks;
136
+ /** Nodes in the active workflow DAG, mirrored from committed facts so anonymous appended nodes get
137
+ * unique positional ids. `undefined` after a restore until the next committed submission. */
138
+ private workflowNodeCount;
114
139
  private readonly memoryBindingId;
115
140
  private payloadInlineThreshold;
116
141
  private payloadPreviewBytes;
@@ -119,6 +144,8 @@ export declare class CanonicalRunnerRuntime {
119
144
  get journal(): KernelJournal;
120
145
  turn(): number;
121
146
  isTerminal(): boolean;
147
+ /** Whether the operation has a root — the precondition for any live host control command. */
148
+ hasStarted(): boolean;
122
149
  recoveryContentBytes(): number;
123
150
  preservedRefs(): string[];
124
151
  drainNewMessages(): ModelMessage[];
@@ -129,11 +156,16 @@ export declare class CanonicalRunnerRuntime {
129
156
  resumeAction(): KernelRunnerAction | null;
130
157
  startAgent(taskValue: Record<string, unknown>, runSpecValue?: Record<string, unknown>): Promise<KernelRunnerAction | null>;
131
158
  startWorkflow(specValue: Record<string, unknown>): Promise<KernelRunnerAction | null>;
159
+ private appendBase;
132
160
  applyHostEvent(event: Record<string, unknown>): Promise<KernelRunnerAction | null>;
133
161
  private ensureConfigured;
134
162
  private commit;
163
+ /** Record what a committed transition tells the host: its observations and published effects. */
164
+ private absorbTransition;
135
165
  private currentAction;
136
166
  private pendingEffects;
167
+ /** One list field of the pending effect `effectId`, which must be of kind `kind`. */
168
+ private pendingEffectPayload;
137
169
  private succeededEffect;
138
170
  private failedEffect;
139
171
  private pageOutResolution;