@deepstrike/wasm 0.1.16 → 0.2.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.
@@ -1,14 +1,24 @@
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 { forceCompact, kernelAction, kernelApply, messageToKernelMessage, skillMetadataToKernel, toolResultToKernel, toolSchemaToKernel, } from "./kernel-step.js";
6
+ import { agentRunSpecToKernel, subAgentResultToKernel, milestoneCheckPass, milestoneCheckResultToKernel } 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
+ pendingObservations = [];
12
+ activeKernel = null;
13
+ currentSessionId = null;
14
+ nextArchiveStart = 0;
6
15
  constructor(opts) {
7
16
  this.opts = opts;
8
17
  }
18
+ get hostOptions() { return this.opts; }
9
19
  interrupt() { this.interrupted = true; }
10
20
  async *run(req) {
11
- const prior = await this.opts.sessionLog.read(req.sessionId);
21
+ const prior = req.inheritEvents ?? await this.opts.sessionLog.read(req.sessionId);
12
22
  const midRun = isMidRun(prior);
13
23
  if (!midRun) {
14
24
  await this.opts.sessionLog.append(req.sessionId, {
@@ -32,6 +42,16 @@ export class RuntimeRunner {
32
42
  const start = startEntry.event;
33
43
  yield* this.execute(sessionId, start.goal, start.criteria, extensions, events, true);
34
44
  }
45
+ /** Push a large artifact into the kernel artifacts partition (not inlined in history). */
46
+ pushArtifact(message, tokens) {
47
+ if (!this.activeKernel)
48
+ return;
49
+ kernelApply(this.activeKernel, this.pendingObservations, {
50
+ kind: "push_artifact",
51
+ message: messageToKernelMessage(message),
52
+ ...(tokens !== undefined ? { tokens } : {}),
53
+ });
54
+ }
35
55
  async dream(agentId, nowMs = Date.now()) {
36
56
  if (!this.opts.dreamStore)
37
57
  throw new Error("dreamStore not configured");
@@ -94,6 +114,8 @@ export class RuntimeRunner {
94
114
  }
95
115
  async *execute(sessionId, goal, criteria, extensions, priorEvents, resumeMidRun = false) {
96
116
  this.interrupted = false;
117
+ this.pendingObservations = [];
118
+ this.currentSessionId = sessionId;
97
119
  const kernel = await getKernel();
98
120
  this.opts.governance?._attach(kernel);
99
121
  const ext = { ...this.opts.extensions, ...(extensions ?? {}) };
@@ -102,19 +124,31 @@ export class RuntimeRunner {
102
124
  const providerPolicy = this.opts.provider.runtimePolicy?.() ?? {};
103
125
  const effectiveMaxTurns = this.opts.maxTurns ?? providerPolicy.maxTurns ?? 25;
104
126
  const effectiveTimeoutMs = this.opts.timeoutMs ?? providerPolicy.timeoutMs;
105
- const sm = new kernel.LoopStateMachine({
127
+ const runtime = new kernel.KernelRuntime({
106
128
  maxTokens: this.opts.maxTokens,
107
129
  maxTurns: effectiveMaxTurns,
108
130
  timeoutMs: effectiveTimeoutMs !== undefined ? BigInt(effectiveTimeoutMs) : undefined,
109
131
  });
132
+ this.activeKernel = runtime;
110
133
  const router = new kernel.SignalRouter(256);
111
- sm.setTools(this.opts.executionPlane.schemas());
134
+ kernelApply(runtime, this.pendingObservations, {
135
+ kind: "set_tools",
136
+ tools: this.opts.executionPlane.schemas().map(toolSchemaToKernel),
137
+ });
112
138
  if (this.opts.systemPrompt) {
113
- sm.addSystemMessage(this.opts.systemPrompt, Math.max(1, Math.ceil(this.opts.systemPrompt.length / 4)));
139
+ kernelApply(runtime, this.pendingObservations, {
140
+ kind: "add_system_message",
141
+ content: this.opts.systemPrompt,
142
+ tokens: Math.max(1, Math.ceil(this.opts.systemPrompt.length / 4)),
143
+ });
114
144
  }
115
145
  if (this.opts.initialMemory) {
116
146
  for (const mem of this.opts.initialMemory) {
117
- sm.addMemoryMessage(mem, Math.max(1, Math.ceil(mem.length / 4)));
147
+ kernelApply(runtime, this.pendingObservations, {
148
+ kind: "add_memory_message",
149
+ content: mem,
150
+ tokens: Math.max(1, Math.ceil(mem.length / 4)),
151
+ });
118
152
  }
119
153
  }
120
154
  if (this.opts.skillContentMap && this.opts.skillContentMap.size > 0) {
@@ -123,24 +157,56 @@ export class RuntimeRunner {
123
157
  description: "",
124
158
  estimatedTokens: 0,
125
159
  }));
126
- sm.setAvailableSkills(metas);
160
+ kernelApply(runtime, this.pendingObservations, {
161
+ kind: "set_available_skills",
162
+ skills: metas.map(skillMetadataToKernel),
163
+ });
164
+ }
165
+ if (this.opts.dreamStore && this.opts.agentId) {
166
+ kernelApply(runtime, this.pendingObservations, { kind: "set_memory_enabled", enabled: true });
167
+ }
168
+ if (this.opts.knowledgeSource) {
169
+ kernelApply(runtime, this.pendingObservations, { kind: "set_knowledge_enabled", enabled: true });
127
170
  }
128
- if (this.opts.dreamStore && this.opts.agentId)
129
- sm.setMemoryEnabled(true);
130
- if (this.opts.knowledgeSource)
131
- sm.setKnowledgeEnabled(true);
171
+ if (this.opts.milestoneContract) {
172
+ kernelApply(runtime, this.pendingObservations, {
173
+ kind: "load_milestone_contract",
174
+ contract: {
175
+ phases: this.opts.milestoneContract.phases.map(p => ({
176
+ id: p.id,
177
+ criteria: p.criteria ?? [],
178
+ unlocks: p.unlocks ?? [],
179
+ verifier: p.verifier ?? null,
180
+ required_evidence: p.requiredEvidence ?? [],
181
+ })),
182
+ },
183
+ });
184
+ }
185
+ const maxBytes = runtime.recoveryContentBytes();
132
186
  if (priorEvents && priorEvents.length > 0) {
133
- seedProviderReplayFromEvents(this.opts.provider, priorEvents);
134
- sm.preloadHistory(replayMessages(priorEvents));
187
+ const repaired = repairEventsForRecovery(priorEvents, maxBytes);
188
+ seedProviderReplayFromEvents(this.opts.provider, repaired);
189
+ kernelApply(runtime, this.pendingObservations, {
190
+ kind: "preload_history",
191
+ messages: replayMessages(repaired, maxBytes).map(messageToKernelMessage),
192
+ });
135
193
  }
136
194
  const sessionStart = Date.now();
195
+ const startPayload = {
196
+ kind: "start_run",
197
+ task: { goal, criteria },
198
+ };
199
+ if (this.opts.runSpec) {
200
+ startPayload.run_spec = agentRunSpecToKernel(this.opts.runSpec);
201
+ }
137
202
  let action = resumeMidRun
138
- ? sm.resumeAfterPreload()
139
- : sm.start({ goal, criteria });
140
- while (!sm.isTerminal()) {
141
- nextCompressedArchiveStart = await this.appendObservations(sessionId, sm, nextCompressedArchiveStart);
203
+ ? kernelAction(runtime, this.pendingObservations, { kind: "resume" })
204
+ : kernelAction(runtime, this.pendingObservations, startPayload);
205
+ let hasAttemptedReactiveCompact = false;
206
+ while (!runtime.isTerminal()) {
207
+ nextCompressedArchiveStart = await this.appendObservations(sessionId, runtime, nextCompressedArchiveStart);
142
208
  if (this.interrupted) {
143
- sm.feedTimeout();
209
+ action = kernelAction(runtime, this.pendingObservations, { kind: "timeout" });
144
210
  break;
145
211
  }
146
212
  if (this.opts.signalSource) {
@@ -156,9 +222,9 @@ export class RuntimeRunner {
156
222
  dedupeKey: sig.dedupeKey,
157
223
  timestampMs: Date.now(),
158
224
  };
159
- const disposition = router.ingest(kernelSig, action.kind === "execute_tools");
225
+ const disposition = router.ingest(kernelSig, action.kind === "execute_tool");
160
226
  if (disposition === "interrupt_now") {
161
- sm.feedTimeout();
227
+ action = kernelAction(runtime, this.pendingObservations, { kind: "timeout" });
162
228
  break;
163
229
  }
164
230
  }
@@ -166,19 +232,20 @@ export class RuntimeRunner {
166
232
  let queued = router.next();
167
233
  while (queued) {
168
234
  if (queued.urgency === "critical") {
169
- sm.feedTimeout();
235
+ action = kernelAction(runtime, this.pendingObservations, { kind: "timeout" });
170
236
  break;
171
237
  }
172
238
  queued = router.next();
173
239
  }
174
- if (sm.isTerminal())
240
+ if (runtime.isTerminal())
175
241
  break;
176
- if (action.kind === "call_llm") {
242
+ if (action.kind === "call_provider") {
177
243
  const finalToolCalls = [];
178
244
  let finalText = "";
179
245
  const context = action.context;
180
- const tools = (action.tools ?? []);
246
+ const tools = action.tools;
181
247
  let turnTokens = 0;
248
+ let shouldRetry = false;
182
249
  try {
183
250
  for await (const evt of this.opts.provider.stream(context, tools, Object.keys(ext).length ? ext : undefined, providerState)) {
184
251
  if (evt.type === "usage") {
@@ -195,29 +262,51 @@ export class RuntimeRunner {
195
262
  }
196
263
  }
197
264
  catch (err) {
198
- yield { type: "error", message: String(err) };
199
- sm.feedTimeout();
200
- break;
265
+ const errMsg = String(err).toLowerCase();
266
+ if ((errMsg.includes("413") || errMsg.includes("too long") || errMsg.includes("context length exceeded") || errMsg.includes("context_length_exceeded")) &&
267
+ !hasAttemptedReactiveCompact) {
268
+ hasAttemptedReactiveCompact = true;
269
+ if (forceCompact(runtime, this.pendingObservations)) {
270
+ nextCompressedArchiveStart = await this.appendObservations(sessionId, runtime, nextCompressedArchiveStart);
271
+ shouldRetry = true;
272
+ }
273
+ }
274
+ if (!shouldRetry) {
275
+ yield { type: "error", message: String(err) };
276
+ action = kernelAction(runtime, this.pendingObservations, { kind: "timeout" });
277
+ break;
278
+ }
279
+ }
280
+ if (shouldRetry) {
281
+ action = {
282
+ kind: "call_provider",
283
+ context: runtime.render(),
284
+ tools,
285
+ };
286
+ continue;
201
287
  }
202
- action = sm.feedLlmResponse({
288
+ const assistantMessage = {
203
289
  role: "assistant",
204
290
  content: finalText,
205
291
  toolCalls: finalToolCalls,
206
292
  tokenCount: turnTokens || undefined,
293
+ };
294
+ action = kernelAction(runtime, this.pendingObservations, {
295
+ kind: "provider_result",
296
+ message: messageToKernelMessage(assistantMessage),
207
297
  });
208
298
  const providerReplay = peekProviderReplay(this.opts.provider, finalText, finalToolCalls);
209
- await this.opts.sessionLog.append(sessionId, {
210
- kind: "llm_completed",
211
- turn: sm.turn,
299
+ await this.opts.sessionLog.append(sessionId, buildLlmCompletedEvent({
300
+ turn: runtime.turn(),
212
301
  content: finalText,
213
- token_count: turnTokens || undefined,
214
- tool_calls: finalToolCalls,
215
- ...(providerReplay ? { provider_replay: providerReplay } : {}),
216
- });
302
+ tokenCount: turnTokens || undefined,
303
+ toolCalls: finalToolCalls,
304
+ providerReplay,
305
+ }));
217
306
  }
218
- else if (action.kind === "execute_tools") {
219
- const allCalls = (action.calls ?? []);
220
- await this.opts.sessionLog.append(sessionId, { kind: "tool_requested", turn: sm.turn, calls: allCalls });
307
+ else if (action.kind === "execute_tool") {
308
+ const allCalls = action.calls;
309
+ await this.opts.sessionLog.append(sessionId, { kind: "tool_requested", turn: runtime.turn(), calls: allCalls });
221
310
  const runCtx = {
222
311
  agentId: this.opts.agentId,
223
312
  skillContentMap: this.opts.skillContentMap,
@@ -231,12 +320,62 @@ export class RuntimeRunner {
231
320
  yield evt;
232
321
  if (evt.type === "tool_result") {
233
322
  const tre = evt;
234
- toolResults.push({ callId: tre.callId, output: tre.content, isError: tre.isError });
323
+ toolResults.push({
324
+ callId: tre.callId,
325
+ output: tre.content,
326
+ isError: tre.isError,
327
+ isFatal: tre.isFatal,
328
+ errorKind: tre.errorKind,
329
+ });
330
+ }
331
+ else if (evt.type === "tool_argument_repaired") {
332
+ const tare = evt;
333
+ await this.opts.sessionLog.append(sessionId, {
334
+ kind: "tool_argument_repaired",
335
+ turn: runtime.turn(),
336
+ tool: tare.name,
337
+ original_arguments: tare.originalArguments,
338
+ repaired_arguments: tare.repairedArguments,
339
+ });
340
+ }
341
+ else if (evt.type === "tool_denied") {
342
+ const tde = evt;
343
+ await this.opts.sessionLog.append(sessionId, {
344
+ kind: "tool_denied",
345
+ turn: runtime.turn(),
346
+ call_id: tde.callId,
347
+ tool_name: tde.toolName,
348
+ reason: tde.reason,
349
+ });
350
+ }
351
+ else if (evt.type === "permission_request") {
352
+ const pre = evt;
353
+ const turn = runtime.turn();
354
+ await this.opts.sessionLog.append(sessionId, {
355
+ kind: "permission_requested",
356
+ turn,
357
+ tool: pre.toolName,
358
+ arguments: typeof pre.arguments === "string" ? pre.arguments : JSON.stringify(pre.arguments),
359
+ reason: pre.reason,
360
+ });
361
+ await this.opts.sessionLog.append(sessionId, {
362
+ kind: "permission_resolved",
363
+ turn,
364
+ approved: false,
365
+ responder: "policy_gate",
366
+ });
367
+ await this.opts.sessionLog.append(sessionId, {
368
+ kind: "tool_denied",
369
+ turn,
370
+ call_id: pre.callId,
371
+ tool_name: pre.toolName,
372
+ reason: `permission denied by policy gate: ${pre.reason}`,
373
+ });
235
374
  }
236
375
  }
237
376
  await this.opts.sessionLog.append(sessionId, {
238
377
  kind: "tool_completed",
239
- turn: sm.turn,
378
+ turn: runtime.turn(),
240
379
  results: toolResults.map(r => ({
241
380
  call_id: r.callId,
242
381
  output: r.output,
@@ -244,20 +383,62 @@ export class RuntimeRunner {
244
383
  token_count: r.tokenCount,
245
384
  })),
246
385
  });
247
- action = sm.feedToolResults(toolResults);
386
+ action = kernelAction(runtime, this.pendingObservations, {
387
+ kind: "tool_results",
388
+ results: toolResults.map(toolResultToKernel),
389
+ });
390
+ }
391
+ else if (action.kind === "evaluate_milestone") {
392
+ const milestonePolicy = this.opts.milestonePolicy ?? "require_verifier";
393
+ if (milestonePolicy === "auto_pass") {
394
+ action = kernelAction(runtime, this.pendingObservations, {
395
+ kind: "milestone_result",
396
+ result: milestoneCheckResultToKernel(milestoneCheckPass(action.phaseId)),
397
+ });
398
+ nextCompressedArchiveStart = await this.appendObservations(sessionId, runtime, nextCompressedArchiveStart);
399
+ }
400
+ else if (this.opts.onMilestoneEvaluate) {
401
+ const check = await this.opts.onMilestoneEvaluate({
402
+ phaseId: action.phaseId,
403
+ criteria: action.criteria ?? [],
404
+ requiredEvidence: action.requiredEvidence ?? [],
405
+ });
406
+ action = kernelAction(runtime, this.pendingObservations, {
407
+ kind: "milestone_result",
408
+ result: milestoneCheckResultToKernel(check),
409
+ });
410
+ nextCompressedArchiveStart = await this.appendObservations(sessionId, runtime, nextCompressedArchiveStart);
411
+ }
412
+ else {
413
+ nextCompressedArchiveStart = await this.appendObservations(sessionId, runtime, nextCompressedArchiveStart);
414
+ const turnsUsed = Math.max(1, runtime.turn());
415
+ await this.opts.sessionLog.append(sessionId, buildRunTerminalEvent({
416
+ reason: "milestone_pending",
417
+ turnsUsed,
418
+ totalTokens: 0,
419
+ }));
420
+ yield { type: "done", iterations: turnsUsed, totalTokens: 0, status: "milestone_pending" };
421
+ this.activeKernel = null;
422
+ this.currentSessionId = null;
423
+ return;
424
+ }
248
425
  }
249
426
  else if (action.kind === "done") {
250
427
  break;
251
428
  }
252
429
  }
253
- const result = action.result;
430
+ const result = action.kind === "done" ? action.result : undefined;
254
431
  const status = result?.termination ?? "error";
255
- const turnsUsed = result ? Math.max(1, result.turnsUsed ?? 0) : 0;
256
- const totalTokens = result?.totalTokensUsed ? Number(result.totalTokensUsed) : 0;
257
- nextCompressedArchiveStart = await this.appendObservations(sessionId, sm, nextCompressedArchiveStart);
258
- await this.opts.sessionLog.append(sessionId, { kind: "run_terminal", reason: status, turns_used: turnsUsed, total_tokens: totalTokens });
432
+ const turnsUsed = result ? Math.max(1, result.turnsUsed) : runtime.turn() || 0;
433
+ const totalTokens = result?.totalTokensUsed ?? 0;
434
+ nextCompressedArchiveStart = await this.appendObservations(sessionId, runtime, nextCompressedArchiveStart);
435
+ await this.opts.sessionLog.append(sessionId, buildRunTerminalEvent({
436
+ reason: status,
437
+ turnsUsed,
438
+ totalTokens,
439
+ }));
259
440
  if (this.opts.dreamStore && this.opts.agentId) {
260
- const newMsgs = sm.drainNewMessages().map(m => ({
441
+ const newMsgs = runtime.drainNewMessages().map(m => ({
261
442
  role: m.role,
262
443
  content: m.content,
263
444
  tokenCount: m.tokenCount,
@@ -278,22 +459,138 @@ export class RuntimeRunner {
278
459
  }
279
460
  }
280
461
  yield { type: "done", iterations: turnsUsed, totalTokens, status };
462
+ this.activeKernel = null;
463
+ this.currentSessionId = null;
281
464
  }
282
- async appendObservations(sessionId, sm, nextArchiveStart) {
283
- const observations = sm.takeObservations();
465
+ async spawnSubAgent(spec) {
466
+ if (!this.activeKernel || !this.currentSessionId) {
467
+ throw new Error("spawnSubAgent requires an active parent run");
468
+ }
469
+ const parentSessionId = this.currentSessionId;
470
+ const runtime = this.activeKernel;
471
+ const observations = kernelApply(runtime, this.pendingObservations, {
472
+ kind: "spawn_sub_agent",
473
+ spec: agentRunSpecToKernel(spec),
474
+ parent_session_id: parentSessionId,
475
+ });
476
+ this.nextArchiveStart = await this.appendObservations(parentSessionId, runtime, this.nextArchiveStart);
477
+ const spawned = observations.find(o => o.kind === "agent_spawned" && typeof o.agent_id === "string");
478
+ if (!spawned)
479
+ throw new Error("spawn_sub_agent did not emit agent_spawned");
480
+ const manifest = {
481
+ kind: "agent_spawned",
482
+ turn: spawned.turn,
483
+ agent_id: spawned.agent_id,
484
+ parent_session_id: spawned.parent_session_id ?? parentSessionId,
485
+ role: spawned.role ?? spec.role,
486
+ isolation: spawned.isolation ?? spec.isolation ?? "shared",
487
+ context_inheritance: spawned.context_inheritance ?? "none",
488
+ permitted_capability_ids: spawned.permitted_capability_ids ?? [],
489
+ };
490
+ const orchestrator = this.opts.subAgentOrchestrator ?? defaultSubAgentOrchestrator;
491
+ const result = await orchestrator.run({
492
+ parentOpts: this.opts,
493
+ parentSessionId,
494
+ spec,
495
+ manifest,
496
+ sessionLog: this.opts.sessionLog,
497
+ });
498
+ kernelApply(runtime, this.pendingObservations, {
499
+ kind: "sub_agent_completed",
500
+ result: subAgentResultToKernel(result),
501
+ });
502
+ return result;
503
+ }
504
+ async appendObservations(sessionId, runtime, nextArchiveStart) {
505
+ const turn = runtime.turn();
506
+ const preservedRefs = runtime.preservedRefs();
507
+ const observations = this.pendingObservations.splice(0);
284
508
  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;
509
+ if (obs.kind === "compressed") {
510
+ const latest = await this.opts.sessionLog.latestSeq(sessionId);
511
+ if (latest < nextArchiveStart)
512
+ continue;
513
+ const end = latest;
514
+ const compressedSeq = await this.opts.sessionLog.append(sessionId, {
515
+ kind: "compressed",
516
+ turn,
517
+ archived_seq_range: [nextArchiveStart, end],
518
+ action: compressionAction(obs.action),
519
+ summary: obs.summary,
520
+ summary_tokens: obs.summary
521
+ ? Math.max(1, Math.ceil(obs.summary.length / 4))
522
+ : undefined,
523
+ preserved_refs: preservedRefs,
524
+ });
525
+ nextArchiveStart = compressedSeq + 1;
526
+ }
527
+ else if (obs.kind === "rollbacked") {
528
+ await this.opts.sessionLog.append(sessionId, {
529
+ kind: "rollbacked",
530
+ turn: obs.turn ?? turn,
531
+ checkpoint_history_len: obs.checkpoint_history_len ?? 0,
532
+ reason: obs.reason,
533
+ });
534
+ }
535
+ else if (obs.kind === "capability_changed") {
536
+ await this.opts.sessionLog.append(sessionId, {
537
+ kind: "capability_changed",
538
+ turn: obs.turn ?? turn,
539
+ added: obs.added ?? [],
540
+ removed: obs.removed ?? [],
541
+ ...(obs.change_kind != null && { change_kind: obs.change_kind }),
542
+ ...(obs.capability_id != null && { capability_id: obs.capability_id }),
543
+ ...(obs.version != null && { version: obs.version }),
544
+ ...(obs.mounted_by != null && { mounted_by: obs.mounted_by }),
545
+ ...(obs.mount_reason != null && { mount_reason: obs.mount_reason }),
546
+ });
547
+ }
548
+ else if (obs.kind === "milestone_advanced") {
549
+ await this.opts.sessionLog.append(sessionId, {
550
+ kind: "milestone_advanced",
551
+ turn: obs.turn ?? turn,
552
+ phase_id: obs.phase_id ?? "",
553
+ capabilities_unlocked: obs.capabilities_unlocked ?? [],
554
+ });
555
+ }
556
+ else if (obs.kind === "milestone_blocked") {
557
+ await this.opts.sessionLog.append(sessionId, {
558
+ kind: "milestone_blocked",
559
+ turn: obs.turn ?? turn,
560
+ phase_id: obs.phase_id ?? "",
561
+ reason: typeof obs.reason === "string" ? obs.reason : "",
562
+ });
563
+ }
564
+ else if (obs.kind === "milestone_evidence") {
565
+ await this.opts.sessionLog.append(sessionId, {
566
+ kind: "milestone_evidence",
567
+ turn: obs.turn ?? turn,
568
+ phase_id: obs.phase_id ?? "",
569
+ evidence: obs.evidence ?? [],
570
+ });
571
+ }
572
+ else if (obs.kind === "checkpoint_taken") {
573
+ await this.opts.sessionLog.append(sessionId, {
574
+ kind: "checkpoint_taken",
575
+ turn: obs.turn ?? turn,
576
+ history_len: obs.history_len ?? 0,
577
+ });
578
+ }
579
+ else if (obs.kind === "agent_spawned") {
580
+ await this.opts.sessionLog.append(sessionId, {
581
+ kind: "agent_spawned",
582
+ turn: obs.turn ?? turn,
583
+ agent_id: obs.agent_id ?? "",
584
+ parent_session_id: obs.parent_session_id ?? "",
585
+ role: obs.role ?? "",
586
+ isolation: obs.isolation ?? "",
587
+ context_inheritance: obs.context_inheritance ?? "",
588
+ permitted_capability_ids: obs.permitted_capability_ids ?? [],
589
+ });
590
+ }
591
+ else if (obs.kind !== "renewed") {
592
+ console.warn(`[deepstrike] unhandled KernelObservation kind: ${obs.kind}`);
593
+ }
297
594
  }
298
595
  return nextArchiveStart;
299
596
  }
@@ -301,7 +598,16 @@ export class RuntimeRunner {
301
598
  function isMidRun(events) {
302
599
  return events.length > 0 && !events.some(e => e.event.kind === "run_terminal");
303
600
  }
304
- function replayMessages(events) {
601
+ function compressionAction(action) {
602
+ if (action === "snip_compact" ||
603
+ action === "micro_compact" ||
604
+ action === "context_collapse" ||
605
+ action === "auto_compact") {
606
+ return action;
607
+ }
608
+ return undefined;
609
+ }
610
+ function replayMessages(events, maxBytes) {
305
611
  const messages = [];
306
612
  for (const { event: e } of events) {
307
613
  if (e.kind === "run_started") {
@@ -315,10 +621,21 @@ function replayMessages(events) {
315
621
  tokenCount: Math.max(1, Math.ceil(userText.length / 4)),
316
622
  });
317
623
  }
624
+ else if (e.kind === "compressed") {
625
+ if (e.summary) {
626
+ const systemText = `[Compressed context: turn ${e.turn}]\n${e.summary}`;
627
+ messages.push({
628
+ role: "system",
629
+ content: systemText,
630
+ toolCalls: [],
631
+ tokenCount: Math.max(1, Math.ceil(systemText.length / 4)),
632
+ });
633
+ }
634
+ }
318
635
  else if (e.kind === "llm_completed") {
319
636
  messages.push({
320
637
  role: "assistant",
321
- content: e.content,
638
+ content: sanitizeReplayText(e.content, maxBytes),
322
639
  toolCalls: e.tool_calls ?? [],
323
640
  tokenCount: e.token_count,
324
641
  });
@@ -327,12 +644,18 @@ function replayMessages(events) {
327
644
  for (const r of e.results) {
328
645
  messages.push({
329
646
  role: "tool",
330
- content: r.output,
647
+ content: sanitizeReplayText(r.output, maxBytes),
331
648
  toolCalls: [],
332
649
  tokenCount: r.token_count,
333
650
  });
334
651
  }
335
652
  }
653
+ else if (e.kind === "rollbacked") {
654
+ const len = e.checkpoint_history_len ?? 0;
655
+ if (messages.length > len) {
656
+ messages.length = len;
657
+ }
658
+ }
336
659
  }
337
660
  return messages;
338
661
  }