@deepstrike/sdk 0.1.13 → 0.1.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.
Files changed (55) hide show
  1. package/README.md +77 -51
  2. package/dist/collaboration/harness.js +6 -1
  3. package/dist/collaboration/modes/creator-verifier.d.ts +2 -2
  4. package/dist/collaboration/modes/creator-verifier.js +2 -2
  5. package/dist/collaboration/pool.d.ts +4 -35
  6. package/dist/collaboration/pool.js +18 -44
  7. package/dist/harness/harness.d.ts +9 -7
  8. package/dist/harness/harness.js +19 -22
  9. package/dist/index.d.ts +18 -6
  10. package/dist/index.js +14 -2
  11. package/dist/kernel.d.ts +1 -0
  12. package/dist/kernel.js +10 -1
  13. package/dist/memory/protocols.d.ts +0 -4
  14. package/dist/providers/anthropic.d.ts +4 -2
  15. package/dist/providers/anthropic.js +34 -10
  16. package/dist/providers/deepseek.d.ts +3 -2
  17. package/dist/providers/deepseek.js +9 -0
  18. package/dist/providers/gemini.d.ts +2 -1
  19. package/dist/providers/gemini.js +11 -0
  20. package/dist/providers/kimi.d.ts +3 -1
  21. package/dist/providers/kimi.js +10 -0
  22. package/dist/providers/minimax.d.ts +3 -1
  23. package/dist/providers/minimax.js +8 -0
  24. package/dist/providers/ollama.d.ts +2 -1
  25. package/dist/providers/ollama.js +22 -0
  26. package/dist/providers/openai-chat.d.ts +1 -1
  27. package/dist/providers/openai-chat.js +5 -7
  28. package/dist/providers/openai-responses.d.ts +1 -0
  29. package/dist/providers/openai-responses.js +13 -0
  30. package/dist/providers/openai.d.ts +4 -1
  31. package/dist/providers/openai.js +28 -0
  32. package/dist/providers/profiles.d.ts +502 -6
  33. package/dist/providers/profiles.js +225 -82
  34. package/dist/providers/qwen.d.ts +2 -1
  35. package/dist/providers/qwen.js +15 -0
  36. package/dist/runtime/credential-vault.d.ts +18 -0
  37. package/dist/runtime/credential-vault.js +33 -0
  38. package/dist/runtime/execution-plane.d.ts +38 -0
  39. package/dist/runtime/execution-plane.js +146 -0
  40. package/dist/runtime/mcp-proxy-plane.d.ts +51 -0
  41. package/dist/runtime/mcp-proxy-plane.js +204 -0
  42. package/dist/runtime/process-sandbox-plane.d.ts +32 -0
  43. package/dist/runtime/process-sandbox-plane.js +109 -0
  44. package/dist/runtime/provider-replay.d.ts +7 -0
  45. package/dist/runtime/provider-replay.js +18 -0
  46. package/dist/runtime/remote-vpc-plane.d.ts +48 -0
  47. package/dist/runtime/remote-vpc-plane.js +82 -0
  48. package/dist/runtime/runner.d.ts +49 -0
  49. package/dist/runtime/runner.js +368 -0
  50. package/dist/runtime/session-log.d.ts +68 -0
  51. package/dist/runtime/session-log.js +84 -0
  52. package/dist/types.d.ts +27 -0
  53. package/package.json +3 -2
  54. package/dist/agent.d.ts +0 -90
  55. package/dist/agent.js +0 -499
@@ -0,0 +1,368 @@
1
+ import { getKernel } from "../kernel.js";
2
+ import { peekProviderReplay, seedProviderReplayFromEvents } from "./provider-replay.js";
3
+ export class RuntimeRunner {
4
+ opts;
5
+ interrupted = false;
6
+ constructor(opts) {
7
+ this.opts = opts;
8
+ }
9
+ interrupt() { this.interrupted = true; }
10
+ async *run(req) {
11
+ const prior = await this.opts.sessionLog.read(req.sessionId);
12
+ const midRun = isMidRun(prior);
13
+ if (!midRun) {
14
+ await this.opts.sessionLog.append(req.sessionId, {
15
+ kind: "run_started",
16
+ run_id: crypto.randomUUID(),
17
+ goal: req.goal,
18
+ criteria: req.criteria ?? [],
19
+ agent_id: this.opts.agentId,
20
+ system_prompt: this.opts.systemPrompt,
21
+ });
22
+ }
23
+ yield* this.execute(req.sessionId, req.goal, req.criteria ?? [], req.extensions, prior.length > 0 ? prior : undefined, midRun);
24
+ }
25
+ async *wake(sessionId, extensions) {
26
+ const events = await this.opts.sessionLog.read(sessionId);
27
+ if (events.some(e => e.event.kind === "run_terminal"))
28
+ return;
29
+ const startEntry = [...events].reverse().find(e => e.event.kind === "run_started");
30
+ if (!startEntry)
31
+ throw new Error(`No run_started event for session: ${sessionId}`);
32
+ const start = startEntry.event;
33
+ yield* this.execute(sessionId, start.goal, start.criteria, extensions, events, true);
34
+ }
35
+ async dream(agentId, nowMs = Date.now()) {
36
+ if (!this.opts.dreamStore)
37
+ throw new Error("dreamStore not configured");
38
+ const kernel = getKernel();
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 = getKernel();
97
+ const ext = { ...this.opts.extensions, ...(extensions ?? {}) };
98
+ const providerState = this.opts.provider.createRunState?.();
99
+ let nextCompressedArchiveStart = nextArchivedSeqStart(priorEvents);
100
+ // Three-layer policy merge: explicit RuntimeOptions > provider.runtimePolicy() > defaults
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.skillDir) {
120
+ const { scanSkillDir } = await import("../skills/loader.js");
121
+ const metas = await scanSkillDir(this.opts.skillDir);
122
+ sm.setAvailableSkills(metas.map((m) => ({
123
+ name: m.name, description: m.description, whenToUse: m.whenToUse,
124
+ effort: m.effort, estimatedTokens: m.estimatedTokens ?? 0,
125
+ })));
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
+ seedProviderReplayFromEvents(this.opts.provider, priorEvents);
133
+ sm.preloadHistory(replayMessages(priorEvents));
134
+ }
135
+ const sessionStart = Date.now();
136
+ let action = resumeMidRun
137
+ ? sm.resumeAfterPreload()
138
+ : sm.start({ goal, criteria });
139
+ while (!sm.isTerminal()) {
140
+ nextCompressedArchiveStart = await this.appendObservations(sessionId, sm, nextCompressedArchiveStart);
141
+ if (this.interrupted) {
142
+ action = sm.feedTimeout();
143
+ break;
144
+ }
145
+ if (this.opts.signalSource) {
146
+ const sig = await this.opts.signalSource.nextSignal();
147
+ if (sig) {
148
+ const kernelSig = {
149
+ id: crypto.randomUUID(),
150
+ source: sig.source ?? "custom",
151
+ signalType: sig.signalType ?? "event",
152
+ urgency: sig.urgency ?? "normal",
153
+ summary: String(sig.payload?.goal ?? sig.kind ?? "signal"),
154
+ payload: JSON.stringify(sig.payload ?? {}),
155
+ dedupeKey: sig.dedupeKey,
156
+ timestampMs: Date.now(),
157
+ };
158
+ const disposition = router.ingest(kernelSig, action.kind === "execute_tools");
159
+ if (disposition === "interrupt_now") {
160
+ action = sm.feedTimeout();
161
+ break;
162
+ }
163
+ }
164
+ }
165
+ let queued = router.next();
166
+ while (queued) {
167
+ if (queued.urgency === "critical") {
168
+ action = sm.feedTimeout();
169
+ break;
170
+ }
171
+ queued = router.next();
172
+ }
173
+ if (sm.isTerminal())
174
+ break;
175
+ if (action.kind === "call_llm") {
176
+ const finalToolCalls = [];
177
+ let finalText = "";
178
+ const context = action.context;
179
+ const tools = (action.tools ?? []);
180
+ let turnTokens = 0;
181
+ try {
182
+ for await (const evt of this.opts.provider.stream(context, tools, Object.keys(ext).length ? ext : undefined, providerState)) {
183
+ if (evt.type === "usage") {
184
+ turnTokens = evt.totalTokens;
185
+ continue;
186
+ }
187
+ yield evt;
188
+ if (evt.type === "text_delta")
189
+ finalText += evt.delta;
190
+ else if (evt.type === "tool_call") {
191
+ const tc = evt;
192
+ finalToolCalls.push({ id: tc.id, name: tc.name, arguments: JSON.stringify(tc.arguments) });
193
+ }
194
+ }
195
+ }
196
+ catch (err) {
197
+ yield { type: "error", message: String(err) };
198
+ action = sm.feedTimeout();
199
+ break;
200
+ }
201
+ action = sm.feedLlmResponse({
202
+ role: "assistant",
203
+ content: finalText,
204
+ toolCalls: finalToolCalls,
205
+ tokenCount: turnTokens || undefined,
206
+ });
207
+ const providerReplay = peekProviderReplay(this.opts.provider, finalText, finalToolCalls);
208
+ await this.opts.sessionLog.append(sessionId, {
209
+ kind: "llm_completed",
210
+ turn: sm.turn,
211
+ content: finalText,
212
+ token_count: turnTokens || undefined,
213
+ tool_calls: finalToolCalls,
214
+ ...(providerReplay ? { provider_replay: providerReplay } : {}),
215
+ });
216
+ }
217
+ else if (action.kind === "execute_tools") {
218
+ const allCalls = action.calls ?? [];
219
+ await this.opts.sessionLog.append(sessionId, { kind: "tool_requested", turn: sm.turn, calls: allCalls });
220
+ const runCtx = {
221
+ agentId: this.opts.agentId,
222
+ skillDir: this.opts.skillDir,
223
+ dreamStore: this.opts.dreamStore,
224
+ knowledgeSource: this.opts.knowledgeSource,
225
+ governance: this.opts.governance,
226
+ onToolSuspend: this.opts.onToolSuspend,
227
+ };
228
+ const toolResults = [];
229
+ for await (const evt of this.opts.executionPlane.executeAll(allCalls, runCtx)) {
230
+ yield evt;
231
+ if (evt.type === "tool_result") {
232
+ const tre = evt;
233
+ toolResults.push({ callId: tre.callId, output: tre.content, isError: tre.isError });
234
+ }
235
+ }
236
+ await this.opts.sessionLog.append(sessionId, {
237
+ kind: "tool_completed",
238
+ turn: sm.turn,
239
+ results: toolResults.map(r => ({
240
+ call_id: r.callId,
241
+ output: r.output,
242
+ is_error: r.isError,
243
+ token_count: r.tokenCount,
244
+ })),
245
+ });
246
+ action = sm.feedToolResults(toolResults);
247
+ }
248
+ else if (action.kind === "done") {
249
+ break;
250
+ }
251
+ }
252
+ const result = action.result;
253
+ const status = result?.termination ?? "error";
254
+ const turnsUsed = result ? Math.max(1, result.turnsUsed) : 0;
255
+ const totalTokens = result?.totalTokensUsed ? Number(result.totalTokensUsed) : 0;
256
+ nextCompressedArchiveStart = await this.appendObservations(sessionId, sm, nextCompressedArchiveStart);
257
+ await this.opts.sessionLog.append(sessionId, { kind: "run_terminal", reason: status, turns_used: turnsUsed, total_tokens: totalTokens });
258
+ if (this.opts.dreamStore && this.opts.agentId) {
259
+ const newMsgs = sm.drainNewMessages().map(m => ({
260
+ role: m.role,
261
+ content: m.content,
262
+ contentParts: m.contentParts,
263
+ tokenCount: m.tokenCount,
264
+ toolCalls: m.toolCalls?.length ? m.toolCalls : undefined,
265
+ }));
266
+ if (newMsgs.length > 0) {
267
+ try {
268
+ await this.opts.dreamStore.saveSession({
269
+ sessionId: crypto.randomUUID(),
270
+ agentId: this.opts.agentId,
271
+ messages: newMsgs,
272
+ metadata: null,
273
+ createdAtMs: sessionStart,
274
+ updatedAtMs: Date.now(),
275
+ });
276
+ }
277
+ catch { /* non-fatal */ }
278
+ }
279
+ }
280
+ yield { type: "done", iterations: turnsUsed, totalTokens, status };
281
+ }
282
+ async appendObservations(sessionId, sm, nextArchiveStart) {
283
+ const observations = sm.takeObservations();
284
+ for (const obs of observations) {
285
+ if (obs.kind !== "compressed")
286
+ continue;
287
+ const latest = await this.opts.sessionLog.latestSeq(sessionId);
288
+ if (latest < nextArchiveStart)
289
+ continue;
290
+ const end = latest;
291
+ const compressedSeq = await this.opts.sessionLog.append(sessionId, {
292
+ kind: "compressed",
293
+ turn: sm.turn,
294
+ archived_seq_range: [nextArchiveStart, end],
295
+ });
296
+ nextArchiveStart = compressedSeq + 1;
297
+ }
298
+ return nextArchiveStart;
299
+ }
300
+ }
301
+ function isMidRun(events) {
302
+ return events.length > 0 && !events.some(e => e.event.kind === "run_terminal");
303
+ }
304
+ /**
305
+ * Replay session events into Message[] for preloadHistory.
306
+ * run_started → user turn; llm_completed → assistant; tool_completed → tool messages.
307
+ */
308
+ function replayMessages(events) {
309
+ const messages = [];
310
+ for (const { event: e } of events) {
311
+ if (e.kind === "run_started") {
312
+ const userText = e.criteria.length
313
+ ? `${e.goal}\n\nCriteria:\n${e.criteria.map((c, i) => `${i + 1}. ${c}`).join("\n")}`
314
+ : e.goal;
315
+ messages.push({
316
+ role: "user",
317
+ content: userText,
318
+ toolCalls: [],
319
+ tokenCount: Math.max(1, Math.ceil(userText.length / 4)),
320
+ });
321
+ }
322
+ else if (e.kind === "llm_completed") {
323
+ messages.push({
324
+ role: "assistant",
325
+ content: e.content,
326
+ toolCalls: e.tool_calls ?? [],
327
+ tokenCount: e.token_count,
328
+ });
329
+ }
330
+ else if (e.kind === "tool_completed") {
331
+ for (const r of e.results) {
332
+ messages.push({
333
+ role: "tool",
334
+ content: "",
335
+ toolCalls: [],
336
+ contentParts: [{ type: "tool_result", callId: r.call_id, output: r.output, isError: r.is_error ?? false }],
337
+ tokenCount: r.token_count,
338
+ });
339
+ }
340
+ }
341
+ }
342
+ return messages;
343
+ }
344
+ function nextArchivedSeqStart(events) {
345
+ let next = 0;
346
+ for (const { event } of events ?? []) {
347
+ if (event.kind === "compressed")
348
+ next = Math.max(next, event.archived_seq_range[1] + 1);
349
+ }
350
+ return next;
351
+ }
352
+ function tryParseJson(s) {
353
+ try {
354
+ return JSON.parse(s);
355
+ }
356
+ catch {
357
+ return null;
358
+ }
359
+ }
360
+ /** Collect all text_delta events from a run into a single string. */
361
+ export async function collectText(stream) {
362
+ let text = "";
363
+ for await (const evt of stream) {
364
+ if (evt.type === "text_delta")
365
+ text += evt.delta;
366
+ }
367
+ return text;
368
+ }
@@ -0,0 +1,68 @@
1
+ import type { ProviderReplay, 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
+ provider_replay?: ProviderReplay;
16
+ } | {
17
+ kind: "tool_requested";
18
+ turn: number;
19
+ calls: ToolCall[];
20
+ } | {
21
+ kind: "tool_completed";
22
+ turn: number;
23
+ results: Array<{
24
+ call_id: string;
25
+ output: string;
26
+ is_error?: boolean;
27
+ token_count?: number;
28
+ }>;
29
+ } | {
30
+ kind: "compressed";
31
+ turn: number;
32
+ archived_seq_range: [number, number];
33
+ } | {
34
+ kind: "run_terminal";
35
+ reason: string;
36
+ turns_used: number;
37
+ total_tokens: number;
38
+ };
39
+ export interface SessionLog {
40
+ append(sessionId: string, event: SessionEvent): Promise<number>;
41
+ read(sessionId: string, fromSeq?: number): Promise<Array<{
42
+ seq: number;
43
+ event: SessionEvent;
44
+ }>>;
45
+ latestSeq(sessionId: string): Promise<number>;
46
+ }
47
+ export declare class InMemorySessionLog implements SessionLog {
48
+ private store;
49
+ append(sessionId: string, event: SessionEvent): Promise<number>;
50
+ read(sessionId: string, fromSeq?: number): Promise<Array<{
51
+ seq: number;
52
+ event: SessionEvent;
53
+ }>>;
54
+ latestSeq(sessionId: string): Promise<number>;
55
+ }
56
+ export declare class FileSessionLog implements SessionLog {
57
+ private dir;
58
+ private seqCounters;
59
+ constructor(dir: string);
60
+ private path;
61
+ private nextSeq;
62
+ append(sessionId: string, event: SessionEvent): Promise<number>;
63
+ read(sessionId: string, fromSeq?: number): Promise<Array<{
64
+ seq: number;
65
+ event: SessionEvent;
66
+ }>>;
67
+ latestSeq(sessionId: string): Promise<number>;
68
+ }
@@ -0,0 +1,84 @@
1
+ import { createWriteStream, createReadStream } from "node:fs";
2
+ import { mkdir } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { createInterface } from "node:readline";
5
+ export class InMemorySessionLog {
6
+ store = new Map();
7
+ async append(sessionId, event) {
8
+ if (!this.store.has(sessionId))
9
+ this.store.set(sessionId, []);
10
+ const entries = this.store.get(sessionId);
11
+ const seq = entries.length;
12
+ entries.push({ seq, event });
13
+ return seq;
14
+ }
15
+ async read(sessionId, fromSeq = 0) {
16
+ return (this.store.get(sessionId) ?? []).filter(e => e.seq >= fromSeq);
17
+ }
18
+ async latestSeq(sessionId) {
19
+ const entries = this.store.get(sessionId);
20
+ return entries ? entries.length - 1 : -1;
21
+ }
22
+ }
23
+ // Single-writer per session. Safe for concurrent appends within one instance.
24
+ // Cross-instance (multi-process) safety requires an external lock.
25
+ export class FileSessionLog {
26
+ dir;
27
+ // Lazy-initialized per-session counter. Avoids re-reading the file on every append.
28
+ seqCounters = new Map();
29
+ constructor(dir) {
30
+ this.dir = dir;
31
+ }
32
+ path(sessionId) {
33
+ return join(this.dir, `${sessionId}.jsonl`);
34
+ }
35
+ async nextSeq(sessionId) {
36
+ if (!this.seqCounters.has(sessionId)) {
37
+ const existing = await this.read(sessionId);
38
+ this.seqCounters.set(sessionId, existing.length);
39
+ }
40
+ const seq = this.seqCounters.get(sessionId);
41
+ this.seqCounters.set(sessionId, seq + 1);
42
+ return seq;
43
+ }
44
+ async append(sessionId, event) {
45
+ await mkdir(this.dir, { recursive: true });
46
+ const seq = await this.nextSeq(sessionId);
47
+ await new Promise((resolve, reject) => {
48
+ const ws = createWriteStream(this.path(sessionId), { flags: "a" });
49
+ ws.write(JSON.stringify({ seq, event }) + "\n", err => {
50
+ if (err)
51
+ reject(err);
52
+ else
53
+ resolve();
54
+ });
55
+ ws.end();
56
+ });
57
+ return seq;
58
+ }
59
+ async read(sessionId, fromSeq = 0) {
60
+ const results = [];
61
+ try {
62
+ const rl = createInterface({
63
+ input: createReadStream(this.path(sessionId)),
64
+ crlfDelay: Infinity,
65
+ });
66
+ for await (const line of rl) {
67
+ if (!line.trim())
68
+ continue;
69
+ const entry = JSON.parse(line);
70
+ if (entry.seq >= fromSeq)
71
+ results.push(entry);
72
+ }
73
+ }
74
+ catch (err) {
75
+ if (err.code !== "ENOENT")
76
+ throw err;
77
+ }
78
+ return results;
79
+ }
80
+ async latestSeq(sessionId) {
81
+ const entries = await this.read(sessionId);
82
+ return entries.length - 1;
83
+ }
84
+ }
package/dist/types.d.ts CHANGED
@@ -152,6 +152,13 @@ export interface RetryConfig {
152
152
  * Responses `previous_response_id` without leaking those semantics into the kernel.
153
153
  */
154
154
  export type ProviderRunState = Record<string, unknown>;
155
+ /** Provider-native fields required to replay a turn across requests (thinking blocks, reasoning_content, etc.). */
156
+ export interface ProviderReplay {
157
+ /** Anthropic-style assistant content blocks (thinking, text, tool_use). */
158
+ native_blocks?: Array<Record<string, unknown>>;
159
+ /** OpenAI-compatible reasoning field (DeepSeek, etc.). */
160
+ reasoning_content?: string;
161
+ }
155
162
  /** Structured render output produced by the kernel for each LLM call. */
156
163
  export interface RenderedContext {
157
164
  /** Combined system text: system partition + dashboard (when non-empty).
@@ -162,8 +169,28 @@ export interface RenderedContext {
162
169
  * Working-partition signals are already folded into the first user turn. */
163
170
  turns: Message[];
164
171
  }
172
+ /**
173
+ * Runtime execution policy advertised by a provider.
174
+ * RuntimeRunner merges these with RuntimeOptions — explicit options always win.
175
+ */
176
+ export interface RuntimePolicy {
177
+ /** Maximum agent turns before termination. */
178
+ maxTurns?: number;
179
+ /** Per-run wall-clock timeout in ms. */
180
+ timeoutMs?: number;
181
+ }
165
182
  export interface LLMProvider {
166
183
  createRunState?(): ProviderRunState;
184
+ /**
185
+ * Optional: return the recommended runtime policy for this provider's model.
186
+ * RuntimeRunner uses this as a fallback when the caller has not specified
187
+ * maxTurns / timeoutMs in RuntimeOptions.
188
+ */
189
+ runtimePolicy?(): RuntimePolicy;
190
+ /** Read provider-native replay fields captured after the most recent assistant turn. */
191
+ peekProviderReplay?(message: Pick<Message, "content" | "toolCalls">): ProviderReplay | undefined;
192
+ /** Restore provider-native replay fields when rebuilding history from SessionLog. */
193
+ seedProviderReplay?(message: Pick<Message, "content" | "toolCalls">, replay: ProviderReplay): void;
167
194
  complete(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>): Promise<Message>;
168
195
  stream(context: RenderedContext, tools: ToolSchema[], extensions?: Record<string, unknown>, state?: ProviderRunState): AsyncIterable<StreamEvent>;
169
196
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepstrike/sdk",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "description": "DeepStrike Node.js SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -12,11 +12,12 @@
12
12
  "scripts": {
13
13
  "build": "tsc",
14
14
  "test": "node --experimental-vm-modules node_modules/.bin/jest",
15
+ "test:local-core": "cd ../crates/deepstrike-node && napi build --platform && cd ../../node && npm test",
15
16
  "smoke:providers": "node ../scripts/smoke-node-provider-loops.mjs"
16
17
  },
17
18
  "dependencies": {
18
19
  "@anthropic-ai/sdk": "^0.39.0",
19
- "@deepstrike/core": "0.1.13",
20
+ "@deepstrike/core": "0.1.15",
20
21
  "@google/generative-ai": "^0.24.1",
21
22
  "openai": "^4.77.0"
22
23
  },
package/dist/agent.d.ts DELETED
@@ -1,90 +0,0 @@
1
- import type { LLMProvider, StreamEvent, ToolSuspendEvent } from "./types.js";
2
- import type { RegisteredTool } from "./tools/index.js";
3
- import type { DreamStore, DreamResult, SessionStore } 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
- * System-level instructions prepended to every context render.
13
- * Passed to the kernel's `system` partition before the first LLM call.
14
- */
15
- systemPrompt?: string;
16
- /**
17
- * Directory containing skill `.md` files. The kernel auto-injects a `skill`
18
- * meta-tool so the model can load any skill by name on demand.
19
- */
20
- skillDir?: string;
21
- knowledgeSource?: KnowledgeSource;
22
- signalSource?: SignalSource;
23
- /** Backing store for the idle dreaming pipeline. Required to call `Agent.dream()`. */
24
- dreamStore?: DreamStore;
25
- /** Optional durable transcript store for same-session conversational continuity. */
26
- sessionStore?: SessionStore;
27
- /**
28
- * Stable identifier for this agent. Required to enable in-session memory retrieval
29
- * when `dreamStore` is configured.
30
- */
31
- agentId?: string;
32
- /**
33
- * SDK Governance instance or any object implementing
34
- * `evaluate(toolName: string, argsJson: string): { kind: string; reason?: string; retryAfterMs?: number }`.
35
- * When provided, every tool call is evaluated through the full pipeline.
36
- */
37
- onToolSuspend?: (event: ToolSuspendEvent) => Promise<unknown> | unknown;
38
- governance?: {
39
- setTime?(nowMs: bigint): void;
40
- evaluate(toolName: string, argsJson: string): {
41
- kind: string;
42
- reason?: string;
43
- retryAfterMs?: number;
44
- };
45
- };
46
- }
47
- export declare class Agent {
48
- private readonly provider;
49
- private readonly options;
50
- private tools;
51
- private extensions;
52
- private skillDir?;
53
- private knowledgeSource?;
54
- private signalSource?;
55
- private dreamStore?;
56
- private readonly inMemorySessions;
57
- private interrupted;
58
- private pendingInterrupt;
59
- private _turn;
60
- private _pressure;
61
- constructor(provider: LLMProvider, options: AgentOptions);
62
- /** Current turn index within the active run (0 before a run starts). */
63
- get turn(): number;
64
- /** Context pressure ratio [0–1] from the kernel. Values > 0.8 trigger compression. */
65
- get pressure(): number;
66
- interrupt(): void;
67
- register(...tools: RegisteredTool[]): this;
68
- unregister(name: string): this;
69
- /**
70
- * Collect the full text response and return it.
71
- * For richer control (streaming, tool events, token counts) use `runStreaming`.
72
- */
73
- run(goal: string, criteria?: string[], extensions?: Record<string, unknown>, sessionId?: string): Promise<string>;
74
- runStreaming(goal: string, criteria?: string[], extensions?: Record<string, unknown>, sessionId?: string): AsyncIterable<StreamEvent>;
75
- private runRegularTool;
76
- private loadSession;
77
- private saveSession;
78
- private toMessage;
79
- private kernelMsgToSessionMsg;
80
- /**
81
- * Trigger the idle dreaming cycle for this agent.
82
- * Requires `dreamStore` and `agentId` to be configured.
83
- *
84
- * Phase 1 — kernel rule-based analysis + LLM prompt assembly
85
- * Phase 2 — LLM synthesis call (I/O)
86
- * Phase 3 — kernel parses + curates results
87
- * Phase 4 — commit delta to DreamStore (I/O)
88
- */
89
- dream(agentId: string, nowMs?: number): Promise<DreamResult>;
90
- }