@deepstrike/sdk 0.1.1

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 ADDED
@@ -0,0 +1,296 @@
1
+ # DeepStrike Node.js SDK
2
+
3
+ Agent framework built on a Rust kernel. The kernel handles loop control, context compression, skill selection, and termination — the SDK handles all I/O.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @deepstrike/sdk
9
+ ```
10
+
11
+ Requires Node.js 18+. The Rust kernel is distributed as a pre-built native addon (`@deepstrike/core`).
12
+
13
+ ---
14
+
15
+ ## Quick start
16
+
17
+ ```typescript
18
+ import { Agent, AnthropicProvider, tool } from "@deepstrike/sdk"
19
+
20
+ const add = tool("add", "Add two numbers.", {
21
+ type: "object",
22
+ properties: { x: { type: "number" }, y: { type: "number" } },
23
+ required: ["x", "y"],
24
+ }, async ({ x, y }) => String((x as number) + (y as number)))
25
+
26
+ const agent = new Agent(
27
+ new AnthropicProvider("sk-..."),
28
+ { maxTokens: 32_000, maxTurns: 10 },
29
+ )
30
+ agent.register(add)
31
+
32
+ await agent.run("What is 2 + 3?")
33
+ ```
34
+
35
+ Streaming:
36
+
37
+ ```typescript
38
+ for await (const event of agent.runStreaming("Summarize README.md")) {
39
+ if (event.type === "text_delta") process.stdout.write(event.delta)
40
+ else if (event.type === "tool_call") console.log(`\n[→ ${event.name}]`)
41
+ else if (event.type === "done") console.log(`\ndone in ${event.iterations} turns (${event.status})`)
42
+ }
43
+ ```
44
+
45
+ ---
46
+
47
+ ## Architecture
48
+
49
+ ```
50
+ src/
51
+ ├── index.ts # Public exports
52
+ ├── agent.ts # Agent — top-level entry point
53
+ ├── types.ts # Shared type definitions
54
+ ├── providers/ # LLM adapters (HTTP + streaming)
55
+ ├── tools/ # tool() helper, executeTools, built-ins
56
+ ├── skills/ # SkillLoader — .md file loading
57
+ ├── memory/ # WorkingMemory + MemorySource/Extractor interfaces
58
+ ├── knowledge/ # KnowledgeSource interface
59
+ ├── harness/ # SinglePassHarness, EvalLoopHarness, QualityGate
60
+ ├── signals/ # RuntimeSignal, SignalSource, ScheduledPrompt
61
+ └── safety/ # PermissionManager
62
+ ```
63
+
64
+ The kernel (`@deepstrike/core`, Rust/NAPI) owns:
65
+ - `LoopStateMachine` — drives `call_llm → execute_tools → load_skills → done`
66
+ - `ContextEngine` — 5-partition context with pressure-based compression
67
+ - `Governance` — tool veto authority
68
+ - `SignalRouter` — external interrupt queue
69
+
70
+ ---
71
+
72
+ ## Providers
73
+
74
+ | Class | Backend | Notes |
75
+ |-------|---------|-------|
76
+ | `AnthropicProvider` | Anthropic API | Native SSE, `ThinkingDelta` support |
77
+ | `OpenAIProvider` | OpenAI API | SSE tool-call accumulation |
78
+ | `QwenProvider` | DashScope | `enable_thinking` via extensions |
79
+ | `DeepSeekProvider` | DeepSeek API | Reasoner models strip tools automatically |
80
+ | `MiniMaxProvider` | MiniMax API | M1 reasoning via `expose_reasoning` |
81
+ | `OllamaProvider` | Local Ollama | `http://localhost:11434` default |
82
+
83
+ ```typescript
84
+ import { AnthropicProvider } from "@deepstrike/sdk"
85
+
86
+ const provider = new AnthropicProvider("sk-...", "claude-opus-4-7", {
87
+ maxRetries: 5,
88
+ baseDelay: 2000,
89
+ })
90
+ ```
91
+
92
+ Thinking / reasoning:
93
+
94
+ ```typescript
95
+ for await (const event of agent.runStreaming("...", undefined, { enable_thinking: true })) {
96
+ if (event.type === "thinking_delta") process.stdout.write(event.delta)
97
+ }
98
+ ```
99
+
100
+ ---
101
+
102
+ ## Tools
103
+
104
+ ```typescript
105
+ import { tool, Agent } from "@deepstrike/sdk"
106
+
107
+ const search = tool("search", "Search the knowledge base.", {
108
+ type: "object",
109
+ properties: { query: { type: "string" }, topK: { type: "number" } },
110
+ required: ["query"],
111
+ }, async ({ query, topK }) => mySearch(query as string, topK as number))
112
+
113
+ agent.register(search)
114
+ agent.unregister("search")
115
+ agent.blockTool("bash") // governance veto — permanent for this agent instance
116
+ ```
117
+
118
+ Built-in tools: `readFile`.
119
+
120
+ ---
121
+
122
+ ## Skills
123
+
124
+ Skills are `.md` files with YAML frontmatter. The kernel selects and injects them automatically.
125
+
126
+ ```markdown
127
+ ---
128
+ name: debug
129
+ description: Step-by-step debugging guide
130
+ when_to_use: error, traceback, exception
131
+ effort: 2
132
+ estimated_tokens: 800
133
+ ---
134
+
135
+ ## Debug protocol
136
+ 1. Read the traceback carefully ...
137
+ ```
138
+
139
+ ```typescript
140
+ import { Agent, SkillLoader } from "@deepstrike/sdk"
141
+
142
+ const loader = new SkillLoader("~/.deepstrike/skills")
143
+ const agent = new Agent(provider, { maxTokens: 32_000, maxTurns: 10, skillLoader: loader })
144
+ ```
145
+
146
+ ---
147
+
148
+ ## Memory
149
+
150
+ Implement `MemorySource` to inject persistent context before a run, and `MemoryExtractor` to persist what was learned after.
151
+
152
+ ```typescript
153
+ import type { MemorySource, MemoryExtractor } from "@deepstrike/sdk"
154
+
155
+ class MyMemorySource implements MemorySource {
156
+ async load(goal: string): Promise<string[]> {
157
+ return db.query(goal)
158
+ }
159
+ }
160
+
161
+ class MyMemoryExtractor implements MemoryExtractor {
162
+ async extract(goal: string, finalText: string, turns: number): Promise<void> {
163
+ await db.save(goal, finalText)
164
+ }
165
+ }
166
+
167
+ const agent = new Agent(provider, {
168
+ maxTokens: 32_000,
169
+ maxTurns: 10,
170
+ memorySource: new MyMemorySource(),
171
+ memoryExtractor: new MyMemoryExtractor(),
172
+ })
173
+ ```
174
+
175
+ `WorkingMemory` is an in-process scratch pad for within-run state:
176
+
177
+ ```typescript
178
+ import { WorkingMemory } from "@deepstrike/sdk"
179
+
180
+ const mem = new WorkingMemory()
181
+ mem.set("step", 1)
182
+ mem.get("step") // 1
183
+ ```
184
+
185
+ ---
186
+
187
+ ## Knowledge
188
+
189
+ Inject run-scoped evidence (RAG results, API responses) without polluting long-term memory:
190
+
191
+ ```typescript
192
+ import type { KnowledgeSource } from "@deepstrike/sdk"
193
+
194
+ class VectorSearch implements KnowledgeSource {
195
+ async retrieve(goal: string, topK = 5): Promise<string[]> {
196
+ return vectorDb.search(goal, topK)
197
+ }
198
+ }
199
+
200
+ const agent = new Agent(provider, {
201
+ maxTokens: 32_000,
202
+ maxTurns: 10,
203
+ knowledgeSource: new VectorSearch(),
204
+ })
205
+ ```
206
+
207
+ Snippets are prepended as a system message before the first LLM call.
208
+
209
+ ---
210
+
211
+ ## Harness
212
+
213
+ Control how runs are attempted:
214
+
215
+ ```typescript
216
+ import { Agent, SinglePassHarness, EvalLoopHarness, HarnessRequest } from "@deepstrike/sdk"
217
+ import type { QualityGate, HarnessOutcome } from "@deepstrike/sdk"
218
+
219
+ // Single pass
220
+ const harness = new SinglePassHarness(agent)
221
+ const outcome = await harness.run({ goal: "Write a haiku" })
222
+
223
+ // Eval loop — retry until QualityGate passes (max 3 attempts)
224
+ class LengthGate implements QualityGate {
225
+ async evaluate(request: HarnessRequest, outcome: HarnessOutcome): Promise<boolean> {
226
+ return outcome.result.length > 50
227
+ }
228
+ }
229
+
230
+ const evalHarness = new EvalLoopHarness(agent, new LengthGate(), 3)
231
+ const result = await evalHarness.run({ goal: "Write a haiku" })
232
+ console.log(result.passed, result.iterations)
233
+ ```
234
+
235
+ ---
236
+
237
+ ## Signals & interrupts
238
+
239
+ ```typescript
240
+ import { ScheduledPrompt } from "@deepstrike/sdk"
241
+ import type { SignalSource, RuntimeSignal } from "@deepstrike/sdk"
242
+
243
+ // Interrupt a running agent
244
+ setTimeout(() => agent.interrupt(), 30_000)
245
+
246
+ // Convert a scheduled prompt to a RuntimeSignal
247
+ const prompt = new ScheduledPrompt("Daily standup summary", 1_700_000_000_000)
248
+ const signal = prompt.toSignal()
249
+ // signal.kind === "scheduled"
250
+ // signal.payload === { goal: "Daily standup summary", criteria: [], runAtMs: ... }
251
+ ```
252
+
253
+ Implement `SignalSource` to feed signals from any external source:
254
+
255
+ ```typescript
256
+ class WebhookSource implements SignalSource {
257
+ async nextSignal(): Promise<RuntimeSignal | null> {
258
+ const event = await webhookQueue.get()
259
+ return { kind: "external", payload: event }
260
+ }
261
+ }
262
+ ```
263
+
264
+ ---
265
+
266
+ ## Permissions
267
+
268
+ ```typescript
269
+ import { PermissionManager, PermissionMode } from "@deepstrike/sdk"
270
+
271
+ const pm = new PermissionManager(PermissionMode.DEFAULT)
272
+ pm.grant("bash", "execute")
273
+ pm.grant("fs", "*") // wildcard: all actions on fs
274
+ pm.revoke("bash", "execute")
275
+
276
+ const decision = pm.evaluate("bash", "execute")
277
+ decision.allowed // boolean
278
+ decision.reason // string
279
+ ```
280
+
281
+ Modes: `DEFAULT` (evaluate grants), `PLAN` (block all), `AUTO` (allow all).
282
+
283
+ ---
284
+
285
+ ## Stream events
286
+
287
+ | Event type | Fields |
288
+ |------------|--------|
289
+ | `text_delta` | `delta: string` |
290
+ | `thinking_delta` | `delta: string` |
291
+ | `tool_call` | `id, name, arguments` |
292
+ | `tool_result` | `callId, name, content, isError` |
293
+ | `done` | `iterations, totalTokens, status` |
294
+ | `error` | `message: string` |
295
+
296
+ `status` mirrors the kernel termination reason: `completed` / `max_turns` / `token_budget` / `timeout` / `user_abort` / `error`.
@@ -0,0 +1,62 @@
1
+ import type { LLMProvider, StreamEvent } from "./types.js";
2
+ import type { RegisteredTool } from "./tools/index.js";
3
+ import type { DreamStore, DreamResult } from "./memory/protocols.js";
4
+ import type { KnowledgeSource } from "./knowledge/source.js";
5
+ import type { SignalSource } from "./signals/types.js";
6
+ export interface AgentOptions {
7
+ maxTokens: number;
8
+ maxTurns?: number;
9
+ timeoutMs?: number;
10
+ extensions?: Record<string, unknown>;
11
+ /**
12
+ * Directory containing skill `.md` files. The kernel will auto-inject a
13
+ * `skill` meta-tool so the model can load any skill by name on demand.
14
+ */
15
+ skillDir?: string;
16
+ knowledgeSource?: KnowledgeSource;
17
+ signalSource?: SignalSource;
18
+ /** Backing store for the idle dreaming pipeline. Required to call `Agent.dream()`. */
19
+ dreamStore?: DreamStore;
20
+ /**
21
+ * Stable identifier for this agent. Required to enable in-session memory retrieval
22
+ * when `dreamStore` is configured — the kernel injects a `memory` meta-tool and
23
+ * the SDK calls `dreamStore.search(agentId, query)` on demand.
24
+ */
25
+ agentId?: string;
26
+ /** Governance instance from @deepstrike/core for permission checks on tool calls. */
27
+ governance?: {
28
+ evaluate(toolName: string, args: string): {
29
+ kind: string;
30
+ reason?: string;
31
+ };
32
+ };
33
+ }
34
+ export declare class Agent {
35
+ private readonly provider;
36
+ private readonly options;
37
+ private tools;
38
+ private blockedTools;
39
+ private extensions;
40
+ private skillDir?;
41
+ private knowledgeSource?;
42
+ private signalSource?;
43
+ private dreamStore?;
44
+ private interrupted;
45
+ private pendingInterrupt;
46
+ constructor(provider: LLMProvider, options: AgentOptions);
47
+ interrupt(): void;
48
+ register(...tools: RegisteredTool[]): this;
49
+ unregister(name: string): this;
50
+ blockTool(name: string): this;
51
+ run(goal: string, criteria?: string[], extensions?: Record<string, unknown>): Promise<string>;
52
+ runStreaming(goal: string, criteria?: string[], extensions?: Record<string, unknown>): AsyncIterable<StreamEvent>;
53
+ /**
54
+ * Trigger an idle dreaming cycle for the given agent.
55
+ *
56
+ * Phase 1 — kernel rule-based analysis + LLM prompt assembly (kernel, pure computation)
57
+ * Phase 2 — LLM synthesis call (SDK, I/O here)
58
+ * Phase 3 — kernel parses + curates results (kernel, pure computation)
59
+ * Phase 4 — commit delta to DreamStore (SDK, I/O here)
60
+ */
61
+ dream(agentId: string, nowMs?: number): Promise<DreamResult>;
62
+ }
package/dist/agent.js ADDED
@@ -0,0 +1,321 @@
1
+ import { executeTools } from "./tools/index.js";
2
+ import { readSkillFile, scanSkillDir } from "./skills/loader.js";
3
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
4
+ async function loadKernel() {
5
+ return import("@deepstrike/core");
6
+ }
7
+ export class Agent {
8
+ provider;
9
+ options;
10
+ tools = new Map();
11
+ blockedTools = new Set();
12
+ extensions;
13
+ skillDir;
14
+ knowledgeSource;
15
+ signalSource;
16
+ dreamStore;
17
+ interrupted = false;
18
+ pendingInterrupt = false;
19
+ constructor(provider, options) {
20
+ this.provider = provider;
21
+ this.options = options;
22
+ this.extensions = options.extensions ?? {};
23
+ this.skillDir = options.skillDir;
24
+ this.knowledgeSource = options.knowledgeSource;
25
+ this.signalSource = options.signalSource;
26
+ this.dreamStore = options.dreamStore;
27
+ }
28
+ interrupt() {
29
+ this.interrupted = true;
30
+ }
31
+ register(...tools) {
32
+ for (const t of tools)
33
+ this.tools.set(t.schema.name, t);
34
+ return this;
35
+ }
36
+ unregister(name) {
37
+ this.tools.delete(name);
38
+ return this;
39
+ }
40
+ blockTool(name) {
41
+ this.blockedTools.add(name);
42
+ return this;
43
+ }
44
+ async run(goal, criteria, extensions) {
45
+ let result;
46
+ for await (const evt of this.runStreaming(goal, criteria, extensions)) {
47
+ if (evt.type === "done")
48
+ result = evt;
49
+ }
50
+ return result ? `done in ${result.iterations} turns (${result.status})` : "done";
51
+ }
52
+ async *runStreaming(goal, criteria, extensions) {
53
+ this.interrupted = false;
54
+ this.pendingInterrupt = false;
55
+ const kernel = await loadKernel();
56
+ const ext = { ...this.extensions, ...(extensions ?? {}) };
57
+ const sm = new kernel.LoopStateMachine({
58
+ maxTokens: this.options.maxTokens,
59
+ maxTurns: this.options.maxTurns ?? 25,
60
+ timeoutMs: this.options.timeoutMs,
61
+ });
62
+ // Create a per-run SignalRouter so dedup state doesn't leak across runs.
63
+ const router = new kernel.SignalRouter(256);
64
+ const toolSchemas = Array.from(this.tools.values()).map(t => t.schema);
65
+ sm.setTools(toolSchemas);
66
+ // Scan skill directory and register metadata with the kernel.
67
+ // The kernel will auto-inject the `skill` meta-tool into every CallLLM action.
68
+ if (this.skillDir) {
69
+ const skillMetas = await scanSkillDir(this.skillDir);
70
+ sm.setAvailableSkills(skillMetas.map(m => ({
71
+ name: m.name,
72
+ description: m.description,
73
+ whenToUse: m.whenToUse,
74
+ effort: m.effort,
75
+ estimatedTokens: m.estimatedTokens ?? 0,
76
+ })));
77
+ }
78
+ // Enable the memory meta-tool when both dreamStore and agentId are provided.
79
+ if (this.dreamStore && this.options.agentId) {
80
+ sm.setMemoryEnabled(true);
81
+ }
82
+ // Enable the knowledge meta-tool when a KnowledgeSource is configured.
83
+ if (this.knowledgeSource) {
84
+ sm.setKnowledgeEnabled(true);
85
+ }
86
+ let action = sm.start({ goal, criteria: criteria ?? [] });
87
+ let finalText = "";
88
+ while (!sm.isTerminal()) {
89
+ if (this.interrupted) {
90
+ action = sm.feedTimeout();
91
+ break;
92
+ }
93
+ if (this.pendingInterrupt) {
94
+ this.pendingInterrupt = false;
95
+ action = sm.feedTimeout();
96
+ break;
97
+ }
98
+ // Poll signal source and route through kernel SignalRouter
99
+ if (this.signalSource) {
100
+ const sig = await this.signalSource.nextSignal();
101
+ if (sig) {
102
+ const kernelSig = {
103
+ id: crypto.randomUUID(),
104
+ source: sig.source ?? "custom",
105
+ signalType: sig.signalType ?? "event",
106
+ urgency: sig.urgency ?? (sig.kind === "interrupt" ? "critical" : "normal"),
107
+ summary: String(sig.payload?.goal ?? sig.kind),
108
+ payload: JSON.stringify(sig.payload ?? {}),
109
+ dedupeKey: sig.dedupeKey ?? null,
110
+ timestampMs: Date.now(),
111
+ };
112
+ const disposition = router.ingest(kernelSig, action.kind === "execute_tools");
113
+ if (disposition === "interrupt_now") {
114
+ action = sm.feedTimeout();
115
+ break;
116
+ }
117
+ if (disposition === "interrupt") {
118
+ this.pendingInterrupt = true;
119
+ }
120
+ // "queue" → buffered; "observe" / "ignore" / "dropped" → no action
121
+ }
122
+ }
123
+ sm.takeObservations();
124
+ if (action.kind === "call_llm") {
125
+ finalText = "";
126
+ const finalToolCalls = [];
127
+ const messages = (action.messages ?? []);
128
+ const tools = (action.tools ?? []);
129
+ try {
130
+ for await (const evt of this.provider.stream(messages, tools, Object.keys(ext).length ? ext : undefined)) {
131
+ yield evt;
132
+ if (evt.type === "text_delta")
133
+ finalText += evt.delta;
134
+ else if (evt.type === "tool_call") {
135
+ const tc = evt;
136
+ finalToolCalls.push({ id: tc.id, name: tc.name, arguments: JSON.stringify(tc.arguments) });
137
+ }
138
+ }
139
+ }
140
+ catch (err) {
141
+ yield { type: "error", message: String(err) };
142
+ action = sm.feedTimeout();
143
+ break;
144
+ }
145
+ action = sm.feedLlmResponse({ role: "assistant", content: finalText, toolCalls: finalToolCalls });
146
+ }
147
+ else if (action.kind === "execute_tools") {
148
+ const allCalls = action.calls ?? [];
149
+ // Governance check: blocked tools + GovernancePipeline
150
+ const permittedCalls = [];
151
+ for (const c of allCalls) {
152
+ if (this.blockedTools.has(c.name)) {
153
+ yield { type: "error", message: `tool blocked: ${c.name}` };
154
+ continue;
155
+ }
156
+ if (this.options.governance) {
157
+ const verdict = this.options.governance.evaluate(c.name, c.arguments);
158
+ if (verdict.kind === "deny") {
159
+ yield { type: "error", message: `permission denied: ${c.name} — ${verdict.reason}` };
160
+ continue;
161
+ }
162
+ if (verdict.kind === "ask_user") {
163
+ yield { type: "permission_request", callId: c.id, toolName: c.name, arguments: c.arguments, reason: verdict.reason };
164
+ continue;
165
+ }
166
+ }
167
+ permittedCalls.push(c);
168
+ }
169
+ const calls = permittedCalls;
170
+ // Intercept `skill` meta-tool calls: read file, return content as tool result.
171
+ const skillCalls = calls.filter((c) => c.name === "skill");
172
+ // Intercept `memory` meta-tool calls: search DreamStore, return entries as tool result.
173
+ const memoryCalls = calls.filter((c) => c.name === "memory");
174
+ // Intercept `knowledge` meta-tool calls: retrieve from KnowledgeSource.
175
+ const knowledgeCalls = calls.filter((c) => c.name === "knowledge");
176
+ const regularCalls = calls.filter((c) => c.name !== "skill" && c.name !== "memory" && c.name !== "knowledge");
177
+ const skillResults = this.skillDir
178
+ ? await Promise.all(skillCalls.map(async (c) => {
179
+ const args = tryParseJson(c.arguments);
180
+ const name = String(args?.name ?? "");
181
+ const content = await readSkillFile(this.skillDir, name);
182
+ const output = content ?? `Skill "${name}" not found.`;
183
+ return { callId: c.id, name: c.name, output, isError: !content };
184
+ }))
185
+ : skillCalls.map((c) => ({ callId: c.id, name: c.name, output: "No skill directory configured.", isError: true }));
186
+ const memoryResults = (this.dreamStore && this.options.agentId)
187
+ ? await Promise.all(memoryCalls.map(async (c) => {
188
+ const args = tryParseJson(c.arguments);
189
+ const query = String(args?.query ?? "");
190
+ const topK = typeof args?.top_k === "number" ? args.top_k : 5;
191
+ const entries = await this.dreamStore.search(this.options.agentId, query, topK);
192
+ const output = entries.length
193
+ ? entries.map(e => `[score=${e.score.toFixed(3)}] ${e.text}`).join("\n---\n")
194
+ : "No relevant memories found.";
195
+ return { callId: c.id, name: c.name, output, isError: false };
196
+ }))
197
+ : memoryCalls.map((c) => ({ callId: c.id, name: c.name, output: "Memory retrieval not configured.", isError: true }));
198
+ const knowledgeResults = this.knowledgeSource
199
+ ? await Promise.all(knowledgeCalls.map(async (c) => {
200
+ const args = tryParseJson(c.arguments);
201
+ const query = String(args?.query ?? "");
202
+ const topK = typeof args?.top_k === "number" ? args.top_k : 5;
203
+ const snippets = await this.knowledgeSource.retrieve(query, topK);
204
+ const output = snippets.length ? snippets.join("\n---\n") : "No relevant knowledge found.";
205
+ return { callId: c.id, name: c.name, output, isError: false };
206
+ }))
207
+ : knowledgeCalls.map((c) => ({ callId: c.id, name: c.name, output: "Knowledge source not configured.", isError: true }));
208
+ // Yield all meta-tool results
209
+ for (const r of [...skillResults, ...memoryResults, ...knowledgeResults])
210
+ yield { type: "tool_result", callId: r.callId, name: r.name, content: r.output, isError: r.isError };
211
+ const results = await executeTools(regularCalls, this.tools);
212
+ for (const r of results) {
213
+ const name = regularCalls.find(c => c.id === r.callId)?.name ?? "";
214
+ yield { type: "tool_result", callId: r.callId, name, content: r.output, isError: r.isError };
215
+ }
216
+ action = sm.feedToolResults([
217
+ ...skillResults,
218
+ ...memoryResults,
219
+ ...knowledgeResults,
220
+ ...results.map(r => ({ callId: r.callId, output: r.output, isError: r.isError })),
221
+ ]);
222
+ }
223
+ else if (action.kind === "done") {
224
+ break;
225
+ }
226
+ }
227
+ const result = action.result;
228
+ yield {
229
+ type: "done",
230
+ iterations: result?.turnsUsed ?? 0,
231
+ totalTokens: Number(result?.totalTokensUsed ?? 0),
232
+ status: result?.termination ?? "error",
233
+ };
234
+ }
235
+ /**
236
+ * Trigger an idle dreaming cycle for the given agent.
237
+ *
238
+ * Phase 1 — kernel rule-based analysis + LLM prompt assembly (kernel, pure computation)
239
+ * Phase 2 — LLM synthesis call (SDK, I/O here)
240
+ * Phase 3 — kernel parses + curates results (kernel, pure computation)
241
+ * Phase 4 — commit delta to DreamStore (SDK, I/O here)
242
+ */
243
+ async dream(agentId, nowMs = Date.now()) {
244
+ if (!this.dreamStore)
245
+ throw new Error("dreamStore not configured on AgentOptions");
246
+ const kernel = await loadKernel();
247
+ const sessions = await this.dreamStore.loadSessions(agentId);
248
+ const existingMemories = await this.dreamStore.loadMemories(agentId);
249
+ if (!sessions.length) {
250
+ return { sessionsProcessed: 0, insightsExtracted: 0, entriesAdded: 0, entriesRemoved: 0 };
251
+ }
252
+ const kernelSessions = sessions.map(s => ({
253
+ sessionId: s.sessionId,
254
+ agentId: s.agentId,
255
+ messages: s.messages.map(m => ({
256
+ role: m.role,
257
+ content: m.content,
258
+ tokenCount: m.tokenCount,
259
+ toolCalls: (m.toolCalls ?? []).map(tc => ({
260
+ id: tc.id, name: tc.name, arguments: tc.arguments,
261
+ })),
262
+ })),
263
+ metadata: JSON.stringify(s.metadata ?? null),
264
+ createdAtMs: s.createdAtMs,
265
+ updatedAtMs: s.updatedAtMs,
266
+ }));
267
+ const kernelMemories = existingMemories.map(e => ({
268
+ text: e.text,
269
+ score: e.score,
270
+ metadata: JSON.stringify(e.metadata ?? null),
271
+ }));
272
+ const pipeline = new kernel.IdlePipeline(agentId);
273
+ const action1 = pipeline.feedTrigger(kernelSessions, kernelMemories, nowMs);
274
+ if (action1.kind === "noop") {
275
+ return { sessionsProcessed: 0, insightsExtracted: 0, entriesAdded: 0, entriesRemoved: 0 };
276
+ }
277
+ if (action1.kind !== "synthesize_insights") {
278
+ throw new Error(`unexpected action after feedTrigger: ${action1.kind}`);
279
+ }
280
+ let synthesisText = "";
281
+ for await (const evt of this.provider.stream((action1.messages ?? []), [], undefined)) {
282
+ if (evt.type === "text_delta")
283
+ synthesisText += evt.delta;
284
+ }
285
+ const action2 = pipeline.feedSynthesisResult(synthesisText);
286
+ if (action2.kind !== "commit_memories") {
287
+ throw new Error(`unexpected action after feedSynthesisResult: ${action2.kind}`);
288
+ }
289
+ const cr = action2.curationResult;
290
+ const rr = action2.runResult;
291
+ const dsResult = {
292
+ toAdd: (cr.toAdd ?? []).map((e) => ({
293
+ text: e.text,
294
+ score: e.score,
295
+ metadata: tryParseJson(e.metadata),
296
+ })),
297
+ toRemoveIndices: (cr.toRemoveIndices ?? []).map(Number),
298
+ stats: {
299
+ insightsProcessed: cr.stats?.insightsProcessed ?? 0,
300
+ duplicatesRemoved: cr.stats?.duplicatesRemoved ?? 0,
301
+ conflictsResolved: cr.stats?.conflictsResolved ?? 0,
302
+ entriesAdded: cr.stats?.entriesAdded ?? 0,
303
+ },
304
+ };
305
+ await this.dreamStore.commit(agentId, dsResult, existingMemories);
306
+ return {
307
+ sessionsProcessed: rr.sessionsProcessed,
308
+ insightsExtracted: rr.insightsExtracted,
309
+ entriesAdded: cr.stats?.entriesAdded ?? 0,
310
+ entriesRemoved: (cr.toRemoveIndices ?? []).length,
311
+ };
312
+ }
313
+ }
314
+ function tryParseJson(s) {
315
+ try {
316
+ return JSON.parse(s);
317
+ }
318
+ catch {
319
+ return null;
320
+ }
321
+ }