@deepstrike/wasm 0.1.10 → 0.1.14

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.
@@ -0,0 +1,108 @@
1
+ function stripFrontmatter(content) {
2
+ const s = content.trimStart();
3
+ if (!s.startsWith("---"))
4
+ return s;
5
+ const rest = s.slice(3);
6
+ const end = rest.indexOf("\n---");
7
+ return end >= 0 ? rest.slice(end + 4).trimStart() : s;
8
+ }
9
+ export class LocalExecutionPlane {
10
+ tools = new Map();
11
+ register(...tools) {
12
+ for (const t of tools)
13
+ this.tools.set(t.schema.name, t);
14
+ return this;
15
+ }
16
+ unregister(name) {
17
+ this.tools.delete(name);
18
+ return this;
19
+ }
20
+ schemas() {
21
+ return Array.from(this.tools.values()).map(t => t.schema);
22
+ }
23
+ async *executeAll(calls, ctx) {
24
+ const permitted = [];
25
+ for (const c of calls) {
26
+ if (ctx.governance) {
27
+ ctx.governance.setTime(Date.now());
28
+ const v = ctx.governance.evaluate(c.name, c.arguments);
29
+ if (v.kind === "deny") {
30
+ yield { type: "error", message: `permission denied: ${c.name} — ${v.reason ?? ""}` };
31
+ yield { type: "tool_result", callId: c.id, name: c.name, content: `permission denied: ${v.reason ?? ""}`, isError: true };
32
+ continue;
33
+ }
34
+ if (v.kind === "rate_limited") {
35
+ yield { type: "error", message: `rate limited: ${c.name}` };
36
+ yield { type: "tool_result", callId: c.id, name: c.name, content: "rate limited", isError: true };
37
+ continue;
38
+ }
39
+ if (v.kind === "ask_user") {
40
+ yield { type: "permission_request", callId: c.id, toolName: c.name, arguments: c.arguments, reason: v.reason ?? "" };
41
+ yield { type: "tool_result", callId: c.id, name: c.name, content: "awaiting user approval", isError: true };
42
+ continue;
43
+ }
44
+ }
45
+ permitted.push(c);
46
+ }
47
+ const skillCalls = permitted.filter(c => c.name === "skill");
48
+ const memoryCalls = permitted.filter(c => c.name === "memory");
49
+ const knowledgeCalls = permitted.filter(c => c.name === "knowledge");
50
+ const regularCalls = permitted.filter(c => !["skill", "memory", "knowledge"].includes(c.name));
51
+ for (const c of skillCalls) {
52
+ const args = tryParseJson(c.arguments);
53
+ const name = String(args?.name ?? "");
54
+ const raw = ctx.skillContentMap?.get(name);
55
+ const content = raw != null ? stripFrontmatter(raw) : null;
56
+ yield {
57
+ type: "tool_result",
58
+ callId: c.id,
59
+ name: c.name,
60
+ content: content ?? `Skill "${name}" not found.`,
61
+ isError: content == null,
62
+ };
63
+ }
64
+ for (const c of memoryCalls) {
65
+ const args = tryParseJson(c.arguments);
66
+ const topK = typeof args?.top_k === "number" ? args.top_k : 5;
67
+ const entries = (ctx.dreamStore && ctx.agentId)
68
+ ? await ctx.dreamStore.search(ctx.agentId, String(args?.query ?? ""), topK)
69
+ : [];
70
+ const content = entries.length
71
+ ? entries.map((e) => `[score=${e.score.toFixed(3)}] ${e.text}`).join("\n---\n")
72
+ : "No relevant memories found.";
73
+ yield { type: "tool_result", callId: c.id, name: c.name, content, isError: false };
74
+ }
75
+ for (const c of knowledgeCalls) {
76
+ const args = tryParseJson(c.arguments);
77
+ const topK = typeof args?.top_k === "number" ? args.top_k : 5;
78
+ const snippets = ctx.knowledgeSource
79
+ ? await ctx.knowledgeSource.retrieve(String(args?.query ?? ""), topK)
80
+ : [];
81
+ const content = snippets.length ? snippets.join("\n---\n") : "No relevant knowledge found.";
82
+ yield { type: "tool_result", callId: c.id, name: c.name, content, isError: false };
83
+ }
84
+ for (const call of regularCalls) {
85
+ const registered = this.tools.get(call.name);
86
+ if (!registered) {
87
+ yield { type: "tool_result", callId: call.id, name: call.name, content: `unknown tool: ${call.name}`, isError: true };
88
+ continue;
89
+ }
90
+ try {
91
+ const args = JSON.parse(call.arguments || "{}");
92
+ const output = await registered.execute(args);
93
+ yield { type: "tool_result", callId: call.id, name: call.name, content: String(output), isError: false };
94
+ }
95
+ catch (err) {
96
+ yield { type: "tool_result", callId: call.id, name: call.name, content: String(err), isError: true };
97
+ }
98
+ }
99
+ }
100
+ }
101
+ function tryParseJson(s) {
102
+ try {
103
+ return JSON.parse(s);
104
+ }
105
+ catch {
106
+ return null;
107
+ }
108
+ }
@@ -0,0 +1,7 @@
1
+ export type { SessionEvent, SessionLog } from "./session-log.js";
2
+ export { InMemorySessionLog } from "./session-log.js";
3
+ export type { RunContext, ExecutionPlane } from "./execution-plane.js";
4
+ export { LocalExecutionPlane } from "./execution-plane.js";
5
+ export type { RuntimeOptions } from "./runner.js";
6
+ export { RuntimeRunner, collectText } from "./runner.js";
7
+ export { getKernel } from "./kernel.js";
@@ -0,0 +1,4 @@
1
+ export { InMemorySessionLog } from "./session-log.js";
2
+ export { LocalExecutionPlane } from "./execution-plane.js";
3
+ export { RuntimeRunner, collectText } from "./runner.js";
4
+ export { getKernel } from "./kernel.js";
@@ -0,0 +1,4 @@
1
+ type WasmKernel = typeof import("@deepstrike/wasm-kernel");
2
+ /** Lazily load the WASM kernel (browser / worker safe). */
3
+ export declare function getKernel(): Promise<WasmKernel>;
4
+ export {};
@@ -0,0 +1,8 @@
1
+ let kernelMod = null;
2
+ /** Lazily load the WASM kernel (browser / worker safe). */
3
+ export async function getKernel() {
4
+ if (!kernelMod) {
5
+ kernelMod = await import("@deepstrike/wasm-kernel");
6
+ }
7
+ return kernelMod;
8
+ }
@@ -0,0 +1,44 @@
1
+ import type { LLMProvider, StreamEvent } from "../types.js";
2
+ import type { ToolSuspendEvent } from "./execution-plane.js";
3
+ import type { DreamStore, DreamResult } from "../memory/index.js";
4
+ import type { KnowledgeSource } from "../knowledge/index.js";
5
+ import type { SignalSource } from "../signals/index.js";
6
+ import type { SessionLog } from "./session-log.js";
7
+ import type { ExecutionPlane } from "./execution-plane.js";
8
+ import type { Governance } from "../governance.js";
9
+ export interface RuntimeOptions {
10
+ provider: LLMProvider;
11
+ sessionLog: SessionLog;
12
+ executionPlane: ExecutionPlane;
13
+ maxTokens: number;
14
+ maxTurns?: number;
15
+ timeoutMs?: number;
16
+ agentId?: string;
17
+ systemPrompt?: string;
18
+ initialMemory?: string[];
19
+ /** Skill name → markdown body (WASM has no filesystem). */
20
+ skillContentMap?: Map<string, string>;
21
+ dreamStore?: DreamStore;
22
+ knowledgeSource?: KnowledgeSource;
23
+ signalSource?: SignalSource;
24
+ extensions?: Record<string, unknown>;
25
+ governance?: Governance;
26
+ onToolSuspend?: (event: ToolSuspendEvent) => Promise<unknown> | unknown;
27
+ }
28
+ export declare class RuntimeRunner {
29
+ private readonly opts;
30
+ private interrupted;
31
+ constructor(opts: RuntimeOptions);
32
+ interrupt(): void;
33
+ run(req: {
34
+ sessionId: string;
35
+ goal: string;
36
+ criteria?: string[];
37
+ extensions?: Record<string, unknown>;
38
+ }): AsyncIterable<StreamEvent>;
39
+ wake(sessionId: string, extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
40
+ dream(agentId: string, nowMs?: number): Promise<DreamResult>;
41
+ private execute;
42
+ private appendObservations;
43
+ }
44
+ export declare function collectText(stream: AsyncIterable<StreamEvent>): Promise<string>;
@@ -0,0 +1,358 @@
1
+ import { getKernel } from "./kernel.js";
2
+ export class RuntimeRunner {
3
+ opts;
4
+ interrupted = false;
5
+ constructor(opts) {
6
+ this.opts = opts;
7
+ }
8
+ interrupt() { this.interrupted = true; }
9
+ async *run(req) {
10
+ const prior = await this.opts.sessionLog.read(req.sessionId);
11
+ const midRun = isMidRun(prior);
12
+ if (!midRun) {
13
+ await this.opts.sessionLog.append(req.sessionId, {
14
+ kind: "run_started",
15
+ run_id: crypto.randomUUID(),
16
+ goal: req.goal,
17
+ criteria: req.criteria ?? [],
18
+ agent_id: this.opts.agentId,
19
+ system_prompt: this.opts.systemPrompt,
20
+ });
21
+ }
22
+ yield* this.execute(req.sessionId, req.goal, req.criteria ?? [], req.extensions, prior.length > 0 ? prior : undefined, midRun);
23
+ }
24
+ async *wake(sessionId, extensions) {
25
+ const events = await this.opts.sessionLog.read(sessionId);
26
+ if (events.some(e => e.event.kind === "run_terminal"))
27
+ return;
28
+ const startEntry = [...events].reverse().find(e => e.event.kind === "run_started");
29
+ if (!startEntry)
30
+ throw new Error(`No run_started event for session: ${sessionId}`);
31
+ const start = startEntry.event;
32
+ yield* this.execute(sessionId, start.goal, start.criteria, extensions, events, true);
33
+ }
34
+ async dream(agentId, nowMs = Date.now()) {
35
+ if (!this.opts.dreamStore)
36
+ throw new Error("dreamStore not configured");
37
+ const kernel = await getKernel();
38
+ this.opts.governance?._attach(kernel);
39
+ const sessions = await this.opts.dreamStore.loadSessions(agentId);
40
+ const existingMemories = await this.opts.dreamStore.loadMemories(agentId);
41
+ if (!sessions.length)
42
+ return { sessionsProcessed: 0, insightsExtracted: 0, entriesAdded: 0, entriesRemoved: 0 };
43
+ const pipeline = new kernel.IdlePipeline(agentId);
44
+ const action1 = pipeline.feedTrigger(sessions.map(s => ({
45
+ sessionId: s.sessionId, agentId: s.agentId,
46
+ messages: s.messages.map(m => ({
47
+ role: m.role, content: m.content, tokenCount: m.tokenCount,
48
+ toolCalls: (m.toolCalls ?? []).map(tc => ({ id: tc.id, name: tc.name, arguments: tc.arguments })),
49
+ })),
50
+ metadata: JSON.stringify(s.metadata ?? null),
51
+ createdAtMs: s.createdAtMs, updatedAtMs: s.updatedAtMs,
52
+ })), existingMemories.map(e => ({ text: e.text, score: e.score, metadata: JSON.stringify(e.metadata ?? null) })), nowMs);
53
+ if (action1.kind === "noop" || action1.kind === "aborted") {
54
+ return { sessionsProcessed: 0, insightsExtracted: 0, entriesAdded: 0, entriesRemoved: 0 };
55
+ }
56
+ if (action1.kind !== "synthesize_insights")
57
+ throw new Error(`unexpected: ${action1.kind}`);
58
+ let synthesisText = "";
59
+ const providerState = this.opts.provider.createRunState?.();
60
+ const synthMsgs = (action1.messages ?? []);
61
+ const synthContext = {
62
+ systemText: synthMsgs.filter(m => m.role === "system").map(m => m.content).join("\n\n"),
63
+ turns: synthMsgs.filter(m => m.role !== "system"),
64
+ };
65
+ for await (const evt of this.opts.provider.stream(synthContext, [], undefined, providerState)) {
66
+ if (evt.type === "text_delta")
67
+ synthesisText += evt.delta;
68
+ }
69
+ const action2 = pipeline.feedSynthesisResult(synthesisText);
70
+ if (action2.kind !== "commit_memories")
71
+ throw new Error(`unexpected: ${action2.kind}`);
72
+ const cr = action2.curationResult;
73
+ const rr = action2.runResult;
74
+ const dsResult = {
75
+ toAdd: (cr.toAdd ?? []).map((e) => ({
76
+ text: e.text, score: e.score, metadata: tryParseJson(e.metadata),
77
+ })),
78
+ toRemoveIndices: (cr.toRemoveIndices ?? []).map(Number),
79
+ stats: {
80
+ insightsProcessed: cr.stats?.insightsProcessed ?? 0,
81
+ duplicatesRemoved: cr.stats?.duplicatesRemoved ?? 0,
82
+ conflictsResolved: cr.stats?.conflictsResolved ?? 0,
83
+ entriesAdded: cr.stats?.entriesAdded ?? 0,
84
+ },
85
+ };
86
+ await this.opts.dreamStore.commit(agentId, dsResult, existingMemories);
87
+ return {
88
+ sessionsProcessed: rr.sessionsProcessed,
89
+ insightsExtracted: rr.insightsExtracted,
90
+ entriesAdded: cr.stats?.entriesAdded ?? 0,
91
+ entriesRemoved: (cr.toRemoveIndices ?? []).length,
92
+ };
93
+ }
94
+ async *execute(sessionId, goal, criteria, extensions, priorEvents, resumeMidRun = false) {
95
+ this.interrupted = false;
96
+ const kernel = await getKernel();
97
+ this.opts.governance?._attach(kernel);
98
+ const ext = { ...this.opts.extensions, ...(extensions ?? {}) };
99
+ const providerState = this.opts.provider.createRunState?.();
100
+ let nextCompressedArchiveStart = nextArchivedSeqStart(priorEvents);
101
+ const providerPolicy = this.opts.provider.runtimePolicy?.() ?? {};
102
+ const effectiveMaxTurns = this.opts.maxTurns ?? providerPolicy.maxTurns ?? 25;
103
+ const effectiveTimeoutMs = this.opts.timeoutMs ?? providerPolicy.timeoutMs;
104
+ const sm = new kernel.LoopStateMachine({
105
+ maxTokens: this.opts.maxTokens,
106
+ maxTurns: effectiveMaxTurns,
107
+ timeoutMs: effectiveTimeoutMs !== undefined ? BigInt(effectiveTimeoutMs) : undefined,
108
+ });
109
+ const router = new kernel.SignalRouter(256);
110
+ sm.setTools(this.opts.executionPlane.schemas());
111
+ if (this.opts.systemPrompt) {
112
+ sm.addSystemMessage(this.opts.systemPrompt, Math.max(1, Math.ceil(this.opts.systemPrompt.length / 4)));
113
+ }
114
+ if (this.opts.initialMemory) {
115
+ for (const mem of this.opts.initialMemory) {
116
+ sm.addMemoryMessage(mem, Math.max(1, Math.ceil(mem.length / 4)));
117
+ }
118
+ }
119
+ if (this.opts.skillContentMap && this.opts.skillContentMap.size > 0) {
120
+ const metas = [...this.opts.skillContentMap.keys()].map(name => ({
121
+ name,
122
+ description: "",
123
+ estimatedTokens: 0,
124
+ }));
125
+ sm.setAvailableSkills(metas);
126
+ }
127
+ if (this.opts.dreamStore && this.opts.agentId)
128
+ sm.setMemoryEnabled(true);
129
+ if (this.opts.knowledgeSource)
130
+ sm.setKnowledgeEnabled(true);
131
+ if (priorEvents && priorEvents.length > 0) {
132
+ sm.preloadHistory(replayMessages(priorEvents));
133
+ }
134
+ const sessionStart = Date.now();
135
+ let action = resumeMidRun
136
+ ? sm.resumeAfterPreload()
137
+ : sm.start({ goal, criteria });
138
+ while (!sm.isTerminal()) {
139
+ nextCompressedArchiveStart = await this.appendObservations(sessionId, sm, nextCompressedArchiveStart);
140
+ if (this.interrupted) {
141
+ sm.feedTimeout();
142
+ break;
143
+ }
144
+ if (this.opts.signalSource) {
145
+ const sig = await this.opts.signalSource.nextSignal();
146
+ if (sig) {
147
+ const kernelSig = {
148
+ id: crypto.randomUUID(),
149
+ source: sig.source ?? "custom",
150
+ signalType: sig.signalType ?? "event",
151
+ urgency: sig.urgency ?? "normal",
152
+ summary: String(sig.payload?.goal ?? "signal"),
153
+ payload: JSON.stringify(sig.payload ?? {}),
154
+ dedupeKey: sig.dedupeKey,
155
+ timestampMs: Date.now(),
156
+ };
157
+ const disposition = router.ingest(kernelSig, action.kind === "execute_tools");
158
+ if (disposition === "interrupt_now") {
159
+ sm.feedTimeout();
160
+ break;
161
+ }
162
+ }
163
+ }
164
+ let queued = router.next();
165
+ while (queued) {
166
+ if (queued.urgency === "critical") {
167
+ sm.feedTimeout();
168
+ break;
169
+ }
170
+ queued = router.next();
171
+ }
172
+ if (sm.isTerminal())
173
+ break;
174
+ if (action.kind === "call_llm") {
175
+ const finalToolCalls = [];
176
+ let finalText = "";
177
+ const context = action.context;
178
+ const tools = (action.tools ?? []);
179
+ let turnTokens = 0;
180
+ try {
181
+ for await (const evt of this.opts.provider.stream(context, tools, Object.keys(ext).length ? ext : undefined, providerState)) {
182
+ if (evt.type === "usage") {
183
+ turnTokens = evt.totalTokens;
184
+ continue;
185
+ }
186
+ yield evt;
187
+ if (evt.type === "text_delta")
188
+ finalText += evt.delta;
189
+ else if (evt.type === "tool_call") {
190
+ const tc = evt;
191
+ finalToolCalls.push({ id: tc.id, name: tc.name, arguments: JSON.stringify(tc.arguments) });
192
+ }
193
+ }
194
+ }
195
+ catch (err) {
196
+ yield { type: "error", message: String(err) };
197
+ sm.feedTimeout();
198
+ break;
199
+ }
200
+ action = sm.feedLlmResponse({
201
+ role: "assistant",
202
+ content: finalText,
203
+ toolCalls: finalToolCalls,
204
+ tokenCount: turnTokens || undefined,
205
+ });
206
+ await this.opts.sessionLog.append(sessionId, {
207
+ kind: "llm_completed",
208
+ turn: sm.turn,
209
+ content: finalText,
210
+ token_count: turnTokens || undefined,
211
+ tool_calls: finalToolCalls,
212
+ });
213
+ }
214
+ else if (action.kind === "execute_tools") {
215
+ const allCalls = (action.calls ?? []);
216
+ await this.opts.sessionLog.append(sessionId, { kind: "tool_requested", turn: sm.turn, calls: allCalls });
217
+ const runCtx = {
218
+ agentId: this.opts.agentId,
219
+ skillContentMap: this.opts.skillContentMap,
220
+ dreamStore: this.opts.dreamStore,
221
+ knowledgeSource: this.opts.knowledgeSource,
222
+ governance: this.opts.governance,
223
+ onToolSuspend: this.opts.onToolSuspend,
224
+ };
225
+ const toolResults = [];
226
+ for await (const evt of this.opts.executionPlane.executeAll(allCalls, runCtx)) {
227
+ yield evt;
228
+ if (evt.type === "tool_result") {
229
+ const tre = evt;
230
+ toolResults.push({ callId: tre.callId, output: tre.content, isError: tre.isError });
231
+ }
232
+ }
233
+ await this.opts.sessionLog.append(sessionId, {
234
+ kind: "tool_completed",
235
+ turn: sm.turn,
236
+ results: toolResults.map(r => ({
237
+ call_id: r.callId,
238
+ output: r.output,
239
+ is_error: r.isError,
240
+ token_count: r.tokenCount,
241
+ })),
242
+ });
243
+ action = sm.feedToolResults(toolResults);
244
+ }
245
+ else if (action.kind === "done") {
246
+ break;
247
+ }
248
+ }
249
+ const result = action.result;
250
+ const status = result?.termination ?? "error";
251
+ const turnsUsed = result ? Math.max(1, result.turnsUsed ?? 0) : 0;
252
+ const totalTokens = result?.totalTokensUsed ? Number(result.totalTokensUsed) : 0;
253
+ nextCompressedArchiveStart = await this.appendObservations(sessionId, sm, nextCompressedArchiveStart);
254
+ await this.opts.sessionLog.append(sessionId, { kind: "run_terminal", reason: status, turns_used: turnsUsed, total_tokens: totalTokens });
255
+ if (this.opts.dreamStore && this.opts.agentId) {
256
+ const newMsgs = sm.drainNewMessages().map(m => ({
257
+ role: m.role,
258
+ content: m.content,
259
+ tokenCount: m.tokenCount,
260
+ toolCalls: m.toolCalls?.length ? m.toolCalls : undefined,
261
+ }));
262
+ if (newMsgs.length > 0) {
263
+ try {
264
+ await this.opts.dreamStore.saveSession({
265
+ sessionId: crypto.randomUUID(),
266
+ agentId: this.opts.agentId,
267
+ messages: newMsgs,
268
+ metadata: null,
269
+ createdAtMs: sessionStart,
270
+ updatedAtMs: Date.now(),
271
+ });
272
+ }
273
+ catch { /* non-fatal */ }
274
+ }
275
+ }
276
+ yield { type: "done", iterations: turnsUsed, totalTokens, status };
277
+ }
278
+ async appendObservations(sessionId, sm, nextArchiveStart) {
279
+ const observations = sm.takeObservations();
280
+ for (const obs of observations) {
281
+ if (obs.kind !== "compressed")
282
+ continue;
283
+ const latest = await this.opts.sessionLog.latestSeq(sessionId);
284
+ if (latest < nextArchiveStart)
285
+ continue;
286
+ const end = latest;
287
+ const compressedSeq = await this.opts.sessionLog.append(sessionId, {
288
+ kind: "compressed",
289
+ turn: sm.turn,
290
+ archived_seq_range: [nextArchiveStart, end],
291
+ });
292
+ nextArchiveStart = compressedSeq + 1;
293
+ }
294
+ return nextArchiveStart;
295
+ }
296
+ }
297
+ function isMidRun(events) {
298
+ return events.length > 0 && !events.some(e => e.event.kind === "run_terminal");
299
+ }
300
+ function replayMessages(events) {
301
+ const messages = [];
302
+ for (const { event: e } of events) {
303
+ if (e.kind === "run_started") {
304
+ const userText = e.criteria.length
305
+ ? `${e.goal}\n\nCriteria:\n${e.criteria.map((c, i) => `${i + 1}. ${c}`).join("\n")}`
306
+ : e.goal;
307
+ messages.push({
308
+ role: "user",
309
+ content: userText,
310
+ toolCalls: [],
311
+ tokenCount: Math.max(1, Math.ceil(userText.length / 4)),
312
+ });
313
+ }
314
+ else if (e.kind === "llm_completed") {
315
+ messages.push({
316
+ role: "assistant",
317
+ content: e.content,
318
+ toolCalls: e.tool_calls ?? [],
319
+ tokenCount: e.token_count,
320
+ });
321
+ }
322
+ else if (e.kind === "tool_completed") {
323
+ for (const r of e.results) {
324
+ messages.push({
325
+ role: "tool",
326
+ content: r.output,
327
+ toolCalls: [],
328
+ tokenCount: r.token_count,
329
+ });
330
+ }
331
+ }
332
+ }
333
+ return messages;
334
+ }
335
+ function nextArchivedSeqStart(events) {
336
+ let next = 0;
337
+ for (const { event } of events ?? []) {
338
+ if (event.kind === "compressed")
339
+ next = Math.max(next, event.archived_seq_range[1] + 1);
340
+ }
341
+ return next;
342
+ }
343
+ function tryParseJson(s) {
344
+ try {
345
+ return JSON.parse(s);
346
+ }
347
+ catch {
348
+ return null;
349
+ }
350
+ }
351
+ export async function collectText(stream) {
352
+ let text = "";
353
+ for await (const evt of stream) {
354
+ if (evt.type === "text_delta")
355
+ text += evt.delta;
356
+ }
357
+ return text;
358
+ }
@@ -0,0 +1,54 @@
1
+ import type { ToolCall } from "../types.js";
2
+ export type SessionEvent = {
3
+ kind: "run_started";
4
+ run_id: string;
5
+ goal: string;
6
+ criteria: string[];
7
+ agent_id?: string;
8
+ system_prompt?: string;
9
+ } | {
10
+ kind: "llm_completed";
11
+ turn: number;
12
+ content: string;
13
+ token_count?: number;
14
+ tool_calls: ToolCall[];
15
+ } | {
16
+ kind: "tool_requested";
17
+ turn: number;
18
+ calls: ToolCall[];
19
+ } | {
20
+ kind: "tool_completed";
21
+ turn: number;
22
+ results: Array<{
23
+ call_id: string;
24
+ output: string;
25
+ is_error?: boolean;
26
+ token_count?: number;
27
+ }>;
28
+ } | {
29
+ kind: "compressed";
30
+ turn: number;
31
+ archived_seq_range: [number, number];
32
+ } | {
33
+ kind: "run_terminal";
34
+ reason: string;
35
+ turns_used: number;
36
+ total_tokens: number;
37
+ };
38
+ export interface SessionLog {
39
+ append(sessionId: string, event: SessionEvent): Promise<number>;
40
+ read(sessionId: string, fromSeq?: number): Promise<Array<{
41
+ seq: number;
42
+ event: SessionEvent;
43
+ }>>;
44
+ latestSeq(sessionId: string): Promise<number>;
45
+ }
46
+ export declare class InMemorySessionLog implements SessionLog {
47
+ private store;
48
+ append(sessionId: string, event: SessionEvent): Promise<number>;
49
+ read(sessionId: string, fromSeq?: number): Promise<Array<{
50
+ seq: number;
51
+ event: SessionEvent;
52
+ }>>;
53
+ latestSeq(sessionId: string): Promise<number>;
54
+ }
@@ -0,0 +1,18 @@
1
+ export class InMemorySessionLog {
2
+ store = new Map();
3
+ async append(sessionId, event) {
4
+ if (!this.store.has(sessionId))
5
+ this.store.set(sessionId, []);
6
+ const entries = this.store.get(sessionId);
7
+ const seq = entries.length;
8
+ entries.push({ seq, event });
9
+ return seq;
10
+ }
11
+ async read(sessionId, fromSeq = 0) {
12
+ return (this.store.get(sessionId) ?? []).filter(e => e.seq >= fromSeq);
13
+ }
14
+ async latestSeq(sessionId) {
15
+ const entries = this.store.get(sessionId);
16
+ return entries ? entries.length - 1 : -1;
17
+ }
18
+ }
package/dist/types.d.ts CHANGED
@@ -20,6 +20,11 @@ export interface ToolSchema {
20
20
  description: string;
21
21
  parameters: string;
22
22
  }
23
+ /** Structured provider context from the kernel (`call_llm` action). */
24
+ export interface RenderedContext {
25
+ systemText: string;
26
+ turns: Message[];
27
+ }
23
28
  export interface StreamEvent {
24
29
  type: string;
25
30
  }
@@ -27,6 +32,10 @@ export interface TextDelta extends StreamEvent {
27
32
  type: "text_delta";
28
33
  delta: string;
29
34
  }
35
+ export interface UsageEvent extends StreamEvent {
36
+ type: "usage";
37
+ totalTokens: number;
38
+ }
30
39
  export interface ThinkingDelta extends StreamEvent {
31
40
  type: "thinking_delta";
32
41
  delta: string;
@@ -61,6 +70,17 @@ export interface PermissionRequestEvent extends StreamEvent {
61
70
  arguments: string;
62
71
  reason: string;
63
72
  }
73
+ /**
74
+ * Opaque per-run state owned by the provider (e.g. OpenAI Responses continuation).
75
+ * The framework creates and threads this object; providers may read/write it.
76
+ */
77
+ export type ProviderRunState = Record<string, unknown>;
64
78
  export interface LLMProvider {
65
- stream(messages: Message[], tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
79
+ createRunState?(): ProviderRunState;
80
+ runtimePolicy?(): {
81
+ maxTurns?: number;
82
+ timeoutMs?: number;
83
+ };
84
+ complete(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): Promise<Message>;
85
+ stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>, state?: ProviderRunState): AsyncIterable<StreamEvent>;
66
86
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepstrike/wasm",
3
- "version": "0.1.10",
3
+ "version": "0.1.14",
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.1.10"
18
+ "@deepstrike/wasm-kernel": "0.1.14"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@types/jest": "^30.0.0",