@deepstrike/wasm 0.2.2 → 0.2.5
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 +29 -2
- package/dist/governance.d.ts +32 -0
- package/dist/governance.js +18 -0
- package/dist/index.d.ts +2 -2
- package/dist/providers/anthropic.js +20 -3
- package/dist/providers/base.js +11 -1
- package/dist/runtime/execution-plane.d.ts +6 -3
- package/dist/runtime/execution-plane.js +54 -23
- package/dist/runtime/kernel-event-log.d.ts +26 -0
- package/dist/runtime/kernel-event-log.js +212 -0
- package/dist/runtime/kernel-step.d.ts +21 -1
- package/dist/runtime/kernel-step.js +9 -0
- package/dist/runtime/large-result-spool.d.ts +49 -0
- package/dist/runtime/large-result-spool.js +137 -0
- package/dist/runtime/os-profile.d.ts +7 -0
- package/dist/runtime/os-profile.js +6 -0
- package/dist/runtime/os-snapshot.d.ts +31 -0
- package/dist/runtime/os-snapshot.js +108 -0
- package/dist/runtime/runner.d.ts +20 -5
- package/dist/runtime/runner.js +305 -147
- package/dist/runtime/session-log.d.ts +110 -3
- package/dist/runtime/session-log.js +10 -2
- package/dist/runtime/sub-agent-orchestrator.d.ts +2 -2
- package/dist/runtime/sub-agent-orchestrator.js +14 -23
- package/dist/runtime/types/agent.d.ts +12 -3
- package/dist/runtime/types/agent.js +18 -0
- package/dist/types.d.ts +24 -0
- package/package.json +2 -2
package/dist/runtime/runner.js
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
|
+
import { resolvePermissionRequest } from "./execution-plane.js";
|
|
2
|
+
import { governancePolicyToKernelEvent } from "../governance.js";
|
|
1
3
|
import { getKernel } from "./kernel.js";
|
|
2
4
|
import { peekProviderReplay, seedProviderReplayFromEvents } from "./provider-replay.js";
|
|
3
5
|
import { sanitizeReplayText } from "./replay-sanitize.js";
|
|
4
6
|
import { buildLlmCompletedEvent, buildRunTerminalEvent, repairEventsForRecovery } from "./session-repair.js";
|
|
5
|
-
import { forceCompact, kernelAction, kernelApply, messageToKernelMessage, skillMetadataToKernel, toolResultToKernel, toolSchemaToKernel, } from "./kernel-step.js";
|
|
6
|
-
import { agentRunSpecToKernel,
|
|
7
|
+
import { forceCompact, kernelAction, kernelApply, kernelMaybeAction, messageToKernelMessage, skillMetadataToKernel, toolResultToKernel, toolSchemaToKernel, } from "./kernel-step.js";
|
|
8
|
+
import { agentRunSpecToKernel, findSpawnProcessObservation, milestoneCheckPass, milestoneCheckResultToKernel, spawnObservationToManifest, subAgentResultToKernel, } from "./types/agent.js";
|
|
7
9
|
import { defaultSubAgentOrchestrator } from "./sub-agent-orchestrator.js";
|
|
10
|
+
import { kernelObservationToSessionEvent, withCategory } from "./kernel-event-log.js";
|
|
11
|
+
import { DEFAULT_NATIVE_ATTENTION_POLICY, DEFAULT_NATIVE_GOVERNANCE_POLICY } from "./os-profile.js";
|
|
12
|
+
import { LargeResultSpool } from "./large-result-spool.js";
|
|
8
13
|
export class RuntimeRunner {
|
|
9
14
|
opts;
|
|
10
15
|
interrupted = false;
|
|
@@ -12,6 +17,8 @@ export class RuntimeRunner {
|
|
|
12
17
|
activeKernel = null;
|
|
13
18
|
currentSessionId = null;
|
|
14
19
|
nextArchiveStart = 0;
|
|
20
|
+
localPageOutCache = [];
|
|
21
|
+
pendingSpoolOutputs = new Map();
|
|
15
22
|
constructor(opts) {
|
|
16
23
|
this.opts = opts;
|
|
17
24
|
}
|
|
@@ -42,21 +49,142 @@ export class RuntimeRunner {
|
|
|
42
49
|
const start = startEntry.event;
|
|
43
50
|
yield* this.execute(sessionId, start.goal, start.criteria, extensions, events, true);
|
|
44
51
|
}
|
|
45
|
-
/** Push
|
|
46
|
-
|
|
52
|
+
/** Push content into Slot 2 (system_knowledge) via add_knowledge_message. */
|
|
53
|
+
pushKnowledge(message, tokens) {
|
|
47
54
|
if (!this.activeKernel)
|
|
48
55
|
return;
|
|
49
56
|
kernelApply(this.activeKernel, this.pendingObservations, {
|
|
50
|
-
kind: "
|
|
51
|
-
|
|
52
|
-
|
|
57
|
+
kind: "add_knowledge_message",
|
|
58
|
+
content: message.content ?? "",
|
|
59
|
+
tokens: tokens ?? Math.max(1, Math.ceil((message.content?.length ?? 0) / 4)),
|
|
53
60
|
});
|
|
54
61
|
}
|
|
62
|
+
/** Phase 4: satisfy kernel page-in requests before meta-tool execution. */
|
|
63
|
+
async applyKernelPageIn(runtime, sessionId) {
|
|
64
|
+
const requests = this.pendingObservations.filter((o) => o.kind === "page_in_requested" && typeof o.tool === "string");
|
|
65
|
+
if (requests.length === 0)
|
|
66
|
+
return;
|
|
67
|
+
const entries = [];
|
|
68
|
+
for (const req of requests) {
|
|
69
|
+
const query = typeof req.query === "string" ? req.query : "";
|
|
70
|
+
const topK = typeof req.top_k === "number" ? req.top_k : 5;
|
|
71
|
+
if (req.tool === "memory") {
|
|
72
|
+
const localHits = this.localPageOutCache.filter(m => typeof m.content === "string" && m.content.toLowerCase().includes(query.toLowerCase())).slice(0, topK);
|
|
73
|
+
for (const hit of localHits) {
|
|
74
|
+
entries.push({
|
|
75
|
+
content: `[local semantic cache] ${hit.role}: ${hit.content}`,
|
|
76
|
+
source: "semantic_cache",
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
const remainingK = topK - entries.length;
|
|
80
|
+
if (remainingK > 0 && this.opts.dreamStore && this.opts.agentId) {
|
|
81
|
+
const hits = await this.opts.dreamStore.search(this.opts.agentId, query, remainingK);
|
|
82
|
+
for (const hit of hits) {
|
|
83
|
+
entries.push({
|
|
84
|
+
content: `[memory score=${hit.score.toFixed(3)}] ${hit.text}`,
|
|
85
|
+
source: "memory",
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else if (req.tool === "knowledge" && this.opts.knowledgeSource) {
|
|
91
|
+
const snippets = await this.opts.knowledgeSource.retrieve(query, topK);
|
|
92
|
+
for (const snippet of snippets) {
|
|
93
|
+
entries.push({ content: snippet, source: "knowledge" });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (entries.length === 0)
|
|
98
|
+
return;
|
|
99
|
+
kernelApply(runtime, this.pendingObservations, { kind: "page_in", entries });
|
|
100
|
+
await this.opts.sessionLog.append(sessionId, withCategory({
|
|
101
|
+
kind: "page_in",
|
|
102
|
+
turn: runtime.turn(),
|
|
103
|
+
entry_count: entries.length,
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
106
|
+
async resolveKernelSuspend(runtime, sessionId) {
|
|
107
|
+
const gated = this.pendingObservations.filter((o) => o.kind === "tool_gated" && typeof o.call_id === "string" && typeof o.tool === "string");
|
|
108
|
+
const approved = [];
|
|
109
|
+
const denied = [];
|
|
110
|
+
const events = [];
|
|
111
|
+
const runCtx = { onPermissionRequest: this.opts.onPermissionRequest };
|
|
112
|
+
for (const g of gated) {
|
|
113
|
+
const request = {
|
|
114
|
+
type: "permission_request",
|
|
115
|
+
callId: g.call_id,
|
|
116
|
+
toolName: g.tool,
|
|
117
|
+
arguments: "{}",
|
|
118
|
+
reason: typeof g.reason === "string" ? g.reason : "",
|
|
119
|
+
};
|
|
120
|
+
events.push(request);
|
|
121
|
+
const decision = await resolvePermissionRequest(request, runCtx);
|
|
122
|
+
events.push({
|
|
123
|
+
type: "permission_resolved",
|
|
124
|
+
callId: g.call_id,
|
|
125
|
+
toolName: g.tool,
|
|
126
|
+
approved: decision.approved,
|
|
127
|
+
responder: decision.responder ?? "host",
|
|
128
|
+
...(decision.reason ? { reason: decision.reason } : {}),
|
|
129
|
+
});
|
|
130
|
+
await this.opts.sessionLog.append(sessionId, {
|
|
131
|
+
kind: "permission_requested",
|
|
132
|
+
turn: runtime.turn(),
|
|
133
|
+
tool: g.tool,
|
|
134
|
+
arguments: "{}",
|
|
135
|
+
reason: request.reason,
|
|
136
|
+
});
|
|
137
|
+
await this.opts.sessionLog.append(sessionId, {
|
|
138
|
+
kind: "permission_resolved",
|
|
139
|
+
turn: runtime.turn(),
|
|
140
|
+
approved: decision.approved,
|
|
141
|
+
responder: decision.responder ?? "host",
|
|
142
|
+
});
|
|
143
|
+
if (decision.approved) {
|
|
144
|
+
approved.push(g.call_id);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
denied.push(g.call_id);
|
|
148
|
+
const denyReason = decision.reason ?? "permission denied";
|
|
149
|
+
events.push({
|
|
150
|
+
type: "tool_denied",
|
|
151
|
+
callId: g.call_id,
|
|
152
|
+
toolName: g.tool,
|
|
153
|
+
reason: denyReason,
|
|
154
|
+
});
|
|
155
|
+
events.push({
|
|
156
|
+
type: "tool_result",
|
|
157
|
+
callId: g.call_id,
|
|
158
|
+
name: g.tool,
|
|
159
|
+
content: `permission denied: ${denyReason}`,
|
|
160
|
+
isError: true,
|
|
161
|
+
errorKind: "governance_denied",
|
|
162
|
+
});
|
|
163
|
+
await this.opts.sessionLog.append(sessionId, {
|
|
164
|
+
kind: "tool_denied",
|
|
165
|
+
turn: runtime.turn(),
|
|
166
|
+
call_id: g.call_id,
|
|
167
|
+
tool_name: g.tool,
|
|
168
|
+
reason: denyReason,
|
|
169
|
+
});
|
|
170
|
+
await this.opts.sessionLog.append(sessionId, {
|
|
171
|
+
kind: "tool_completed",
|
|
172
|
+
turn: runtime.turn(),
|
|
173
|
+
results: [{
|
|
174
|
+
call_id: g.call_id,
|
|
175
|
+
output: `permission denied: ${denyReason}`,
|
|
176
|
+
is_error: true,
|
|
177
|
+
error_kind: "governance_denied",
|
|
178
|
+
}],
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return { approved, denied, events };
|
|
183
|
+
}
|
|
55
184
|
async dream(agentId, nowMs = Date.now()) {
|
|
56
185
|
if (!this.opts.dreamStore)
|
|
57
186
|
throw new Error("dreamStore not configured");
|
|
58
187
|
const kernel = await getKernel();
|
|
59
|
-
this.opts.governance?._attach(kernel);
|
|
60
188
|
const sessions = await this.opts.dreamStore.loadSessions(agentId);
|
|
61
189
|
const existingMemories = await this.opts.dreamStore.loadMemories(agentId);
|
|
62
190
|
if (!sessions.length)
|
|
@@ -77,13 +205,14 @@ export class RuntimeRunner {
|
|
|
77
205
|
if (action1.kind !== "synthesize_insights")
|
|
78
206
|
throw new Error(`unexpected: ${action1.kind}`);
|
|
79
207
|
let synthesisText = "";
|
|
80
|
-
const
|
|
208
|
+
const dreamProvider = this.opts.dreamProvider ?? this.opts.provider;
|
|
209
|
+
const providerState = dreamProvider.createRunState?.();
|
|
81
210
|
const synthMsgs = (action1.messages ?? []);
|
|
82
211
|
const synthContext = {
|
|
83
212
|
systemText: synthMsgs.filter(m => m.role === "system").map(m => m.content).join("\n\n"),
|
|
84
213
|
turns: synthMsgs.filter(m => m.role !== "system"),
|
|
85
214
|
};
|
|
86
|
-
for await (const evt of
|
|
215
|
+
for await (const evt of dreamProvider.stream(synthContext, [], undefined, providerState)) {
|
|
87
216
|
if (evt.type === "text_delta")
|
|
88
217
|
synthesisText += evt.delta;
|
|
89
218
|
}
|
|
@@ -115,9 +244,9 @@ export class RuntimeRunner {
|
|
|
115
244
|
async *execute(sessionId, goal, criteria, extensions, priorEvents, resumeMidRun = false) {
|
|
116
245
|
this.interrupted = false;
|
|
117
246
|
this.pendingObservations = [];
|
|
247
|
+
this.pendingSpoolOutputs.clear();
|
|
118
248
|
this.currentSessionId = sessionId;
|
|
119
249
|
const kernel = await getKernel();
|
|
120
|
-
this.opts.governance?._attach(kernel);
|
|
121
250
|
const ext = { ...this.opts.extensions, ...(extensions ?? {}) };
|
|
122
251
|
const providerState = this.opts.provider.createRunState?.();
|
|
123
252
|
let nextCompressedArchiveStart = nextArchivedSeqStart(priorEvents);
|
|
@@ -130,7 +259,6 @@ export class RuntimeRunner {
|
|
|
130
259
|
timeoutMs: effectiveTimeoutMs !== undefined ? BigInt(effectiveTimeoutMs) : undefined,
|
|
131
260
|
});
|
|
132
261
|
this.activeKernel = runtime;
|
|
133
|
-
const router = new kernel.SignalRouter(256);
|
|
134
262
|
kernelApply(runtime, this.pendingObservations, {
|
|
135
263
|
kind: "set_tools",
|
|
136
264
|
tools: this.opts.executionPlane.schemas().map(toolSchemaToKernel),
|
|
@@ -199,11 +327,23 @@ export class RuntimeRunner {
|
|
|
199
327
|
if (this.opts.runSpec) {
|
|
200
328
|
startPayload.run_spec = agentRunSpecToKernel(this.opts.runSpec);
|
|
201
329
|
}
|
|
330
|
+
const attentionPolicy = this.opts.attentionPolicy ?? DEFAULT_NATIVE_ATTENTION_POLICY;
|
|
331
|
+
const governancePolicy = this.opts.governancePolicy ?? DEFAULT_NATIVE_GOVERNANCE_POLICY;
|
|
332
|
+
kernelApply(runtime, this.pendingObservations, governancePolicyToKernelEvent(governancePolicy));
|
|
333
|
+
kernelApply(runtime, this.pendingObservations, {
|
|
334
|
+
kind: "set_attention_policy",
|
|
335
|
+
...(attentionPolicy.maxQueueSize !== undefined
|
|
336
|
+
? { max_queue_size: attentionPolicy.maxQueueSize }
|
|
337
|
+
: {}),
|
|
338
|
+
});
|
|
202
339
|
let action = resumeMidRun
|
|
203
340
|
? kernelAction(runtime, this.pendingObservations, { kind: "resume" })
|
|
204
341
|
: kernelAction(runtime, this.pendingObservations, startPayload);
|
|
205
342
|
let hasAttemptedReactiveCompact = false;
|
|
206
343
|
while (!runtime.isTerminal()) {
|
|
344
|
+
if (action.kind === "execute_tool") {
|
|
345
|
+
await this.applyKernelPageIn(runtime, sessionId);
|
|
346
|
+
}
|
|
207
347
|
nextCompressedArchiveStart = await this.appendObservations(sessionId, runtime, nextCompressedArchiveStart);
|
|
208
348
|
if (this.interrupted) {
|
|
209
349
|
action = kernelAction(runtime, this.pendingObservations, { kind: "timeout" });
|
|
@@ -212,30 +352,27 @@ export class RuntimeRunner {
|
|
|
212
352
|
if (this.opts.signalSource) {
|
|
213
353
|
const sig = await this.opts.signalSource.nextSignal();
|
|
214
354
|
if (sig) {
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
action = kernelAction(runtime, this.pendingObservations, { kind: "timeout" });
|
|
236
|
-
break;
|
|
355
|
+
const id = crypto.randomUUID();
|
|
356
|
+
const source = sig.source ?? "custom";
|
|
357
|
+
const signalType = sig.signalType ?? "event";
|
|
358
|
+
const urgency = sig.urgency ?? "normal";
|
|
359
|
+
const summary = String(sig.payload?.goal ?? "signal");
|
|
360
|
+
const sigAction = kernelMaybeAction(runtime, this.pendingObservations, {
|
|
361
|
+
kind: "signal",
|
|
362
|
+
signal: {
|
|
363
|
+
id,
|
|
364
|
+
source,
|
|
365
|
+
signal_type: signalType,
|
|
366
|
+
urgency,
|
|
367
|
+
summary,
|
|
368
|
+
payload: sig.payload ?? {},
|
|
369
|
+
...(sig.dedupeKey ? { dedupe_key: sig.dedupeKey } : {}),
|
|
370
|
+
timestamp_ms: Date.now(),
|
|
371
|
+
},
|
|
372
|
+
});
|
|
373
|
+
if (sigAction)
|
|
374
|
+
action = sigAction;
|
|
237
375
|
}
|
|
238
|
-
queued = router.next();
|
|
239
376
|
}
|
|
240
377
|
if (runtime.isTerminal())
|
|
241
378
|
break;
|
|
@@ -245,11 +382,16 @@ export class RuntimeRunner {
|
|
|
245
382
|
const context = action.context;
|
|
246
383
|
const tools = action.tools;
|
|
247
384
|
let turnTokens = 0;
|
|
385
|
+
let turnInputTokens = 0;
|
|
386
|
+
let turnOutputTokens = 0;
|
|
248
387
|
let shouldRetry = false;
|
|
249
388
|
try {
|
|
250
389
|
for await (const evt of this.opts.provider.stream(context, tools, Object.keys(ext).length ? ext : undefined, providerState)) {
|
|
251
390
|
if (evt.type === "usage") {
|
|
252
|
-
|
|
391
|
+
const usageEvt = evt;
|
|
392
|
+
turnTokens = usageEvt.totalTokens;
|
|
393
|
+
turnInputTokens = usageEvt.inputTokens ?? 0;
|
|
394
|
+
turnOutputTokens = usageEvt.outputTokens ?? 0;
|
|
253
395
|
continue;
|
|
254
396
|
}
|
|
255
397
|
yield evt;
|
|
@@ -289,17 +431,33 @@ export class RuntimeRunner {
|
|
|
289
431
|
role: "assistant",
|
|
290
432
|
content: finalText,
|
|
291
433
|
toolCalls: finalToolCalls,
|
|
292
|
-
tokenCount: turnTokens || undefined,
|
|
434
|
+
tokenCount: turnOutputTokens || turnTokens || undefined,
|
|
293
435
|
};
|
|
294
|
-
|
|
436
|
+
const providerEvent = {
|
|
295
437
|
kind: "provider_result",
|
|
296
438
|
message: messageToKernelMessage(assistantMessage),
|
|
297
|
-
|
|
439
|
+
...(turnInputTokens > 0 ? { observed_input_tokens: turnInputTokens } : {}),
|
|
440
|
+
...(turnOutputTokens > 0 ? { observed_output_tokens: turnOutputTokens } : {}),
|
|
441
|
+
now_ms: Date.now(),
|
|
442
|
+
};
|
|
443
|
+
let nextAction = kernelMaybeAction(runtime, this.pendingObservations, providerEvent);
|
|
444
|
+
const hasSuspended = this.pendingObservations.some(o => o.kind === "suspended");
|
|
445
|
+
if (!nextAction && hasSuspended) {
|
|
446
|
+
const resolved = await this.resolveKernelSuspend(runtime, sessionId);
|
|
447
|
+
for (const evt of resolved.events)
|
|
448
|
+
yield evt;
|
|
449
|
+
nextAction = kernelAction(runtime, this.pendingObservations, {
|
|
450
|
+
kind: "resume",
|
|
451
|
+
approved_calls: resolved.approved,
|
|
452
|
+
denied_calls: resolved.denied,
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
action = nextAction ?? kernelAction(runtime, this.pendingObservations, providerEvent);
|
|
298
456
|
const providerReplay = peekProviderReplay(this.opts.provider, finalText, finalToolCalls);
|
|
299
457
|
await this.opts.sessionLog.append(sessionId, buildLlmCompletedEvent({
|
|
300
458
|
turn: runtime.turn(),
|
|
301
459
|
content: finalText,
|
|
302
|
-
tokenCount: turnTokens || undefined,
|
|
460
|
+
tokenCount: turnOutputTokens || turnTokens || undefined,
|
|
303
461
|
toolCalls: finalToolCalls,
|
|
304
462
|
providerReplay,
|
|
305
463
|
}));
|
|
@@ -312,8 +470,8 @@ export class RuntimeRunner {
|
|
|
312
470
|
skillContentMap: this.opts.skillContentMap,
|
|
313
471
|
dreamStore: this.opts.dreamStore,
|
|
314
472
|
knowledgeSource: this.opts.knowledgeSource,
|
|
315
|
-
governance: this.opts.governance,
|
|
316
473
|
onToolSuspend: this.opts.onToolSuspend,
|
|
474
|
+
onPermissionRequest: this.opts.onPermissionRequest,
|
|
317
475
|
};
|
|
318
476
|
const toolResults = [];
|
|
319
477
|
for await (const evt of this.opts.executionPlane.executeAll(allCalls, runCtx)) {
|
|
@@ -358,18 +516,15 @@ export class RuntimeRunner {
|
|
|
358
516
|
arguments: typeof pre.arguments === "string" ? pre.arguments : JSON.stringify(pre.arguments),
|
|
359
517
|
reason: pre.reason,
|
|
360
518
|
});
|
|
519
|
+
}
|
|
520
|
+
else if (evt.type === "permission_resolved") {
|
|
521
|
+
const resolved = evt;
|
|
522
|
+
const turn = runtime.turn();
|
|
361
523
|
await this.opts.sessionLog.append(sessionId, {
|
|
362
524
|
kind: "permission_resolved",
|
|
363
525
|
turn,
|
|
364
|
-
approved:
|
|
365
|
-
responder:
|
|
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}`,
|
|
526
|
+
approved: resolved.approved,
|
|
527
|
+
responder: resolved.responder,
|
|
373
528
|
});
|
|
374
529
|
}
|
|
375
530
|
}
|
|
@@ -383,6 +538,12 @@ export class RuntimeRunner {
|
|
|
383
538
|
token_count: r.tokenCount,
|
|
384
539
|
})),
|
|
385
540
|
});
|
|
541
|
+
for (const call of allCalls) {
|
|
542
|
+
const result = toolResults.find(r => r.callId === call.id);
|
|
543
|
+
if (result) {
|
|
544
|
+
this.pendingSpoolOutputs.set(call.id, { tool: call.name, output: result.output });
|
|
545
|
+
}
|
|
546
|
+
}
|
|
386
547
|
action = kernelAction(runtime, this.pendingObservations, {
|
|
387
548
|
kind: "tool_results",
|
|
388
549
|
results: toolResults.map(toolResultToKernel),
|
|
@@ -474,19 +635,10 @@ export class RuntimeRunner {
|
|
|
474
635
|
parent_session_id: parentSessionId,
|
|
475
636
|
});
|
|
476
637
|
this.nextArchiveStart = await this.appendObservations(parentSessionId, runtime, this.nextArchiveStart);
|
|
477
|
-
const spawned = observations
|
|
638
|
+
const spawned = findSpawnProcessObservation(observations);
|
|
478
639
|
if (!spawned)
|
|
479
|
-
throw new Error("spawn_sub_agent did not emit
|
|
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
|
-
};
|
|
640
|
+
throw new Error("spawn_sub_agent did not emit agent_process_changed");
|
|
641
|
+
const manifest = spawnObservationToManifest(spawned, spec, parentSessionId);
|
|
490
642
|
const orchestrator = this.opts.subAgentOrchestrator ?? defaultSubAgentOrchestrator;
|
|
491
643
|
const result = await orchestrator.run({
|
|
492
644
|
parentOpts: this.opts,
|
|
@@ -505,95 +657,94 @@ export class RuntimeRunner {
|
|
|
505
657
|
const turn = runtime.turn();
|
|
506
658
|
const preservedRefs = runtime.preservedRefs();
|
|
507
659
|
const observations = this.pendingObservations.splice(0);
|
|
508
|
-
for (
|
|
509
|
-
if (obs.kind === "
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
const
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
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
|
-
});
|
|
660
|
+
for (let obs of observations) {
|
|
661
|
+
if (obs.kind === "page_in_requested")
|
|
662
|
+
continue;
|
|
663
|
+
let spoolRef;
|
|
664
|
+
if (obs.kind === "large_result_spooled") {
|
|
665
|
+
const pending = this.pendingSpoolOutputs.get(obs.call_id ?? "");
|
|
666
|
+
if (pending) {
|
|
667
|
+
const spool = this.opts.resultSpool ?? new LargeResultSpool();
|
|
668
|
+
try {
|
|
669
|
+
spoolRef = await spool.persistOutput(obs.call_id ?? "", pending.output);
|
|
670
|
+
}
|
|
671
|
+
catch {
|
|
672
|
+
// non-fatal
|
|
673
|
+
}
|
|
674
|
+
if (!obs.tool && pending.tool) {
|
|
675
|
+
obs = { ...obs, tool: pending.tool };
|
|
676
|
+
}
|
|
677
|
+
this.pendingSpoolOutputs.delete(obs.call_id ?? "");
|
|
678
|
+
}
|
|
571
679
|
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
680
|
+
const latest = obs.kind === "compressed" ? await this.opts.sessionLog.latestSeq(sessionId) : undefined;
|
|
681
|
+
const event = kernelObservationToSessionEvent(obs, turn, {
|
|
682
|
+
nextArchiveStart,
|
|
683
|
+
latestSeq: latest,
|
|
684
|
+
preservedRefs,
|
|
685
|
+
spoolRef,
|
|
686
|
+
compressionAction,
|
|
687
|
+
});
|
|
688
|
+
if (!event)
|
|
689
|
+
continue;
|
|
690
|
+
if (obs.kind === "page_out" && obs.archived) {
|
|
691
|
+
this.localPageOutCache.push(...obs.archived);
|
|
578
692
|
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
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
|
-
});
|
|
693
|
+
const compressedSeq = await this.opts.sessionLog.append(sessionId, event);
|
|
694
|
+
if (event.kind === "compressed") {
|
|
695
|
+
nextArchiveStart = compressedSeq + 1;
|
|
590
696
|
}
|
|
591
|
-
|
|
592
|
-
|
|
697
|
+
if (obs.kind === "page_out"
|
|
698
|
+
&& obs.tier_hint === "semantic"
|
|
699
|
+
&& Array.isArray(obs.archived)
|
|
700
|
+
&& obs.archived.length > 0) {
|
|
701
|
+
void this.archiveSemanticPageOut(obs.archived, compressionAction(obs.action));
|
|
593
702
|
}
|
|
594
703
|
}
|
|
595
704
|
return nextArchiveStart;
|
|
596
705
|
}
|
|
706
|
+
async archiveSemanticPageOut(archived, action) {
|
|
707
|
+
if (!this.opts.dreamStore || !this.opts.agentId)
|
|
708
|
+
return;
|
|
709
|
+
try {
|
|
710
|
+
const summary = this.opts.dreamSummarizer
|
|
711
|
+
? await this.opts.dreamSummarizer.summarize(archived, { action })
|
|
712
|
+
: await summarizeForLongTermMemory(this.opts.dreamProvider ?? this.opts.provider, archived, this.opts.dreamSystemPrompt);
|
|
713
|
+
const existing = await this.opts.dreamStore.loadMemories(this.opts.agentId);
|
|
714
|
+
await this.opts.dreamStore.commit(this.opts.agentId, {
|
|
715
|
+
toAdd: [{ text: summary, score: 1.0, metadata: { source: "semantic_page_out", action } }],
|
|
716
|
+
toRemoveIndices: [],
|
|
717
|
+
stats: {
|
|
718
|
+
insightsProcessed: 1,
|
|
719
|
+
duplicatesRemoved: 0,
|
|
720
|
+
conflictsResolved: 0,
|
|
721
|
+
entriesAdded: 1,
|
|
722
|
+
},
|
|
723
|
+
}, existing);
|
|
724
|
+
}
|
|
725
|
+
catch {
|
|
726
|
+
// non-fatal
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
async function summarizeForLongTermMemory(provider, archived, systemPrompt) {
|
|
731
|
+
const transcript = archived
|
|
732
|
+
.map(m => `${m.role}: ${m.content}`)
|
|
733
|
+
.join("\n");
|
|
734
|
+
const context = {
|
|
735
|
+
systemText: [
|
|
736
|
+
systemPrompt,
|
|
737
|
+
"Summarize the following conversation for long-term memory. Preserve key facts, decisions, and open questions.",
|
|
738
|
+
].filter(Boolean).join("\n\n"),
|
|
739
|
+
turns: [{ role: "user", content: transcript, toolCalls: [] }],
|
|
740
|
+
};
|
|
741
|
+
let text = "";
|
|
742
|
+
const state = provider.createRunState?.();
|
|
743
|
+
for await (const evt of provider.stream(context, [], undefined, state)) {
|
|
744
|
+
if (evt.type === "text_delta")
|
|
745
|
+
text += evt.delta;
|
|
746
|
+
}
|
|
747
|
+
return text.trim() || transcript.slice(0, 2000);
|
|
597
748
|
}
|
|
598
749
|
function isMidRun(events) {
|
|
599
750
|
return events.length > 0 && !events.some(e => e.event.kind === "run_terminal");
|
|
@@ -608,8 +759,14 @@ function compressionAction(action) {
|
|
|
608
759
|
return undefined;
|
|
609
760
|
}
|
|
610
761
|
function replayMessages(events, maxBytes) {
|
|
611
|
-
|
|
762
|
+
// Build upgraded-summary index: compressed_seq -> upgraded summary
|
|
763
|
+
const upgradedSummaries = new Map();
|
|
612
764
|
for (const { event: e } of events) {
|
|
765
|
+
if (e.kind === "summary_upgraded")
|
|
766
|
+
upgradedSummaries.set(e.compressed_seq, e.summary);
|
|
767
|
+
}
|
|
768
|
+
const messages = [];
|
|
769
|
+
for (const { seq, event: e } of events) {
|
|
613
770
|
if (e.kind === "run_started") {
|
|
614
771
|
const userText = e.criteria.length
|
|
615
772
|
? `${e.goal}\n\nCriteria:\n${e.criteria.map((c, i) => `${i + 1}. ${c}`).join("\n")}`
|
|
@@ -622,8 +779,9 @@ function replayMessages(events, maxBytes) {
|
|
|
622
779
|
});
|
|
623
780
|
}
|
|
624
781
|
else if (e.kind === "compressed") {
|
|
625
|
-
|
|
626
|
-
|
|
782
|
+
const summary = upgradedSummaries.get(seq) ?? e.summary;
|
|
783
|
+
if (summary) {
|
|
784
|
+
const systemText = `[Compressed context: turn ${e.turn}]\n${summary}`;
|
|
627
785
|
messages.push({
|
|
628
786
|
role: "system",
|
|
629
787
|
content: systemText,
|