@deepstrike/sdk 0.1.15 → 0.2.0

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 (57) hide show
  1. package/dist/collaboration/harness.d.ts +4 -1
  2. package/dist/collaboration/harness.js +20 -6
  3. package/dist/collaboration/modes/creator-verifier.d.ts +8 -2
  4. package/dist/collaboration/modes/creator-verifier.js +9 -3
  5. package/dist/collaboration/pool.d.ts +24 -3
  6. package/dist/collaboration/pool.js +65 -5
  7. package/dist/harness/harness.d.ts +5 -1
  8. package/dist/harness/harness.js +23 -1
  9. package/dist/index.d.ts +10 -1
  10. package/dist/index.js +5 -0
  11. package/dist/kernel.d.ts +12 -43
  12. package/dist/kernel.js +1 -1
  13. package/dist/providers/anthropic.js +7 -2
  14. package/dist/providers/base.d.ts +12 -0
  15. package/dist/providers/base.js +75 -0
  16. package/dist/providers/catalog.d.ts +3 -2
  17. package/dist/providers/catalog.js +61 -13
  18. package/dist/providers/deepseek.js +1 -1
  19. package/dist/providers/gemini.d.ts +3 -0
  20. package/dist/providers/gemini.js +26 -6
  21. package/dist/providers/glm.d.ts +9 -0
  22. package/dist/providers/glm.js +21 -0
  23. package/dist/providers/kimi.js +2 -0
  24. package/dist/providers/minimax.js +5 -0
  25. package/dist/providers/openai-responses.js +9 -0
  26. package/dist/providers/openai.js +52 -6
  27. package/dist/providers/profiles.d.ts +1304 -242
  28. package/dist/providers/profiles.js +429 -26
  29. package/dist/providers/qwen.d.ts +3 -1
  30. package/dist/providers/qwen.js +43 -13
  31. package/dist/runtime/archive.d.ts +15 -0
  32. package/dist/runtime/archive.js +35 -0
  33. package/dist/runtime/execution-plane.js +14 -4
  34. package/dist/runtime/filtered-plane.d.ts +14 -0
  35. package/dist/runtime/filtered-plane.js +45 -0
  36. package/dist/runtime/kernel-step.d.ts +88 -0
  37. package/dist/runtime/kernel-step.js +262 -0
  38. package/dist/runtime/process-sandbox-plane.d.ts +4 -4
  39. package/dist/runtime/process-sandbox-plane.js +5 -5
  40. package/dist/runtime/provider-replay.js +37 -3
  41. package/dist/runtime/replay-sanitize.d.ts +5 -0
  42. package/dist/runtime/replay-sanitize.js +25 -0
  43. package/dist/runtime/runner.d.ts +60 -4
  44. package/dist/runtime/runner.js +589 -86
  45. package/dist/runtime/session-log.d.ts +93 -1
  46. package/dist/runtime/session-repair.d.ts +37 -0
  47. package/dist/runtime/session-repair.js +77 -0
  48. package/dist/runtime/sub-agent-orchestrator.d.ts +19 -0
  49. package/dist/runtime/sub-agent-orchestrator.js +114 -0
  50. package/dist/skills/watcher.d.ts +19 -0
  51. package/dist/skills/watcher.js +38 -0
  52. package/dist/tools/index.d.ts +4 -1
  53. package/dist/tools/index.js +82 -11
  54. package/dist/types/agent.d.ts +68 -0
  55. package/dist/types/agent.js +78 -0
  56. package/dist/types.d.ts +27 -0
  57. package/package.json +2 -2
@@ -1,14 +1,104 @@
1
1
  import { getKernel } from "../kernel.js";
2
2
  import { peekProviderReplay, seedProviderReplayFromEvents } from "./provider-replay.js";
3
+ import { sanitizeReplayText } from "./replay-sanitize.js";
4
+ import { buildLlmCompletedEvent, buildRunTerminalEvent, repairEventsForRecovery } from "./session-repair.js";
5
+ import { capabilityMarker, capabilitySkill, capabilityTool, capabilityCommandMount, capabilityCommandUnmount, kernelAction, kernelApply, forceCompact, messageToKernelMessage, skillMetadataToKernel, taskUpdateToKernel, toolResultToKernel, toolSchemaToKernel, } from "./kernel-step.js";
6
+ import { agentRunSpecToKernel, milestoneCheckPass, milestoneCheckResultToKernel, subAgentResultToKernel } from "../types/agent.js";
7
+ import { defaultSubAgentOrchestrator } from "./sub-agent-orchestrator.js";
3
8
  export class RuntimeRunner {
4
9
  opts;
5
10
  interrupted = false;
11
+ activeKernel = null;
12
+ pendingObservations = [];
13
+ currentSessionId = null;
14
+ nextArchiveStart = 0;
6
15
  constructor(opts) {
7
16
  this.opts = opts;
8
17
  }
18
+ /** Host configuration (for coordinator / sub-agent spawn). */
19
+ get hostOptions() {
20
+ return this.opts;
21
+ }
22
+ /** Mount a tool capability on the currently-running kernel runtime. No-op if not running. */
23
+ mountTool(schema) {
24
+ if (!this.activeKernel)
25
+ return;
26
+ kernelApply(this.activeKernel, this.pendingObservations, capabilityCommandMount(capabilityTool(schema)));
27
+ }
28
+ /** Mount a skill capability on the currently-running kernel runtime. No-op if not running. */
29
+ mountSkill(name, description) {
30
+ if (!this.activeKernel)
31
+ return;
32
+ kernelApply(this.activeKernel, this.pendingObservations, capabilityCommandMount(capabilitySkill({ name, description, estimatedTokens: 0 })));
33
+ }
34
+ /** Mount a generic marker capability (e.g. MCP server, agent) on the active run. No-op if not running. */
35
+ mountMarker(kind, id, description) {
36
+ if (!this.activeKernel)
37
+ return;
38
+ kernelApply(this.activeKernel, this.pendingObservations, capabilityCommandMount(capabilityMarker(kind, id, description)));
39
+ }
40
+ /** Unmount a capability by kind + id from the active run. No-op if not running. */
41
+ unmountCapability(kind, id) {
42
+ if (!this.activeKernel)
43
+ return;
44
+ kernelApply(this.activeKernel, this.pendingObservations, capabilityCommandUnmount(kind, id));
45
+ }
46
+ /** Push a large artifact into the kernel artifacts partition (not inlined in history). */
47
+ pushArtifact(message, tokens) {
48
+ if (!this.activeKernel)
49
+ return;
50
+ kernelApply(this.activeKernel, this.pendingObservations, {
51
+ kind: "push_artifact",
52
+ message: messageToKernelMessage(message),
53
+ ...(tokens !== undefined ? { tokens } : {}),
54
+ });
55
+ }
56
+ /**
57
+ * Spawn an isolated sub-agent via the kernel, run it on the host, and feed the result back.
58
+ * Requires an active parent run (`run()` / `wake()` in progress or paused at milestone).
59
+ */
60
+ async *spawnSubAgent(spec) {
61
+ if (!this.activeKernel || !this.currentSessionId) {
62
+ throw new Error("spawnSubAgent requires an active parent run");
63
+ }
64
+ const parentSessionId = this.currentSessionId;
65
+ const runtime = this.activeKernel;
66
+ const observations = kernelApply(runtime, this.pendingObservations, {
67
+ kind: "spawn_sub_agent",
68
+ spec: agentRunSpecToKernel(spec),
69
+ parent_session_id: parentSessionId,
70
+ });
71
+ this.nextArchiveStart = await this.appendObservations(parentSessionId, runtime, this.nextArchiveStart);
72
+ const spawned = observations.find((o) => o.kind === "agent_spawned" && typeof o.agent_id === "string");
73
+ if (!spawned)
74
+ throw new Error("spawn_sub_agent did not emit agent_spawned");
75
+ const manifest = {
76
+ kind: "agent_spawned",
77
+ turn: spawned.turn,
78
+ agent_id: spawned.agent_id,
79
+ parent_session_id: spawned.parent_session_id ?? parentSessionId,
80
+ role: spawned.role ?? spec.role,
81
+ isolation: spawned.isolation ?? spec.isolation ?? "shared",
82
+ context_inheritance: spawned.context_inheritance ?? "none",
83
+ permitted_capability_ids: spawned.permitted_capability_ids ?? [],
84
+ };
85
+ const orchestrator = this.opts.subAgentOrchestrator ?? defaultSubAgentOrchestrator;
86
+ const result = await orchestrator.run({
87
+ parentOpts: this.opts,
88
+ parentSessionId,
89
+ spec,
90
+ manifest,
91
+ sessionLog: this.opts.sessionLog,
92
+ });
93
+ kernelApply(runtime, this.pendingObservations, {
94
+ kind: "sub_agent_completed",
95
+ result: subAgentResultToKernel(result),
96
+ });
97
+ yield { type: "done", iterations: result.result.turnsUsed, totalTokens: result.result.totalTokensUsed, status: result.result.termination };
98
+ }
9
99
  interrupt() { this.interrupted = true; }
10
100
  async *run(req) {
11
- const prior = await this.opts.sessionLog.read(req.sessionId);
101
+ const prior = req.inheritEvents ?? await this.opts.sessionLog.read(req.sessionId);
12
102
  const midRun = isMidRun(prior);
13
103
  if (!midRun) {
14
104
  await this.opts.sessionLog.append(req.sessionId, {
@@ -32,14 +122,16 @@ export class RuntimeRunner {
32
122
  const start = startEntry.event;
33
123
  yield* this.execute(sessionId, start.goal, start.criteria, extensions, events, true);
34
124
  }
35
- async dream(agentId, nowMs = Date.now()) {
125
+ async *dream(agentId, nowMs = Date.now()) {
36
126
  if (!this.opts.dreamStore)
37
127
  throw new Error("dreamStore not configured");
38
128
  const kernel = getKernel();
39
129
  const sessions = await this.opts.dreamStore.loadSessions(agentId);
40
130
  const existingMemories = await this.opts.dreamStore.loadMemories(agentId);
41
- if (!sessions.length)
42
- return { sessionsProcessed: 0, insightsExtracted: 0, entriesAdded: 0, entriesRemoved: 0 };
131
+ if (!sessions.length) {
132
+ yield { type: "done", iterations: 0, totalTokens: 0, status: "completed", dreamResult: { sessionsProcessed: 0, insightsExtracted: 0, entriesAdded: 0, entriesRemoved: 0 } };
133
+ return;
134
+ }
43
135
  const pipeline = new kernel.IdlePipeline(agentId);
44
136
  const action1 = pipeline.feedTrigger(sessions.map(s => ({
45
137
  sessionId: s.sessionId, agentId: s.agentId,
@@ -51,20 +143,27 @@ export class RuntimeRunner {
51
143
  createdAtMs: s.createdAtMs, updatedAtMs: s.updatedAtMs,
52
144
  })), existingMemories.map(e => ({ text: e.text, score: e.score, metadata: JSON.stringify(e.metadata ?? null) })), nowMs);
53
145
  if (action1.kind === "noop" || action1.kind === "aborted") {
54
- return { sessionsProcessed: 0, insightsExtracted: 0, entriesAdded: 0, entriesRemoved: 0 };
146
+ yield { type: "done", iterations: 0, totalTokens: 0, status: "completed", dreamResult: { sessionsProcessed: 0, insightsExtracted: 0, entriesAdded: 0, entriesRemoved: 0 } };
147
+ return;
55
148
  }
56
149
  if (action1.kind !== "synthesize_insights")
57
150
  throw new Error(`unexpected: ${action1.kind}`);
58
151
  let synthesisText = "";
59
152
  const providerState = this.opts.provider.createRunState?.();
60
153
  const synthMsgs = (action1.messages ?? []);
154
+ const kernelSystemText = synthMsgs.filter(m => m.role === "system").map(m => m.content).join("\n\n");
61
155
  const synthContext = {
62
- systemText: synthMsgs.filter(m => m.role === "system").map(m => m.content).join("\n\n"),
156
+ systemText: [kernelSystemText, this.opts.dreamSystemPrompt].filter(Boolean).join("\n\n"),
63
157
  turns: synthMsgs.filter(m => m.role !== "system"),
64
158
  };
159
+ let totalTokens = 0;
65
160
  for await (const evt of this.opts.provider.stream(synthContext, [], undefined, providerState)) {
66
- if (evt.type === "text_delta")
161
+ if (evt.type === "text_delta") {
67
162
  synthesisText += evt.delta;
163
+ yield evt;
164
+ }
165
+ else if (evt.type === "usage")
166
+ totalTokens = evt.totalTokens;
68
167
  }
69
168
  const action2 = pipeline.feedSynthesisResult(synthesisText);
70
169
  if (action2.kind !== "commit_memories")
@@ -84,62 +183,131 @@ export class RuntimeRunner {
84
183
  },
85
184
  };
86
185
  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,
186
+ yield {
187
+ type: "done", iterations: 1, totalTokens, status: "completed",
188
+ dreamResult: {
189
+ sessionsProcessed: rr.sessionsProcessed,
190
+ insightsExtracted: rr.insightsExtracted,
191
+ entriesAdded: cr.stats?.entriesAdded ?? 0,
192
+ entriesRemoved: (cr.toRemoveIndices ?? []).length,
193
+ },
92
194
  };
93
195
  }
94
196
  async *execute(sessionId, goal, criteria, extensions, priorEvents, resumeMidRun = false) {
95
197
  this.interrupted = false;
198
+ this.pendingObservations = [];
199
+ this.currentSessionId = sessionId;
96
200
  const kernel = getKernel();
97
201
  const ext = { ...this.opts.extensions, ...(extensions ?? {}) };
98
202
  const providerState = this.opts.provider.createRunState?.();
99
203
  let nextCompressedArchiveStart = nextArchivedSeqStart(priorEvents);
100
- // Three-layer policy merge: explicit RuntimeOptions > provider.runtimePolicy() > defaults
101
204
  const providerPolicy = this.opts.provider.runtimePolicy?.() ?? {};
102
205
  const effectiveMaxTurns = this.opts.maxTurns ?? providerPolicy.maxTurns ?? 25;
103
206
  const effectiveTimeoutMs = this.opts.timeoutMs ?? providerPolicy.timeoutMs;
104
- const sm = new kernel.LoopStateMachine({
207
+ const runtime = new kernel.KernelRuntime({
105
208
  maxTokens: this.opts.maxTokens,
106
209
  maxTurns: effectiveMaxTurns,
107
210
  timeoutMs: effectiveTimeoutMs !== undefined ? BigInt(effectiveTimeoutMs) : undefined,
108
211
  });
212
+ this.activeKernel = runtime;
213
+ this.nextArchiveStart = nextCompressedArchiveStart;
109
214
  const router = new kernel.SignalRouter(256);
110
- sm.setTools(this.opts.executionPlane.schemas());
215
+ if (this.opts.tokenizer) {
216
+ kernelApply(runtime, this.pendingObservations, {
217
+ kind: "set_tokenizer",
218
+ name: this.opts.tokenizer,
219
+ });
220
+ }
221
+ if (this.opts.enablePlanTool !== undefined) {
222
+ kernelApply(runtime, this.pendingObservations, {
223
+ kind: "set_plan_tool_enabled",
224
+ enabled: this.opts.enablePlanTool,
225
+ });
226
+ }
227
+ kernelApply(runtime, this.pendingObservations, {
228
+ kind: "set_tools",
229
+ tools: this.opts.executionPlane.schemas().map(toolSchemaToKernel),
230
+ });
111
231
  if (this.opts.systemPrompt) {
112
- sm.addSystemMessage(this.opts.systemPrompt, Math.max(1, Math.ceil(this.opts.systemPrompt.length / 4)));
232
+ kernelApply(runtime, this.pendingObservations, {
233
+ kind: "add_system_message",
234
+ content: this.opts.systemPrompt,
235
+ tokens: Math.max(1, Math.ceil(this.opts.systemPrompt.length / 4)),
236
+ });
113
237
  }
114
238
  if (this.opts.initialMemory) {
115
239
  for (const mem of this.opts.initialMemory) {
116
- sm.addMemoryMessage(mem, Math.max(1, Math.ceil(mem.length / 4)));
240
+ kernelApply(runtime, this.pendingObservations, {
241
+ kind: "add_memory_message",
242
+ content: mem,
243
+ tokens: Math.max(1, Math.ceil(mem.length / 4)),
244
+ });
117
245
  }
118
246
  }
119
247
  if (this.opts.skillDir) {
120
248
  const { scanSkillDir } = await import("../skills/loader.js");
121
249
  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
- })));
250
+ kernelApply(runtime, this.pendingObservations, {
251
+ kind: "set_available_skills",
252
+ skills: metas.map((m) => skillMetadataToKernel({
253
+ name: m.name,
254
+ description: m.description,
255
+ whenToUse: m.whenToUse,
256
+ effort: m.effort,
257
+ estimatedTokens: m.estimatedTokens ?? 0,
258
+ })),
259
+ });
260
+ }
261
+ if (this.opts.dreamStore && this.opts.agentId) {
262
+ kernelApply(runtime, this.pendingObservations, { kind: "set_memory_enabled", enabled: true });
263
+ }
264
+ if (this.opts.knowledgeSource) {
265
+ kernelApply(runtime, this.pendingObservations, { kind: "set_knowledge_enabled", enabled: true });
126
266
  }
127
- if (this.opts.dreamStore && this.opts.agentId)
128
- sm.setMemoryEnabled(true);
129
- if (this.opts.knowledgeSource)
130
- sm.setKnowledgeEnabled(true);
267
+ if (this.opts.milestoneContract) {
268
+ kernelApply(runtime, this.pendingObservations, {
269
+ kind: "load_milestone_contract",
270
+ contract: {
271
+ phases: this.opts.milestoneContract.phases.map(p => ({
272
+ id: p.id,
273
+ criteria: p.criteria ?? [],
274
+ unlocks: p.unlocks ?? [],
275
+ required_evidence: p.requiredEvidence ?? [],
276
+ ...(p.verifier ? { verifier: p.verifier } : {}),
277
+ })),
278
+ },
279
+ });
280
+ }
281
+ const maxBytes = runtime.recoveryContentBytes();
131
282
  if (priorEvents && priorEvents.length > 0) {
132
- seedProviderReplayFromEvents(this.opts.provider, priorEvents);
133
- sm.preloadHistory(replayMessages(priorEvents));
283
+ const repaired = repairEventsForRecovery(priorEvents, maxBytes);
284
+ seedProviderReplayFromEvents(this.opts.provider, repaired);
285
+ const loadArchive = this.opts.compressionStore
286
+ ? (ref) => this.opts.compressionStore.read(ref)
287
+ : undefined;
288
+ const replayed = await replayMessagesAsync(repaired, maxBytes, loadArchive);
289
+ kernelApply(runtime, this.pendingObservations, {
290
+ kind: "preload_history",
291
+ messages: replayed.map(messageToKernelMessage),
292
+ });
134
293
  }
135
294
  const sessionStart = Date.now();
295
+ const startPayload = {
296
+ kind: "start_run",
297
+ task: { goal, criteria },
298
+ };
299
+ if (this.opts.runSpec) {
300
+ startPayload.run_spec = agentRunSpecToKernel(this.opts.runSpec);
301
+ }
136
302
  let action = resumeMidRun
137
- ? sm.resumeAfterPreload()
138
- : sm.start({ goal, criteria });
139
- while (!sm.isTerminal()) {
140
- nextCompressedArchiveStart = await this.appendObservations(sessionId, sm, nextCompressedArchiveStart);
303
+ ? kernelAction(runtime, this.pendingObservations, { kind: "resume" })
304
+ : kernelAction(runtime, this.pendingObservations, startPayload);
305
+ let hasAttemptedReactiveCompact = false;
306
+ while (!runtime.isTerminal()) {
307
+ nextCompressedArchiveStart = await this.appendObservations(sessionId, runtime, nextCompressedArchiveStart);
308
+ this.nextArchiveStart = nextCompressedArchiveStart;
141
309
  if (this.interrupted) {
142
- action = sm.feedTimeout();
310
+ action = kernelAction(runtime, this.pendingObservations, { kind: "timeout" });
143
311
  break;
144
312
  }
145
313
  if (this.opts.signalSource) {
@@ -155,9 +323,9 @@ export class RuntimeRunner {
155
323
  dedupeKey: sig.dedupeKey,
156
324
  timestampMs: Date.now(),
157
325
  };
158
- const disposition = router.ingest(kernelSig, action.kind === "execute_tools");
326
+ const disposition = router.ingest(kernelSig, action.kind === "execute_tool");
159
327
  if (disposition === "interrupt_now") {
160
- action = sm.feedTimeout();
328
+ action = kernelAction(runtime, this.pendingObservations, { kind: "timeout" });
161
329
  break;
162
330
  }
163
331
  }
@@ -165,19 +333,20 @@ export class RuntimeRunner {
165
333
  let queued = router.next();
166
334
  while (queued) {
167
335
  if (queued.urgency === "critical") {
168
- action = sm.feedTimeout();
336
+ action = kernelAction(runtime, this.pendingObservations, { kind: "timeout" });
169
337
  break;
170
338
  }
171
339
  queued = router.next();
172
340
  }
173
- if (sm.isTerminal())
341
+ if (runtime.isTerminal())
174
342
  break;
175
- if (action.kind === "call_llm") {
343
+ if (action.kind === "call_provider") {
176
344
  const finalToolCalls = [];
177
345
  let finalText = "";
178
346
  const context = action.context;
179
- const tools = (action.tools ?? []);
347
+ const tools = action.tools;
180
348
  let turnTokens = 0;
349
+ let shouldRetry = false;
181
350
  try {
182
351
  for await (const evt of this.opts.provider.stream(context, tools, Object.keys(ext).length ? ext : undefined, providerState)) {
183
352
  if (evt.type === "usage") {
@@ -194,29 +363,51 @@ export class RuntimeRunner {
194
363
  }
195
364
  }
196
365
  catch (err) {
197
- yield { type: "error", message: String(err) };
198
- action = sm.feedTimeout();
199
- break;
366
+ const errMsg = String(err).toLowerCase();
367
+ if ((errMsg.includes("413") || errMsg.includes("too long") || errMsg.includes("context length exceeded") || errMsg.includes("context_length_exceeded")) &&
368
+ !hasAttemptedReactiveCompact) {
369
+ hasAttemptedReactiveCompact = true;
370
+ if (forceCompact(runtime, this.pendingObservations)) {
371
+ nextCompressedArchiveStart = await this.appendObservations(sessionId, runtime, nextCompressedArchiveStart);
372
+ shouldRetry = true;
373
+ }
374
+ }
375
+ if (!shouldRetry) {
376
+ yield { type: "error", message: String(err) };
377
+ action = kernelAction(runtime, this.pendingObservations, { kind: "timeout" });
378
+ break;
379
+ }
200
380
  }
201
- action = sm.feedLlmResponse({
381
+ if (shouldRetry) {
382
+ action = {
383
+ kind: "call_provider",
384
+ context: runtime.render(),
385
+ tools,
386
+ };
387
+ continue;
388
+ }
389
+ const assistantMessage = {
202
390
  role: "assistant",
203
391
  content: finalText,
204
392
  toolCalls: finalToolCalls,
205
393
  tokenCount: turnTokens || undefined,
394
+ };
395
+ action = kernelAction(runtime, this.pendingObservations, {
396
+ kind: "provider_result",
397
+ message: messageToKernelMessage(assistantMessage),
206
398
  });
207
399
  const providerReplay = peekProviderReplay(this.opts.provider, finalText, finalToolCalls);
208
- await this.opts.sessionLog.append(sessionId, {
209
- kind: "llm_completed",
210
- turn: sm.turn,
400
+ await this.opts.sessionLog.append(sessionId, buildLlmCompletedEvent({
401
+ turn: runtime.turn(),
211
402
  content: finalText,
212
- token_count: turnTokens || undefined,
213
- tool_calls: finalToolCalls,
214
- ...(providerReplay ? { provider_replay: providerReplay } : {}),
215
- });
403
+ tokenCount: turnTokens || undefined,
404
+ toolCalls: finalToolCalls,
405
+ providerReplay,
406
+ }));
216
407
  }
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 });
408
+ else if (action.kind === "execute_tool") {
409
+ const allCalls = action.calls;
410
+ await this.opts.sessionLog.append(sessionId, { kind: "tool_requested", turn: runtime.turn(), calls: allCalls });
220
411
  const runCtx = {
221
412
  agentId: this.opts.agentId,
222
413
  skillDir: this.opts.skillDir,
@@ -226,16 +417,85 @@ export class RuntimeRunner {
226
417
  onToolSuspend: this.opts.onToolSuspend,
227
418
  };
228
419
  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 });
420
+ const normalCalls = allCalls.filter(c => c.name !== "update_plan");
421
+ const planCalls = allCalls.filter(c => c.name === "update_plan");
422
+ for (const call of planCalls) {
423
+ const update = parseUpdatePlanArgs(call.arguments);
424
+ kernelApply(runtime, this.pendingObservations, {
425
+ kind: "update_task",
426
+ update: taskUpdateToKernel(update),
427
+ });
428
+ const result = { callId: call.id, output: "success", isError: false };
429
+ toolResults.push(result);
430
+ yield { type: "tool_result", callId: call.id, content: "success", isError: false };
431
+ }
432
+ if (normalCalls.length > 0) {
433
+ for await (const evt of this.opts.executionPlane.executeAll(normalCalls, runCtx)) {
434
+ yield evt;
435
+ if (evt.type === "tool_result") {
436
+ const tre = evt;
437
+ toolResults.push({
438
+ callId: tre.callId,
439
+ output: tre.content,
440
+ isError: tre.isError,
441
+ isFatal: tre.isFatal,
442
+ errorKind: tre.errorKind,
443
+ });
444
+ }
445
+ else if (evt.type === "tool_argument_repaired") {
446
+ const tare = evt;
447
+ await this.opts.sessionLog.append(sessionId, {
448
+ kind: "tool_argument_repaired",
449
+ turn: runtime.turn(),
450
+ tool: tare.name,
451
+ original_arguments: tare.originalArguments,
452
+ repaired_arguments: tare.repairedArguments,
453
+ });
454
+ }
455
+ else if (evt.type === "tool_denied") {
456
+ const tde = evt;
457
+ await this.opts.sessionLog.append(sessionId, {
458
+ kind: "tool_denied",
459
+ turn: runtime.turn(),
460
+ call_id: tde.callId,
461
+ tool_name: tde.toolName,
462
+ reason: tde.reason,
463
+ });
464
+ }
465
+ else if (evt.type === "permission_request") {
466
+ const pre = evt;
467
+ const turn = runtime.turn();
468
+ await this.opts.sessionLog.append(sessionId, {
469
+ kind: "permission_requested",
470
+ turn,
471
+ tool: pre.toolName,
472
+ arguments: JSON.stringify(pre.arguments),
473
+ reason: pre.reason,
474
+ });
475
+ await this.opts.sessionLog.append(sessionId, {
476
+ kind: "permission_resolved",
477
+ turn,
478
+ approved: false,
479
+ responder: "policy_gate",
480
+ });
481
+ await this.opts.sessionLog.append(sessionId, {
482
+ kind: "tool_denied",
483
+ turn,
484
+ call_id: pre.callId,
485
+ tool_name: pre.toolName,
486
+ reason: `permission denied by policy gate: ${pre.reason}`,
487
+ });
488
+ }
234
489
  }
490
+ const names = normalCalls.map(c => c.name).join(", ");
491
+ kernelApply(runtime, this.pendingObservations, {
492
+ kind: "update_task",
493
+ update: taskUpdateToKernel({ progress: `Executed tools: ${names}` }),
494
+ });
235
495
  }
236
496
  await this.opts.sessionLog.append(sessionId, {
237
497
  kind: "tool_completed",
238
- turn: sm.turn,
498
+ turn: runtime.turn(),
239
499
  results: toolResults.map(r => ({
240
500
  call_id: r.callId,
241
501
  output: r.output,
@@ -243,20 +503,62 @@ export class RuntimeRunner {
243
503
  token_count: r.tokenCount,
244
504
  })),
245
505
  });
246
- action = sm.feedToolResults(toolResults);
506
+ action = kernelAction(runtime, this.pendingObservations, {
507
+ kind: "tool_results",
508
+ results: toolResults.map(toolResultToKernel),
509
+ });
510
+ }
511
+ else if (action.kind === "evaluate_milestone") {
512
+ const milestonePolicy = this.opts.milestonePolicy ?? "require_verifier";
513
+ if (milestonePolicy === "auto_pass") {
514
+ action = kernelAction(runtime, this.pendingObservations, {
515
+ kind: "milestone_result",
516
+ result: milestoneCheckResultToKernel(milestoneCheckPass(action.phaseId)),
517
+ });
518
+ this.nextArchiveStart = await this.appendObservations(sessionId, runtime, this.nextArchiveStart);
519
+ }
520
+ else if (this.opts.onMilestoneEvaluate) {
521
+ const check = await this.opts.onMilestoneEvaluate({
522
+ phaseId: action.phaseId,
523
+ criteria: action.criteria,
524
+ requiredEvidence: action.requiredEvidence,
525
+ });
526
+ action = kernelAction(runtime, this.pendingObservations, {
527
+ kind: "milestone_result",
528
+ result: milestoneCheckResultToKernel(check),
529
+ });
530
+ this.nextArchiveStart = await this.appendObservations(sessionId, runtime, this.nextArchiveStart);
531
+ }
532
+ else {
533
+ this.nextArchiveStart = await this.appendObservations(sessionId, runtime, this.nextArchiveStart);
534
+ const turnsUsed = Math.max(1, runtime.turn());
535
+ await this.opts.sessionLog.append(sessionId, buildRunTerminalEvent({
536
+ reason: "milestone_pending",
537
+ turnsUsed,
538
+ totalTokens: 0,
539
+ }));
540
+ yield { type: "done", iterations: turnsUsed, totalTokens: 0, status: "milestone_pending" };
541
+ this.activeKernel = null;
542
+ this.currentSessionId = null;
543
+ return;
544
+ }
247
545
  }
248
546
  else if (action.kind === "done") {
249
547
  break;
250
548
  }
251
549
  }
252
- const result = action.result;
550
+ const result = action.kind === "done" ? action.result : undefined;
253
551
  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 });
552
+ const turnsUsed = result ? Math.max(1, result.turnsUsed) : runtime.turn() || 0;
553
+ const totalTokens = result?.totalTokensUsed ?? 0;
554
+ nextCompressedArchiveStart = await this.appendObservations(sessionId, runtime, nextCompressedArchiveStart);
555
+ await this.opts.sessionLog.append(sessionId, buildRunTerminalEvent({
556
+ reason: status,
557
+ turnsUsed,
558
+ totalTokens,
559
+ }));
258
560
  if (this.opts.dreamStore && this.opts.agentId) {
259
- const newMsgs = sm.drainNewMessages().map(m => ({
561
+ const newMsgs = runtime.drainNewMessages().map(m => ({
260
562
  role: m.role,
261
563
  content: m.content,
262
564
  contentParts: m.contentParts,
@@ -278,22 +580,110 @@ export class RuntimeRunner {
278
580
  }
279
581
  }
280
582
  yield { type: "done", iterations: turnsUsed, totalTokens, status };
583
+ this.activeKernel = null;
584
+ this.currentSessionId = null;
281
585
  }
282
- async appendObservations(sessionId, sm, nextArchiveStart) {
283
- const observations = sm.takeObservations();
586
+ async appendObservations(sessionId, runtime, nextArchiveStart) {
587
+ const turn = runtime.turn();
588
+ const preservedRefs = runtime.preservedRefs();
589
+ const observations = this.pendingObservations.splice(0);
284
590
  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;
591
+ if (obs.kind === "compressed") {
592
+ const latest = await this.opts.sessionLog.latestSeq(sessionId);
593
+ if (latest < nextArchiveStart)
594
+ continue;
595
+ const end = latest;
596
+ let archiveRef = undefined;
597
+ const archived = obs.archived;
598
+ if (this.opts.compressionStore && archived && archived.length > 0) {
599
+ try {
600
+ const pathRef = await this.opts.compressionStore.write(sessionId, nextArchiveStart, archived);
601
+ if (pathRef)
602
+ archiveRef = pathRef;
603
+ }
604
+ catch {
605
+ // non-fatal
606
+ }
607
+ }
608
+ const compressedSeq = await this.opts.sessionLog.append(sessionId, {
609
+ kind: "compressed",
610
+ turn,
611
+ archived_seq_range: [nextArchiveStart, end],
612
+ action: compressionAction(obs.action),
613
+ summary: obs.summary,
614
+ summary_tokens: obs.summary ? Math.max(1, Math.ceil(obs.summary.length / 4)) : undefined,
615
+ archive_ref: archiveRef,
616
+ preserved_refs: preservedRefs,
617
+ });
618
+ nextArchiveStart = compressedSeq + 1;
619
+ }
620
+ else if (obs.kind === "rollbacked") {
621
+ await this.opts.sessionLog.append(sessionId, {
622
+ kind: "rollbacked",
623
+ turn: obs.turn ?? turn,
624
+ checkpoint_history_len: obs.checkpoint_history_len ?? 0,
625
+ reason: obs.reason,
626
+ });
627
+ }
628
+ else if (obs.kind === "capability_changed") {
629
+ await this.opts.sessionLog.append(sessionId, {
630
+ kind: "capability_changed",
631
+ turn: obs.turn ?? turn,
632
+ added: obs.added ?? [],
633
+ removed: obs.removed ?? [],
634
+ ...(obs.change_kind != null && { change_kind: obs.change_kind }),
635
+ ...(obs.capability_id != null && { capability_id: obs.capability_id }),
636
+ ...(obs.version != null && { version: obs.version }),
637
+ ...(obs.mounted_by != null && { mounted_by: obs.mounted_by }),
638
+ ...(obs.mount_reason != null && { mount_reason: obs.mount_reason }),
639
+ });
640
+ }
641
+ else if (obs.kind === "milestone_advanced") {
642
+ await this.opts.sessionLog.append(sessionId, {
643
+ kind: "milestone_advanced",
644
+ turn: obs.turn ?? turn,
645
+ phase_id: obs.phase_id ?? "",
646
+ capabilities_unlocked: obs.capabilities_unlocked ?? [],
647
+ });
648
+ }
649
+ else if (obs.kind === "milestone_blocked") {
650
+ await this.opts.sessionLog.append(sessionId, {
651
+ kind: "milestone_blocked",
652
+ turn: obs.turn ?? turn,
653
+ phase_id: obs.phase_id ?? "",
654
+ reason: typeof obs.reason === "string" ? obs.reason : "",
655
+ });
656
+ }
657
+ else if (obs.kind === "milestone_evidence") {
658
+ await this.opts.sessionLog.append(sessionId, {
659
+ kind: "milestone_evidence",
660
+ turn: obs.turn ?? turn,
661
+ phase_id: obs.phase_id ?? "",
662
+ evidence: obs.evidence ?? [],
663
+ });
664
+ }
665
+ else if (obs.kind === "checkpoint_taken") {
666
+ await this.opts.sessionLog.append(sessionId, {
667
+ kind: "checkpoint_taken",
668
+ turn: obs.turn ?? turn,
669
+ history_len: obs.history_len ?? 0,
670
+ });
671
+ }
672
+ else if (obs.kind === "agent_spawned") {
673
+ await this.opts.sessionLog.append(sessionId, {
674
+ kind: "agent_spawned",
675
+ turn: obs.turn ?? turn,
676
+ agent_id: obs.agent_id ?? "",
677
+ parent_session_id: obs.parent_session_id ?? "",
678
+ role: obs.role ?? "",
679
+ isolation: obs.isolation ?? "",
680
+ context_inheritance: obs.context_inheritance ?? "",
681
+ permitted_capability_ids: obs.permitted_capability_ids ?? [],
682
+ });
683
+ }
684
+ else if (obs.kind !== "renewed") {
685
+ console.warn(`[deepstrike] unhandled KernelObservation kind: ${obs.kind}`);
686
+ }
297
687
  }
298
688
  return nextArchiveStart;
299
689
  }
@@ -301,11 +691,69 @@ export class RuntimeRunner {
301
691
  function isMidRun(events) {
302
692
  return events.length > 0 && !events.some(e => e.event.kind === "run_terminal");
303
693
  }
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) {
694
+ function compressionAction(action) {
695
+ if (action === "snip_compact" ||
696
+ action === "micro_compact" ||
697
+ action === "context_collapse" ||
698
+ action === "auto_compact") {
699
+ return action;
700
+ }
701
+ return undefined;
702
+ }
703
+ export function replayMessages(events, maxBytes) {
704
+ const messages = [];
705
+ for (const { event: e } of events) {
706
+ if (e.kind === "run_started") {
707
+ const userText = e.criteria.length
708
+ ? `${e.goal}\n\nCriteria:\n${e.criteria.map((c, i) => `${i + 1}. ${c}`).join("\n")}`
709
+ : e.goal;
710
+ messages.push({
711
+ role: "user",
712
+ content: userText,
713
+ toolCalls: [],
714
+ tokenCount: Math.max(1, Math.ceil(userText.length / 4)),
715
+ });
716
+ }
717
+ else if (e.kind === "compressed") {
718
+ if (e.summary) {
719
+ const systemText = `[Compressed context: turn ${e.turn}]\n${e.summary}`;
720
+ messages.push({
721
+ role: "system",
722
+ content: systemText,
723
+ toolCalls: [],
724
+ tokenCount: Math.max(1, Math.ceil(systemText.length / 4)),
725
+ });
726
+ }
727
+ }
728
+ else if (e.kind === "llm_completed") {
729
+ messages.push({
730
+ role: "assistant",
731
+ content: sanitizeReplayText(e.content, maxBytes),
732
+ toolCalls: e.tool_calls ?? [],
733
+ tokenCount: e.token_count,
734
+ });
735
+ }
736
+ else if (e.kind === "tool_completed") {
737
+ for (const r of e.results) {
738
+ messages.push({
739
+ role: "tool",
740
+ content: "",
741
+ toolCalls: [],
742
+ contentParts: [{ type: "tool_result", callId: r.call_id, output: sanitizeReplayText(r.output, maxBytes), isError: r.is_error ?? false }],
743
+ tokenCount: r.token_count,
744
+ });
745
+ }
746
+ }
747
+ else if (e.kind === "rollbacked") {
748
+ const len = e.checkpoint_history_len ?? 0;
749
+ if (messages.length > len) {
750
+ messages.length = len;
751
+ }
752
+ }
753
+ }
754
+ return messages;
755
+ }
756
+ export async function replayMessagesAsync(events, maxBytes, loadArchive) {
309
757
  const messages = [];
310
758
  for (const { event: e } of events) {
311
759
  if (e.kind === "run_started") {
@@ -319,10 +767,41 @@ function replayMessages(events) {
319
767
  tokenCount: Math.max(1, Math.ceil(userText.length / 4)),
320
768
  });
321
769
  }
770
+ else if (e.kind === "compressed") {
771
+ let loadedSuccessfully = false;
772
+ if (e.archive_ref && loadArchive) {
773
+ try {
774
+ const archivedMsgs = await loadArchive(e.archive_ref);
775
+ for (const msg of archivedMsgs) {
776
+ messages.push({
777
+ role: msg.role,
778
+ content: sanitizeReplayText(msg.content, maxBytes),
779
+ toolCalls: msg.toolCalls ?? [],
780
+ tokenCount: msg.tokenCount,
781
+ });
782
+ }
783
+ loadedSuccessfully = true;
784
+ }
785
+ catch (err) {
786
+ // Loader failed (e.g. MissingArchive). We degrade and fallback.
787
+ }
788
+ }
789
+ if (!loadedSuccessfully) {
790
+ if (e.summary) {
791
+ const systemText = `[Compressed context: turn ${e.turn}]\n${e.summary}`;
792
+ messages.push({
793
+ role: "system",
794
+ content: systemText,
795
+ toolCalls: [],
796
+ tokenCount: Math.max(1, Math.ceil(systemText.length / 4)),
797
+ });
798
+ }
799
+ }
800
+ }
322
801
  else if (e.kind === "llm_completed") {
323
802
  messages.push({
324
803
  role: "assistant",
325
- content: e.content,
804
+ content: sanitizeReplayText(e.content, maxBytes),
326
805
  toolCalls: e.tool_calls ?? [],
327
806
  tokenCount: e.token_count,
328
807
  });
@@ -333,11 +812,17 @@ function replayMessages(events) {
333
812
  role: "tool",
334
813
  content: "",
335
814
  toolCalls: [],
336
- contentParts: [{ type: "tool_result", callId: r.call_id, output: r.output, isError: r.is_error ?? false }],
815
+ contentParts: [{ type: "tool_result", callId: r.call_id, output: sanitizeReplayText(r.output, maxBytes), isError: r.is_error ?? false }],
337
816
  tokenCount: r.token_count,
338
817
  });
339
818
  }
340
819
  }
820
+ else if (e.kind === "rollbacked") {
821
+ const len = e.checkpoint_history_len ?? 0;
822
+ if (messages.length > len) {
823
+ messages.length = len;
824
+ }
825
+ }
341
826
  }
342
827
  return messages;
343
828
  }
@@ -366,3 +851,21 @@ export async function collectText(stream) {
366
851
  }
367
852
  return text;
368
853
  }
854
+ function parseUpdatePlanArgs(argsStr) {
855
+ let parsed = {};
856
+ try {
857
+ parsed = JSON.parse(argsStr);
858
+ }
859
+ catch {
860
+ // Ignore parse error
861
+ }
862
+ return {
863
+ plan: parsed.plan,
864
+ currentStep: parsed.currentStep !== undefined ? Number(parsed.currentStep) : parsed.current_step !== undefined ? Number(parsed.current_step) : undefined,
865
+ progress: parsed.progress,
866
+ scratchpad: parsed.scratchpad,
867
+ blockedOn: parsed.blockedOn !== undefined
868
+ ? parsed.blockedOn
869
+ : parsed.blocked_on,
870
+ };
871
+ }