@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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # DeepStrike WASM SDK
2
2
 
3
- Agent framework built on a Rust kernel compiled to WebAssembly. Runs in browsers, Cloudflare Workers, Deno Deploy, and Vercel Edge — anywhere that supports `fetch` and WASM.
3
+ Runtime framework built on a Rust kernel compiled to WebAssembly. Runs in browsers, Cloudflare Workers, Deno Deploy, and Vercel Edge — anywhere that supports `fetch` and WASM.
4
4
 
5
5
  ## Install
6
6
 
@@ -15,7 +15,14 @@ The Rust kernel is distributed as a pre-built `.wasm` binary (`@deepstrike/wasm-
15
15
  ## Quick start
16
16
 
17
17
  ```typescript
18
- import { Agent, AnthropicProvider, tool } from "@deepstrike/wasm"
18
+ import {
19
+ RuntimeRunner,
20
+ collectText,
21
+ InMemorySessionLog,
22
+ LocalExecutionPlane,
23
+ AnthropicProvider,
24
+ tool,
25
+ } from "@deepstrike/wasm"
19
26
 
20
27
  const add = tool("add", "Add two numbers.", {
21
28
  type: "object",
@@ -23,20 +30,25 @@ const add = tool("add", "Add two numbers.", {
23
30
  required: ["x", "y"],
24
31
  }, async ({ x, y }) => String((x as number) + (y as number)))
25
32
 
26
- const agent = new Agent(
27
- new AnthropicProvider(apiKey),
28
- { maxTokens: 32_000, maxTurns: 10 },
29
- )
30
- agent.register(add)
33
+ const plane = new LocalExecutionPlane()
34
+ plane.register(add)
31
35
 
32
- const answer = await agent.run("What is 2 + 3?")
36
+ const runner = new RuntimeRunner({
37
+ provider: new AnthropicProvider(apiKey),
38
+ sessionLog: new InMemorySessionLog(),
39
+ executionPlane: plane,
40
+ maxTokens: 32_000,
41
+ maxTurns: 10,
42
+ })
43
+
44
+ const answer = await collectText(runner.run({ sessionId: "demo", goal: "What is 2 + 3?" }))
33
45
  console.log(answer) // "5"
34
46
  ```
35
47
 
36
48
  Streaming:
37
49
 
38
50
  ```typescript
39
- for await (const event of agent.runStreaming("Summarize this page")) {
51
+ for await (const event of runner.run({ sessionId: "demo", goal: "Summarize this page" })) {
40
52
  if (event.type === "text_delta") process.stdout.write(event.delta)
41
53
  else if (event.type === "tool_call") console.log(`\n[→ ${event.name}]`)
42
54
  else if (event.type === "done") console.log(`\ndone in ${event.iterations} turns (${event.status})`)
@@ -50,13 +62,13 @@ for await (const event of agent.runStreaming("Summarize this page")) {
50
62
  ```
51
63
  src/
52
64
  ├── index.ts # Public exports
53
- ├── agent.ts # Agent top-level entry point
65
+ ├── runtime/ # RuntimeRunner, SessionLog, ExecutionPlane
54
66
  ├── types.ts # Shared type definitions
55
67
  ├── providers/ # LLM adapters (fetch-based SSE)
56
68
  ├── tools/ # tool() helper, executeTools (no fs/shell)
57
- ├── memory/ # WorkingMemory + MemorySource/Extractor interfaces
69
+ ├── memory/ # WorkingMemory + DreamStore interfaces
58
70
  ├── knowledge/ # KnowledgeSource interface
59
- ├── harness/ # SinglePassHarness, EvalLoopHarness, QualityGate
71
+ ├── harness/ # SinglePassHarness, HarnessLoop
60
72
  ├── signals/ # RuntimeSignal, SignalSource, ScheduledPrompt
61
73
  └── safety/ # PermissionManager
62
74
  ```
@@ -76,7 +88,7 @@ The kernel (`@deepstrike/wasm-kernel`, Rust/wasm-bindgen) owns:
76
88
  | Long-term storage | IndexedDB | KV / D1 | SQLite |
77
89
  | External signals | `postMessage` | event | any |
78
90
 
79
- The WASM SDK ships **no `readFile` built-in**. Tools must be pure JS / serializable data. Skill loading is delegated to the host (fetch from a URL, read from IndexedDB, etc.).
91
+ The WASM SDK ships **no `readFile` built-in**. Tools must be pure JS / serializable data. Skills use `skillContentMap` on `RuntimeOptions` (no filesystem).
80
92
 
81
93
  ---
82
94
 
@@ -101,7 +113,11 @@ const provider = new AnthropicProvider("sk-...", "claude-opus-4-7")
101
113
  Thinking / reasoning:
102
114
 
103
115
  ```typescript
104
- for await (const event of agent.runStreaming("...", undefined, { enable_thinking: true })) {
116
+ for await (const event of runner.run({
117
+ sessionId: "demo",
118
+ goal: "...",
119
+ extensions: { enable_thinking: true },
120
+ })) {
105
121
  if (event.type === "thinking_delta") console.log(event.delta)
106
122
  }
107
123
  ```
@@ -113,7 +129,7 @@ for await (const event of agent.runStreaming("...", undefined, { enable_thinking
113
129
  Tools must be pure functions — no shell, no filesystem.
114
130
 
115
131
  ```typescript
116
- import { tool, Agent } from "@deepstrike/wasm"
132
+ import { tool, LocalExecutionPlane } from "@deepstrike/wasm"
117
133
 
118
134
  const fetchUrl = tool("fetch_url", "Fetch a URL and return its text.", {
119
135
  type: "object",
@@ -124,7 +140,8 @@ const fetchUrl = tool("fetch_url", "Fetch a URL and return its text.", {
124
140
  return resp.text()
125
141
  })
126
142
 
127
- agent.register(fetchUrl)
143
+ const plane = new LocalExecutionPlane()
144
+ plane.register(fetchUrl)
128
145
  ```
129
146
 
130
147
  ---
@@ -132,12 +149,15 @@ agent.register(fetchUrl)
132
149
  ## Governance
133
150
 
134
151
  ```typescript
135
- import { Agent, AnthropicProvider, Governance } from "@deepstrike/wasm"
152
+ import { RuntimeRunner, AnthropicProvider, Governance } from "@deepstrike/wasm"
136
153
 
137
154
  const gov = new Governance()
138
155
  gov.blockTool("dangerous_tool")
139
156
 
140
- const agent = new Agent(provider, {
157
+ const runner = new RuntimeRunner({
158
+ provider: new AnthropicProvider(apiKey),
159
+ sessionLog: new InMemorySessionLog(),
160
+ executionPlane: new LocalExecutionPlane(),
141
161
  maxTokens: 32_000,
142
162
  governance: gov,
143
163
  })
@@ -171,7 +191,10 @@ class VectorSearch implements KnowledgeSource {
171
191
  }
172
192
  }
173
193
 
174
- const agent = new Agent(provider, {
194
+ const runner = new RuntimeRunner({
195
+ provider,
196
+ sessionLog: new InMemorySessionLog(),
197
+ executionPlane: new LocalExecutionPlane(),
175
198
  maxTokens: 32_000,
176
199
  maxTurns: 10,
177
200
  knowledgeSource: new VectorSearch(),
@@ -186,12 +209,12 @@ const agent = new Agent(provider, {
186
209
  import { SinglePassHarness, HarnessLoop } from "@deepstrike/wasm"
187
210
 
188
211
  // Single pass — always passes
189
- const harness = new SinglePassHarness(agent)
212
+ const harness = new SinglePassHarness(runner)
190
213
  const outcome = await harness.run({ goal: "Write a haiku" })
191
214
  console.log(outcome.result)
192
215
 
193
216
  // Eval loop — LLM-judges the output; retries up to 3 times
194
- const loop = new HarnessLoop(agent, evalProvider, { maxAttempts: 3 })
217
+ const loop = new HarnessLoop(runner, evalProvider, { maxAttempts: 3 })
195
218
  for await (const event of loop.runStreaming({
196
219
  goal: "Write a haiku",
197
220
  criteria: [
@@ -212,7 +235,7 @@ import { ScheduledPrompt } from "@deepstrike/wasm"
212
235
  import type { SignalSource, RuntimeSignal } from "@deepstrike/wasm"
213
236
 
214
237
  // Interrupt from a UI button
215
- document.getElementById("stop")!.onclick = () => agent.interrupt()
238
+ document.getElementById("stop")!.onclick = () => runner.interrupt()
216
239
 
217
240
  // Convert a scheduled prompt to a RuntimeSignal
218
241
  const prompt = new ScheduledPrompt("Daily standup summary", 1_700_000_000_000)
@@ -260,14 +283,29 @@ Modes: `DEFAULT` (evaluate grants), `PLAN` (block all), `AUTO` (allow all).
260
283
 
261
284
  ```typescript
262
285
  import init from "@deepstrike/wasm-kernel"
263
- import { Agent, AnthropicProvider } from "@deepstrike/wasm"
286
+ import {
287
+ RuntimeRunner,
288
+ collectText,
289
+ InMemorySessionLog,
290
+ LocalExecutionPlane,
291
+ AnthropicProvider,
292
+ } from "@deepstrike/wasm"
264
293
  import wasmBinary from "@deepstrike/wasm-kernel/deepstrike_wasm_bg.wasm"
265
294
 
266
295
  export default {
267
296
  async fetch(request: Request, env: Env): Promise<Response> {
268
297
  await init(wasmBinary)
269
- const agent = new Agent(new AnthropicProvider(env.ANTHROPIC_KEY), { maxTokens: 32_000, maxTurns: 10 })
270
- const result = await agent.run(await request.text())
298
+ const runner = new RuntimeRunner({
299
+ provider: new AnthropicProvider(env.ANTHROPIC_KEY),
300
+ sessionLog: new InMemorySessionLog(),
301
+ executionPlane: new LocalExecutionPlane(),
302
+ maxTokens: 32_000,
303
+ maxTurns: 10,
304
+ })
305
+ const result = await collectText(runner.run({
306
+ sessionId: crypto.randomUUID(),
307
+ goal: await request.text(),
308
+ }))
271
309
  return new Response(result)
272
310
  },
273
311
  }
@@ -277,10 +315,16 @@ export default {
277
315
 
278
316
  ```typescript
279
317
  import init from "@deepstrike/wasm-kernel"
280
- import { Agent, AnthropicProvider } from "@deepstrike/wasm"
318
+ import { RuntimeRunner, InMemorySessionLog, LocalExecutionPlane, AnthropicProvider } from "@deepstrike/wasm"
281
319
 
282
320
  await init()
283
- const agent = new Agent(new AnthropicProvider(import.meta.env.VITE_ANTHROPIC_KEY), { maxTokens: 32_000, maxTurns: 10 })
321
+ const runner = new RuntimeRunner({
322
+ provider: new AnthropicProvider(import.meta.env.VITE_ANTHROPIC_KEY),
323
+ sessionLog: new InMemorySessionLog(),
324
+ executionPlane: new LocalExecutionPlane(),
325
+ maxTokens: 32_000,
326
+ maxTurns: 10,
327
+ })
284
328
  ```
285
329
 
286
330
  ---
@@ -291,6 +335,7 @@ const agent = new Agent(new AnthropicProvider(import.meta.env.VITE_ANTHROPIC_KEY
291
335
  |------------|--------|
292
336
  | `text_delta` | `delta: string` |
293
337
  | `thinking_delta` | `delta: string` |
338
+ | `usage` | `totalTokens: number` |
294
339
  | `tool_call` | `id, name, arguments` |
295
340
  | `tool_result` | `callId, name, content, isError` |
296
341
  | `done` | `iterations, totalTokens, status` |
@@ -1,4 +1,4 @@
1
- import type { Agent } from "../agent.js";
1
+ import type { RuntimeRunner } from "../runtime/runner.js";
2
2
  export interface Criterion {
3
3
  text: string;
4
4
  required: boolean;
@@ -58,17 +58,17 @@ export type HarnessEvent = {
58
58
  type: "max_attempts_reached";
59
59
  };
60
60
  export declare class SinglePassHarness {
61
- private agent;
62
- constructor(agent: Agent);
61
+ private runner;
62
+ constructor(runner: RuntimeRunner);
63
63
  run(request: HarnessRequest): Promise<HarnessOutcome>;
64
64
  }
65
65
  export interface HarnessLoopOptions {
66
66
  maxAttempts?: number;
67
67
  }
68
68
  export declare class HarnessLoop {
69
- private agent;
69
+ private runner;
70
70
  private evalProvider;
71
71
  private maxAttempts;
72
- constructor(agent: Agent, evalProvider: import("../types.js").LLMProvider, options?: HarnessLoopOptions);
72
+ constructor(runner: RuntimeRunner, evalProvider: import("../types.js").LLMProvider, options?: HarnessLoopOptions);
73
73
  runStreaming(request: HarnessRequest): AsyncIterable<HarnessEvent>;
74
74
  }
@@ -1,7 +1,8 @@
1
- async function runOnce(agent, goal, req) {
1
+ async function runOnce(runner, req) {
2
2
  let text = "";
3
3
  let done;
4
- for await (const evt of agent.runStreaming(goal, (req.criteria ?? []).map(c => c.text), req.extensions)) {
4
+ const sessionId = crypto.randomUUID();
5
+ for await (const evt of runner.run({ sessionId, goal: req.goal, criteria: req.criteria?.map(c => c.text), extensions: req.extensions })) {
5
6
  if (evt.type === "text_delta")
6
7
  text += evt.delta;
7
8
  else if (evt.type === "done")
@@ -10,20 +11,20 @@ async function runOnce(agent, goal, req) {
10
11
  return { result: text, passed: false, iterations: done?.iterations ?? 0, totalTokens: done?.totalTokens ?? 0, status: done?.status ?? "error" };
11
12
  }
12
13
  export class SinglePassHarness {
13
- agent;
14
- constructor(agent) {
15
- this.agent = agent;
14
+ runner;
15
+ constructor(runner) {
16
+ this.runner = runner;
16
17
  }
17
18
  async run(request) {
18
- return { ...await runOnce(this.agent, request.goal, request), passed: true };
19
+ return { ...await runOnce(this.runner, request), passed: true };
19
20
  }
20
21
  }
21
22
  export class HarnessLoop {
22
- agent;
23
+ runner;
23
24
  evalProvider;
24
25
  maxAttempts;
25
- constructor(agent, evalProvider, options = {}) {
26
- this.agent = agent;
26
+ constructor(runner, evalProvider, options = {}) {
27
+ this.runner = runner;
27
28
  this.evalProvider = evalProvider;
28
29
  this.maxAttempts = options.maxAttempts ?? 3;
29
30
  }
@@ -36,8 +37,9 @@ export class HarnessLoop {
36
37
  let lastTotalTokens = 0;
37
38
  let lastStatus = "error";
38
39
  let lastResult = "";
40
+ const sessionId = crypto.randomUUID();
39
41
  for (let attempt = 1; attempt <= this.maxAttempts; attempt++) {
40
- for await (const evt of this.agent.runStreaming(currentGoal, criteria.map(c => c.text), request.extensions)) {
42
+ for await (const evt of this.runner.run({ sessionId, goal: currentGoal, criteria: criteria.map(c => c.text), extensions: request.extensions })) {
41
43
  if (evt.type === "text_delta") {
42
44
  lastResult += evt.delta;
43
45
  yield { type: "token", text: evt.delta };
@@ -64,7 +66,11 @@ export class HarnessLoop {
64
66
  if (evalAction.kind !== "evaluate")
65
67
  break;
66
68
  let evalText = "";
67
- for await (const evt of this.evalProvider.stream((evalAction.messages ?? []), [], undefined)) {
69
+ const evalContext = {
70
+ systemText: "",
71
+ turns: (evalAction.messages ?? []),
72
+ };
73
+ for await (const evt of this.evalProvider.stream(evalContext, [], undefined)) {
68
74
  if (evt.type === "text_delta")
69
75
  evalText += evt.delta;
70
76
  }
@@ -75,7 +81,7 @@ export class HarnessLoop {
75
81
  passed: doneAction.passed ?? false,
76
82
  overallScore: doneAction.overallScore ?? 0,
77
83
  feedback: doneAction.feedback ?? "",
78
- details: doneAction.details ?? [],
84
+ details: (doneAction.details ?? []),
79
85
  };
80
86
  if (verdict.passed) {
81
87
  yield { type: "done", verdict, iterations: lastIterations, totalTokens: lastTotalTokens, status: lastStatus };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export { Agent } from "./agent.js";
2
- export type { AgentOptions, SkillMetadata } from "./agent.js";
1
+ export { RuntimeRunner, collectText, InMemorySessionLog, LocalExecutionPlane, } from "./runtime/index.js";
2
+ export type { RuntimeOptions, SessionEvent, SessionLog, RunContext, ExecutionPlane, } from "./runtime/index.js";
3
3
  export { Governance } from "./governance.js";
4
4
  export type { GovernanceVerdict } from "./governance.js";
5
5
  export { AnthropicProvider } from "./providers/anthropic.js";
@@ -7,7 +7,7 @@ export { OpenAIProvider, QwenProvider, DeepSeekProvider, MiniMaxProvider, KimiPr
7
7
  export { tool, executeTools } from "./tools/index.js";
8
8
  export type { RegisteredTool } from "./tools/index.js";
9
9
  export { WorkingMemory } from "./memory/index.js";
10
- export type { DreamStore, DreamResult, SessionData, SessionMessage, MemoryEntry, CurationResult, CurationStats, } from "./memory/index.js";
10
+ export type { DreamStore, DreamResult, SessionStore, SessionData, SessionMessage, MemoryEntry, CurationResult, CurationStats, } from "./memory/index.js";
11
11
  export type { KnowledgeSource } from "./knowledge/index.js";
12
12
  export { SinglePassHarness, HarnessLoop } from "./harness/index.js";
13
13
  export type { HarnessRequest, HarnessOutcome, HarnessLoopOptions } from "./harness/index.js";
@@ -15,4 +15,4 @@ export { ScheduledPrompt } from "./signals/index.js";
15
15
  export type { RuntimeSignal, SignalSource } from "./signals/index.js";
16
16
  export { PermissionManager, PermissionMode } from "./safety/index.js";
17
17
  export type { PermissionDecision } from "./safety/index.js";
18
- export type { Message, ToolCall, ToolResult, ToolSchema, StreamEvent, TextDelta, ThinkingDelta, ToolCallEvent, ToolResultEvent, DoneEvent, ErrorEvent, PermissionRequestEvent, LLMProvider, } from "./types.js";
18
+ export type { Message, ToolCall, ToolResult, ToolSchema, RenderedContext, ProviderRunState, StreamEvent, TextDelta, ThinkingDelta, ToolCallEvent, ToolResultEvent, DoneEvent, ErrorEvent, PermissionRequestEvent, LLMProvider, } from "./types.js";
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { Agent } from "./agent.js";
1
+ export { RuntimeRunner, collectText, InMemorySessionLog, LocalExecutionPlane, } from "./runtime/index.js";
2
2
  export { Governance } from "./governance.js";
3
3
  export { AnthropicProvider } from "./providers/anthropic.js";
4
4
  export { OpenAIProvider, QwenProvider, DeepSeekProvider, MiniMaxProvider, KimiProvider } from "./providers/openai.js";
@@ -54,3 +54,7 @@ export interface DreamStore {
54
54
  /** Persist a completed session for future consolidation via `Agent.dream()`. */
55
55
  saveSession(data: SessionData): Promise<void>;
56
56
  }
57
+ export interface SessionStore {
58
+ loadSession(sessionId: string): Promise<SessionData | undefined>;
59
+ saveSession(data: SessionData): Promise<void>;
60
+ }
@@ -1,8 +1,9 @@
1
- import type { Message, ToolSchema, StreamEvent, LLMProvider } from "../types.js";
1
+ import type { RenderedContext, ToolSchema, StreamEvent, LLMProvider, Message } from "../types.js";
2
2
  export declare class AnthropicProvider implements LLMProvider {
3
3
  private readonly apiKey;
4
4
  private readonly model;
5
5
  private readonly maxTokens;
6
6
  constructor(apiKey: string, model?: string, maxTokens?: number);
7
- stream(messages: Message[], tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
7
+ complete(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): Promise<Message>;
8
+ stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
8
9
  }
@@ -1,3 +1,4 @@
1
+ import { collectStreamMessage, toAnthropicMessages } from "./base.js";
1
2
  function buildAnthropicTools(tools) {
2
3
  return tools.map(t => ({ name: t.name, description: t.description, input_schema: JSON.parse(t.parameters) }));
3
4
  }
@@ -10,9 +11,12 @@ export class AnthropicProvider {
10
11
  this.model = model;
11
12
  this.maxTokens = maxTokens;
12
13
  }
13
- async *stream(messages, tools, extensions) {
14
- const system = messages.filter(m => m.role === "system").map(m => m.content).join("\n\n");
15
- const msgs = messages.filter(m => m.role !== "system").map(m => ({ role: m.role, content: m.content }));
14
+ async complete(context, tools, extensions) {
15
+ return collectStreamMessage(this.stream(context, tools, extensions));
16
+ }
17
+ async *stream(context, tools, extensions) {
18
+ const system = context.systemText || undefined;
19
+ const msgs = toAnthropicMessages(context);
16
20
  const body = {
17
21
  model: this.model,
18
22
  max_tokens: this.maxTokens,
@@ -54,7 +58,16 @@ export class AnthropicProvider {
54
58
  return;
55
59
  try {
56
60
  const evt = JSON.parse(data);
57
- if (evt.type === "content_block_start") {
61
+ if (evt.type === "message_start" || evt.type === "message_delta") {
62
+ const usage = (evt.usage ?? evt.message?.usage);
63
+ if (usage?.input_tokens != null) {
64
+ yield {
65
+ type: "usage",
66
+ totalTokens: usage.input_tokens + (usage.output_tokens ?? 0),
67
+ };
68
+ }
69
+ }
70
+ else if (evt.type === "content_block_start") {
58
71
  const cb = evt.content_block;
59
72
  if (cb.type === "tool_use")
60
73
  toolBlocks[evt.index] = { id: cb.id, name: cb.name, argsBuf: "" };
@@ -0,0 +1,16 @@
1
+ import type { Message, RenderedContext } from "../types.js";
2
+ /** Build OpenAI-compatible chat messages from a RenderedContext. */
3
+ export declare function toOpenAIMessages(context: RenderedContext): Array<Record<string, unknown>>;
4
+ /** Anthropic messages array (system is a separate top-level param). */
5
+ export declare function toAnthropicMessages(context: RenderedContext): Array<{
6
+ role: string;
7
+ content: string;
8
+ }>;
9
+ /** Collect a non-streaming assistant Message from stream events. */
10
+ export declare function collectStreamMessage(stream: AsyncIterable<{
11
+ type: string;
12
+ delta?: string;
13
+ id?: string;
14
+ name?: string;
15
+ arguments?: Record<string, unknown>;
16
+ }>): Promise<Message>;
@@ -0,0 +1,42 @@
1
+ /** Build OpenAI-compatible chat messages from a RenderedContext. */
2
+ export function toOpenAIMessages(context) {
3
+ const messages = [];
4
+ if (context.systemText) {
5
+ messages.push({ role: "system", content: context.systemText });
6
+ }
7
+ for (const msg of context.turns) {
8
+ if (msg.role === "tool") {
9
+ messages.push({ role: "tool", content: msg.content });
10
+ continue;
11
+ }
12
+ const next = { role: msg.role, content: msg.content };
13
+ if (msg.role === "assistant" && msg.toolCalls?.length) {
14
+ next.tool_calls = msg.toolCalls.map(tc => ({
15
+ id: tc.id,
16
+ type: "function",
17
+ function: { name: tc.name, arguments: tc.arguments },
18
+ }));
19
+ }
20
+ messages.push(next);
21
+ }
22
+ return messages;
23
+ }
24
+ /** Anthropic messages array (system is a separate top-level param). */
25
+ export function toAnthropicMessages(context) {
26
+ return context.turns
27
+ .filter(m => m.role !== "system")
28
+ .map(m => ({ role: m.role, content: m.content }));
29
+ }
30
+ /** Collect a non-streaming assistant Message from stream events. */
31
+ export async function collectStreamMessage(stream) {
32
+ let content = "";
33
+ const toolCalls = [];
34
+ for await (const evt of stream) {
35
+ if (evt.type === "text_delta" && evt.delta)
36
+ content += evt.delta;
37
+ else if (evt.type === "tool_call" && evt.id && evt.name) {
38
+ toolCalls.push({ id: evt.id, name: evt.name, arguments: JSON.stringify(evt.arguments ?? {}) });
39
+ }
40
+ }
41
+ return { role: "assistant", content, ...(toolCalls.length ? { toolCalls } : {}) };
42
+ }
@@ -1,4 +1,4 @@
1
- import type { Message, ToolSchema, StreamEvent, LLMProvider } from "../types.js";
1
+ import type { RenderedContext, ToolSchema, StreamEvent, LLMProvider, Message } from "../types.js";
2
2
  export declare class OpenAIProvider implements LLMProvider {
3
3
  protected readonly apiKey: string;
4
4
  protected readonly model: string;
@@ -12,20 +12,21 @@ export declare class OpenAIProvider implements LLMProvider {
12
12
  parameters: any;
13
13
  };
14
14
  }[];
15
- protected streamInner(messages: Message[], tools: ToolSchema[], extraBody: Record<string, unknown>, exposeReasoning?: boolean): AsyncIterable<StreamEvent>;
16
- stream(messages: Message[], tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
15
+ complete(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): Promise<Message>;
16
+ protected streamInner(context: RenderedContext, tools: ToolSchema[], extraBody: Record<string, unknown>, exposeReasoning?: boolean): AsyncIterable<StreamEvent>;
17
+ stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
17
18
  }
18
19
  export declare class QwenProvider extends OpenAIProvider {
19
20
  constructor(apiKey: string, model?: string);
20
- stream(messages: Message[], tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
21
+ stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
21
22
  }
22
23
  export declare class DeepSeekProvider extends OpenAIProvider {
23
24
  constructor(apiKey: string, model?: string);
24
- stream(messages: Message[], tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
25
+ stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
25
26
  }
26
27
  export declare class MiniMaxProvider extends OpenAIProvider {
27
28
  constructor(apiKey: string, model?: string);
28
- stream(messages: Message[], tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
29
+ stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
29
30
  }
30
31
  export declare class KimiProvider extends OpenAIProvider {
31
32
  constructor(apiKey: string, model?: string);
@@ -1,3 +1,4 @@
1
+ import { collectStreamMessage, toOpenAIMessages } from "./base.js";
1
2
  const DEEPSEEK_REASONERS = new Set(["deepseek-reasoner", "deepseek-r1"]);
2
3
  const MINIMAX_REASONERS = new Set(["MiniMax-M1", "minimax-m1"]);
3
4
  // OpenAI-compatible provider — works for OpenAI, Qwen (DashScope), DeepSeek, MiniMax, Kimi
@@ -13,11 +14,13 @@ export class OpenAIProvider {
13
14
  buildTools(tools) {
14
15
  return tools.map(t => ({ type: "function", function: { name: t.name, description: t.description, parameters: JSON.parse(t.parameters) } }));
15
16
  }
16
- async *streamInner(messages, tools, extraBody, exposeReasoning = false) {
17
- const msgs = messages.map(m => ({ role: m.role, content: m.content }));
17
+ async complete(context, tools, extensions) {
18
+ return collectStreamMessage(this.stream(context, tools, extensions));
19
+ }
20
+ async *streamInner(context, tools, extraBody, exposeReasoning = false) {
18
21
  const body = {
19
22
  model: this.model,
20
- messages: msgs,
23
+ messages: toOpenAIMessages(context),
21
24
  stream: true,
22
25
  ...(tools.length ? { tools: this.buildTools(tools) } : {}),
23
26
  ...extraBody,
@@ -76,16 +79,16 @@ export class OpenAIProvider {
76
79
  yield { type: "tool_call", id: tb.id, name: tb.name, arguments: args };
77
80
  }
78
81
  }
79
- async *stream(messages, tools, extensions) {
82
+ async *stream(context, tools, extensions) {
80
83
  const { expose_reasoning: _, exposeReasoning: __, ...passthrough } = extensions ?? {};
81
- yield* this.streamInner(messages, tools, passthrough);
84
+ yield* this.streamInner(context, tools, passthrough);
82
85
  }
83
86
  }
84
87
  export class QwenProvider extends OpenAIProvider {
85
88
  constructor(apiKey, model = "qwen-max") {
86
89
  super(apiKey, model, "https://dashscope.aliyuncs.com/compatible-mode/v1");
87
90
  }
88
- async *stream(messages, tools, extensions) {
91
+ async *stream(context, tools, extensions) {
89
92
  const enableThinking = Boolean(extensions?.enableThinking);
90
93
  const thinkingBudget = extensions?.thinkingBudget;
91
94
  const { enableThinking: _, thinkingBudget: __, expose_reasoning: ___, exposeReasoning: ____, ...passthrough } = extensions ?? {};
@@ -93,31 +96,31 @@ export class QwenProvider extends OpenAIProvider {
93
96
  ...passthrough,
94
97
  ...(enableThinking ? { enable_thinking: true, ...(thinkingBudget ? { thinking_budget: thinkingBudget } : {}) } : {}),
95
98
  };
96
- yield* this.streamInner(messages, tools, extra, enableThinking);
99
+ yield* this.streamInner(context, tools, extra, enableThinking);
97
100
  }
98
101
  }
99
102
  export class DeepSeekProvider extends OpenAIProvider {
100
103
  constructor(apiKey, model = "deepseek-chat") {
101
104
  super(apiKey, model, "https://api.deepseek.com/v1");
102
105
  }
103
- async *stream(messages, tools, extensions) {
106
+ async *stream(context, tools, extensions) {
104
107
  const exposeReasoning = Boolean(extensions?.exposeReasoning);
105
108
  const isReasoner = DEEPSEEK_REASONERS.has(this.model);
106
109
  const filteredTools = isReasoner ? [] : tools;
107
110
  const { exposeReasoning: _, expose_reasoning: __, ...passthrough } = extensions ?? {};
108
- yield* this.streamInner(messages, filteredTools, passthrough, exposeReasoning);
111
+ yield* this.streamInner(context, filteredTools, passthrough, exposeReasoning);
109
112
  }
110
113
  }
111
114
  export class MiniMaxProvider extends OpenAIProvider {
112
115
  constructor(apiKey, model = "MiniMax-Text-01") {
113
116
  super(apiKey, model, "https://api.minimax.chat/v1");
114
117
  }
115
- async *stream(messages, tools, extensions) {
118
+ async *stream(context, tools, extensions) {
116
119
  const exposeReasoning = Boolean(extensions?.exposeReasoning);
117
120
  const isReasoner = MINIMAX_REASONERS.has(this.model);
118
121
  const filteredTools = isReasoner ? [] : tools;
119
122
  const { exposeReasoning: _, expose_reasoning: __, ...passthrough } = extensions ?? {};
120
- yield* this.streamInner(messages, filteredTools, passthrough, exposeReasoning);
123
+ yield* this.streamInner(context, filteredTools, passthrough, exposeReasoning);
121
124
  }
122
125
  }
123
126
  export class KimiProvider extends OpenAIProvider {
@@ -0,0 +1,33 @@
1
+ import type { ToolCall, ToolSchema, StreamEvent } from "../types.js";
2
+ import type { RegisteredTool } from "../tools/index.js";
3
+ import type { DreamStore } from "../memory/index.js";
4
+ import type { KnowledgeSource } from "../knowledge/index.js";
5
+ import type { Governance } from "../governance.js";
6
+ export interface ToolSuspendEvent {
7
+ type: "tool_suspend";
8
+ callId: string;
9
+ name: string;
10
+ suspensionId: string;
11
+ payload?: unknown;
12
+ }
13
+ export interface RunContext {
14
+ agentId?: string;
15
+ skillContentMap?: Map<string, string>;
16
+ dreamStore?: DreamStore;
17
+ knowledgeSource?: KnowledgeSource;
18
+ governance?: Governance;
19
+ onToolSuspend?: (event: ToolSuspendEvent) => Promise<unknown> | unknown;
20
+ }
21
+ export interface ExecutionPlane {
22
+ register(...tools: RegisteredTool[]): this;
23
+ unregister(name: string): this;
24
+ schemas(): ToolSchema[];
25
+ executeAll(calls: ToolCall[], ctx: RunContext): AsyncIterable<StreamEvent>;
26
+ }
27
+ export declare class LocalExecutionPlane implements ExecutionPlane {
28
+ private tools;
29
+ register(...tools: RegisteredTool[]): this;
30
+ unregister(name: string): this;
31
+ schemas(): ToolSchema[];
32
+ executeAll(calls: ToolCall[], ctx: RunContext): AsyncIterable<StreamEvent>;
33
+ }