@deepstrike/wasm 0.2.13 → 0.2.15

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.
@@ -19,17 +19,26 @@ function buildAnthropicTools(tools, anchorCache) {
19
19
  * Roll cache breakpoints across the conversation tail so the message-history
20
20
  * prefix is written once and re-read on later turns (without this the cached
21
21
  * prefix stops at the end of `system` and the whole tool-result history is
22
- * re-billed at full input price every turn). Marks the final message plus the
23
- * nearest preceding user turn (read anchor); a bare string body is promoted to
24
- * a cache-bearing text block.
22
+ * re-billed at full input price every turn).
23
+ *
24
+ * When `frozenPrefixLen` marks a distinct frozen prefix (the compaction
25
+ * boundary), pin the deep breakpoint there — it is byte-stable across turns,
26
+ * so `[0..frozen]` is re-read cheaply every turn; the tail breakpoint writes
27
+ * only the incremental `[frozen..tail]`. Otherwise fall back to the rolling
28
+ * pair: final message + nearest preceding user turn.
25
29
  */
26
- function applyMessageCacheControl(msgs) {
30
+ function applyMessageCacheControl(msgs, frozenPrefixLen) {
27
31
  if (!msgs.length)
28
32
  return;
29
33
  const targets = new Set([msgs.length - 1]);
30
- for (let i = msgs.length - 2; i >= 0 && targets.size < MESSAGE_CACHE_BREAKPOINTS; i--) {
31
- if (msgs[i].role === "user")
32
- targets.add(i);
34
+ if (typeof frozenPrefixLen === "number" && frozenPrefixLen >= 1 && frozenPrefixLen < msgs.length) {
35
+ targets.add(frozenPrefixLen - 1);
36
+ }
37
+ else {
38
+ for (let i = msgs.length - 2; i >= 0 && targets.size < MESSAGE_CACHE_BREAKPOINTS; i--) {
39
+ if (msgs[i].role === "user")
40
+ targets.add(i);
41
+ }
33
42
  }
34
43
  for (const idx of targets)
35
44
  markLastBlockCacheable(msgs[idx]);
@@ -110,14 +119,15 @@ export class AnthropicProvider {
110
119
  }
111
120
  const system = systemBlocks.length ? systemBlocks : (context.systemText || undefined);
112
121
  const msgs = toAnthropicMessages(context, message => this.nativeAssistantBlocks.get(assistantReplayKey(message)));
113
- applyMessageCacheControl(msgs);
122
+ applyMessageCacheControl(msgs, context.frozenPrefixLen);
114
123
  // Append the volatile State turn AFTER the cache breakpoints (uncached tail);
115
124
  // absent on un-rebuilt bindings, where the state is already inside `turns`.
125
+ // Render through toAnthropicMessages so assistant tool_use blocks are
126
+ // serialized correctly — raw content would silently drop toolCalls.
116
127
  if (context.stateTurn) {
117
- msgs.push({
118
- role: context.stateTurn.role === "assistant" ? "assistant" : "user",
119
- content: context.stateTurn.content,
120
- });
128
+ const stateCtx = { systemText: "", turns: [context.stateTurn] };
129
+ const stateMsgs = toAnthropicMessages(stateCtx, message => this.nativeAssistantBlocks.get(assistantReplayKey(message)));
130
+ msgs.push(...stateMsgs);
121
131
  }
122
132
  assertCacheBudget(system, tools.length);
123
133
  const body = {
@@ -66,7 +66,16 @@ export function toAnthropicMessages(context, nativeReplay) {
66
66
  const result = [];
67
67
  for (const msg of context.turns) {
68
68
  if (msg.role === "tool") {
69
- result.push({ role: "user", content: [{ type: "tool_result", tool_use_id: msg.content ? undefined : "", content: msg.content, is_error: false }] });
69
+ const parts = (msg.contentParts ?? [])
70
+ .filter(p => p.type === "tool_result")
71
+ .map(p => ({ type: "tool_result", tool_use_id: p.callId, content: p.output, is_error: p.isError ?? false }));
72
+ if (parts.length) {
73
+ result.push({ role: "user", content: parts });
74
+ }
75
+ else {
76
+ // Fallback for messages without structured contentParts
77
+ result.push({ role: "user", content: [{ type: "tool_result", tool_use_id: "", content: msg.content, is_error: false }] });
78
+ }
70
79
  continue;
71
80
  }
72
81
  if (msg.role === "assistant" && msg.toolCalls?.length) {
@@ -92,12 +101,6 @@ export function toAnthropicMessages(context, nativeReplay) {
92
101
  content: msg.contentParts?.length ? anthropicPartsContent(msg.contentParts) : msg.content,
93
102
  });
94
103
  }
95
- if (context.systemVolatile && result.length > 0) {
96
- const last = result[result.length - 1];
97
- if (last.role === "user") {
98
- last.content = `${String(last.content ?? "")}\n\n[SYSTEM REMINDER]\n${context.systemVolatile}`;
99
- }
100
- }
101
104
  return result;
102
105
  }
103
106
  /** Collect a non-streaming assistant Message from stream events. */
@@ -14,11 +14,25 @@ function terminationFromStatus(status) {
14
14
  }
15
15
  return status;
16
16
  }
17
+ /** Derive which meta-tools a child runner should expose based on permitted IDs and available sources. */
18
+ function deriveMetaTools(permitted, opts) {
19
+ const metaTools = new Set();
20
+ if (permitted.has("skill") && opts.skillContentMap?.size)
21
+ metaTools.add("skill");
22
+ if (permitted.has("memory") && opts.dreamStore)
23
+ metaTools.add("memory");
24
+ if (permitted.has("knowledge") && opts.knowledgeSource)
25
+ metaTools.add("knowledge");
26
+ if (permitted.has("update_plan") && opts.enablePlanTool)
27
+ metaTools.add("update_plan");
28
+ return metaTools;
29
+ }
17
30
  /** Host-side driver for kernel-isolated sub-agent runs. */
18
31
  export class SubAgentOrchestrator {
19
32
  async run(ctx) {
20
33
  const permitted = new Set(ctx.manifest.permitted_capability_ids ?? []);
21
- const filteredPlane = new FilteredExecutionPlane(ctx.parentOpts.executionPlane, permitted);
34
+ const metaTools = deriveMetaTools(permitted, ctx.parentOpts);
35
+ const filteredPlane = new FilteredExecutionPlane(ctx.parentOpts.executionPlane, permitted, metaTools);
22
36
  let systemPrompt = ctx.parentOpts.systemPrompt;
23
37
  let inheritEvents;
24
38
  if (ctx.manifest.context_inheritance === "full") {
@@ -38,6 +52,10 @@ export class SubAgentOrchestrator {
38
52
  agentId: ctx.spec.identity.agentId,
39
53
  systemPrompt,
40
54
  sessionLog: ctx.sessionLog,
55
+ skillContentMap: metaTools.has("skill") ? ctx.parentOpts.skillContentMap : undefined,
56
+ dreamStore: metaTools.has("memory") ? ctx.parentOpts.dreamStore : undefined,
57
+ knowledgeSource: metaTools.has("knowledge") ? ctx.parentOpts.knowledgeSource : undefined,
58
+ enablePlanTool: metaTools.has("update_plan") ? ctx.parentOpts.enablePlanTool : undefined,
41
59
  });
42
60
  let done;
43
61
  let finalText = "";
package/dist/types.d.ts CHANGED
@@ -47,11 +47,13 @@ export interface RenderedContext {
47
47
  systemText: string;
48
48
  systemStable?: string;
49
49
  systemKnowledge?: string;
50
- systemVolatile?: string;
51
50
  turns: Message[];
52
51
  /** Volatile State turn (task_state + signals), rendered after the cacheable
53
52
  * history. Absent on un-rebuilt bindings — then it's still inside turns[0]. */
54
53
  stateTurn?: Message;
54
+ /** Message count of the frozen history prefix (compaction boundary). When set,
55
+ * Anthropic pins a deep cache breakpoint here instead of the rolling pair. */
56
+ frozenPrefixLen?: number;
55
57
  }
56
58
  export interface StreamEvent {
57
59
  type: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepstrike/wasm",
3
- "version": "0.2.13",
3
+ "version": "0.2.15",
4
4
  "description": "DeepStrike WASM SDK — browser, Cloudflare Workers, Deno Deploy",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -15,7 +15,7 @@
15
15
  "test": "node --experimental-vm-modules node_modules/.bin/jest"
16
16
  },
17
17
  "dependencies": {
18
- "@deepstrike/wasm-kernel": "0.2.13"
18
+ "@deepstrike/wasm-kernel": "0.2.15"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@types/jest": "^30.0.0",