@deepstrike/sdk 0.2.4 → 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 +287 -70
- package/dist/collaboration/modes/creator-verifier.d.ts +0 -3
- package/dist/collaboration/modes/creator-verifier.js +2 -6
- package/dist/governance.d.ts +36 -0
- package/dist/governance.js +22 -0
- package/dist/index.d.ts +11 -5
- package/dist/index.js +5 -1
- package/dist/memory/agent.d.ts +111 -0
- package/dist/memory/agent.js +151 -0
- package/dist/memory/protocols.d.ts +56 -0
- package/dist/runtime/execution-plane.d.ts +6 -9
- package/dist/runtime/execution-plane.js +53 -25
- package/dist/runtime/kernel-event-log.d.ts +26 -0
- package/dist/runtime/kernel-event-log.js +220 -0
- package/dist/runtime/kernel-primitives-dashboard.d.ts +44 -0
- package/dist/runtime/kernel-primitives-dashboard.js +135 -0
- package/dist/runtime/kernel-step.d.ts +25 -1
- package/dist/runtime/kernel-step.js +12 -3
- package/dist/runtime/large-result-spool.d.ts +84 -0
- package/dist/runtime/large-result-spool.js +167 -0
- package/dist/runtime/os-profile.d.ts +18 -0
- package/dist/runtime/os-profile.js +47 -0
- package/dist/runtime/os-snapshot.d.ts +35 -0
- package/dist/runtime/os-snapshot.js +128 -0
- package/dist/runtime/runner.d.ts +62 -9
- package/dist/runtime/runner.js +425 -135
- package/dist/runtime/session-log.d.ts +118 -4
- package/dist/runtime/session-log.js +15 -4
- package/dist/runtime/sub-agent-orchestrator.d.ts +2 -2
- package/dist/runtime/sub-agent-orchestrator.js +14 -23
- package/dist/types/agent.d.ts +12 -3
- package/dist/types/agent.js +18 -0
- package/dist/types.d.ts +22 -0
- package/package.json +2 -2
package/dist/runtime/runner.js
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
|
+
import { memoriesToIndex, selectMemories } from "../memory/agent.js";
|
|
2
|
+
import { resolvePermissionRequest } from "./execution-plane.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 {
|
|
6
|
-
import {
|
|
7
|
+
import { KernelPrimitivesDashboard } from "./kernel-primitives-dashboard.js";
|
|
8
|
+
import { capabilityMarker, capabilitySkill, capabilityTool, capabilityCommandMount, capabilityCommandUnmount, kernelAction, kernelApply, kernelMaybeAction, forceCompact, messageToKernelMessage, skillMetadataToKernel, taskUpdateToKernel, toolResultToKernel, toolSchemaToKernel, } from "./kernel-step.js";
|
|
9
|
+
import { agentRunSpecToKernel, findSpawnProcessObservation, milestoneCheckPass, milestoneCheckResultToKernel, spawnObservationToManifest, subAgentResultToKernel, } from "../types/agent.js";
|
|
7
10
|
import { defaultSubAgentOrchestrator } from "./sub-agent-orchestrator.js";
|
|
11
|
+
import { governancePolicyToKernelEvent } from "../governance.js";
|
|
12
|
+
import { kernelObservationToSessionEvent, withCategory } from "./kernel-event-log.js";
|
|
13
|
+
import { DEFAULT_NATIVE_ATTENTION_POLICY, DEFAULT_NATIVE_GOVERNANCE_POLICY } from "./os-profile.js";
|
|
14
|
+
import { LargeResultSpool } from "./large-result-spool.js";
|
|
8
15
|
export class RuntimeRunner {
|
|
9
16
|
opts;
|
|
10
17
|
interrupted = false;
|
|
@@ -12,13 +19,128 @@ export class RuntimeRunner {
|
|
|
12
19
|
pendingObservations = [];
|
|
13
20
|
currentSessionId = null;
|
|
14
21
|
nextArchiveStart = 0;
|
|
22
|
+
/** Full tool outputs keyed by call_id until Layer-1 spool observations are logged. */
|
|
23
|
+
pendingSpoolOutputs = new Map();
|
|
24
|
+
/** Local cache of paged-out/archived messages for priority memory retrieval. */
|
|
25
|
+
localPageOutCache = [];
|
|
26
|
+
dashboard = null;
|
|
15
27
|
constructor(opts) {
|
|
16
28
|
this.opts = opts;
|
|
29
|
+
if (opts.enableDiagnosticsDashboard) {
|
|
30
|
+
const originalAppend = opts.sessionLog.append.bind(opts.sessionLog);
|
|
31
|
+
opts.sessionLog.append = async (sessionId, event) => {
|
|
32
|
+
const seq = await originalAppend(sessionId, event);
|
|
33
|
+
if (this.dashboard) {
|
|
34
|
+
this.dashboard.ingest(event);
|
|
35
|
+
this.dashboard.print();
|
|
36
|
+
}
|
|
37
|
+
return seq;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
17
40
|
}
|
|
18
41
|
/** Host configuration (for coordinator / sub-agent spawn). */
|
|
19
42
|
get hostOptions() {
|
|
20
43
|
return this.opts;
|
|
21
44
|
}
|
|
45
|
+
async writeMemory(memory, opts = {}) {
|
|
46
|
+
const sessionId = opts.sessionId ?? this.currentSessionId;
|
|
47
|
+
const agentId = opts.agentId ?? this.opts.agentId;
|
|
48
|
+
if (!this.opts.dreamStore || !agentId)
|
|
49
|
+
return;
|
|
50
|
+
const observations = [];
|
|
51
|
+
const runtime = this.activeKernel ?? this.createSyscallRuntime();
|
|
52
|
+
kernelApply(runtime, observations, { kind: "write_memory", memory });
|
|
53
|
+
const event = observations.find(o => o.kind === "memory_written");
|
|
54
|
+
if (!event) {
|
|
55
|
+
await this.appendMemorySyscallObservations(sessionId, observations);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const existing = await this.opts.dreamStore.loadMemories(agentId);
|
|
59
|
+
await this.opts.dreamStore.commit(agentId, {
|
|
60
|
+
toAdd: [{
|
|
61
|
+
text: memory.content,
|
|
62
|
+
score: 1.0,
|
|
63
|
+
metadata: {
|
|
64
|
+
...memory.metadata,
|
|
65
|
+
source: "write_memory_syscall",
|
|
66
|
+
},
|
|
67
|
+
}],
|
|
68
|
+
toRemoveIndices: [],
|
|
69
|
+
stats: {
|
|
70
|
+
insightsProcessed: 1,
|
|
71
|
+
duplicatesRemoved: 0,
|
|
72
|
+
conflictsResolved: 0,
|
|
73
|
+
entriesAdded: 1,
|
|
74
|
+
},
|
|
75
|
+
}, existing);
|
|
76
|
+
await this.appendMemorySyscallObservations(sessionId, observations);
|
|
77
|
+
}
|
|
78
|
+
async queryMemory(query, opts = {}) {
|
|
79
|
+
const sessionId = opts.sessionId ?? this.currentSessionId;
|
|
80
|
+
const agentId = opts.agentId ?? this.opts.agentId;
|
|
81
|
+
if (!this.opts.dreamStore || !agentId)
|
|
82
|
+
return [];
|
|
83
|
+
const observations = [];
|
|
84
|
+
const runtime = this.activeKernel ?? this.createSyscallRuntime();
|
|
85
|
+
kernelApply(runtime, observations, { kind: "query_memory", query });
|
|
86
|
+
const allMemories = await this.opts.dreamStore.loadMemories(agentId);
|
|
87
|
+
const retrieval = await selectMemories(query, memoriesToIndex(allMemories));
|
|
88
|
+
let hits;
|
|
89
|
+
if (retrieval.selected_memory_ids.length > 0) {
|
|
90
|
+
const selected = new Set(retrieval.selected_memory_ids);
|
|
91
|
+
hits = allMemories
|
|
92
|
+
.filter(m => selected.has(String(m.metadata?.name ?? "")))
|
|
93
|
+
.slice(0, query.top_k);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
hits = await this.opts.dreamStore.search(agentId, query.current_context, query.top_k);
|
|
97
|
+
if (hits.length > 0 && retrieval.selection_rationale === "No candidates after filtering") {
|
|
98
|
+
retrieval.selected_memory_ids = hits.map(h => String(h.metadata?.name ?? h.text.slice(0, 32)));
|
|
99
|
+
retrieval.selection_rationale = `DreamStore.search returned ${hits.length} hit(s)`;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
await this.appendMemorySyscallObservations(sessionId, observations);
|
|
103
|
+
await this.logMemoryRetrievalResult(sessionId, runtime, retrieval);
|
|
104
|
+
return hits;
|
|
105
|
+
}
|
|
106
|
+
async logMemoryRetrievalResult(sessionId, runtime, retrieval) {
|
|
107
|
+
if (!sessionId)
|
|
108
|
+
return;
|
|
109
|
+
await this.opts.sessionLog.append(sessionId, {
|
|
110
|
+
kind: "memory_retrieval_result",
|
|
111
|
+
selected_memory_ids: retrieval.selected_memory_ids,
|
|
112
|
+
selection_rationale: retrieval.selection_rationale,
|
|
113
|
+
});
|
|
114
|
+
kernelApply(runtime, [], {
|
|
115
|
+
kind: "memory_retrieval_result",
|
|
116
|
+
retrieval: {
|
|
117
|
+
selected_memory_ids: retrieval.selected_memory_ids,
|
|
118
|
+
selection_rationale: retrieval.selection_rationale,
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
createSyscallRuntime() {
|
|
123
|
+
const { KernelRuntime } = getKernel();
|
|
124
|
+
return new KernelRuntime({
|
|
125
|
+
maxTokens: this.opts.maxTokens,
|
|
126
|
+
maxTurns: this.opts.maxTurns,
|
|
127
|
+
timeoutMs: this.opts.timeoutMs !== undefined ? BigInt(this.opts.timeoutMs) : undefined,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
async appendMemorySyscallObservations(sessionId, observations) {
|
|
131
|
+
if (!sessionId)
|
|
132
|
+
return;
|
|
133
|
+
const turn = this.activeKernel?.turn() ?? 0;
|
|
134
|
+
for (const obs of observations) {
|
|
135
|
+
if (obs.kind !== "memory_written"
|
|
136
|
+
&& obs.kind !== "memory_queried"
|
|
137
|
+
&& obs.kind !== "memory_validation_failed")
|
|
138
|
+
continue;
|
|
139
|
+
const event = kernelObservationToSessionEvent(obs, turn);
|
|
140
|
+
if (event)
|
|
141
|
+
await this.opts.sessionLog.append(sessionId, event);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
22
144
|
/** Mount a tool capability on the currently-running kernel runtime. No-op if not running. */
|
|
23
145
|
mountTool(schema) {
|
|
24
146
|
if (!this.activeKernel)
|
|
@@ -43,6 +165,52 @@ export class RuntimeRunner {
|
|
|
43
165
|
return;
|
|
44
166
|
kernelApply(this.activeKernel, this.pendingObservations, capabilityCommandUnmount(kind, id));
|
|
45
167
|
}
|
|
168
|
+
/** Phase 4: satisfy kernel page-in requests before meta-tool execution. */
|
|
169
|
+
async applyKernelPageIn(runtime, sessionId) {
|
|
170
|
+
const requests = this.pendingObservations.filter((o) => o.kind === "page_in_requested" && typeof o.tool === "string");
|
|
171
|
+
if (requests.length === 0)
|
|
172
|
+
return;
|
|
173
|
+
const entries = [];
|
|
174
|
+
for (const req of requests) {
|
|
175
|
+
const query = typeof req.query === "string" ? req.query : "";
|
|
176
|
+
const topK = typeof req.top_k === "number" ? req.top_k : 5;
|
|
177
|
+
if (req.tool === "memory") {
|
|
178
|
+
// Priority search: Local Page-Out Cache (lexical/keyword filter)
|
|
179
|
+
const localHits = this.localPageOutCache.filter(m => typeof m.content === "string" && m.content.toLowerCase().includes(query.toLowerCase())).slice(0, topK);
|
|
180
|
+
for (const hit of localHits) {
|
|
181
|
+
entries.push({
|
|
182
|
+
content: `[local semantic cache] ${hit.role}: ${hit.content}`,
|
|
183
|
+
source: "semantic_cache",
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
// Fall back to dreamStore for the remainder if needed
|
|
187
|
+
const remainingK = topK - entries.length;
|
|
188
|
+
if (remainingK > 0 && this.opts.dreamStore && this.opts.agentId) {
|
|
189
|
+
const hits = await this.opts.dreamStore.search(this.opts.agentId, query, remainingK);
|
|
190
|
+
for (const hit of hits) {
|
|
191
|
+
entries.push({
|
|
192
|
+
content: `[memory score=${hit.score.toFixed(3)}] ${hit.text}`,
|
|
193
|
+
source: "memory",
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
else if (req.tool === "knowledge" && this.opts.knowledgeSource) {
|
|
199
|
+
const snippets = await this.opts.knowledgeSource.retrieve(query, topK);
|
|
200
|
+
for (const snippet of snippets) {
|
|
201
|
+
entries.push({ content: snippet, source: "knowledge" });
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (entries.length === 0)
|
|
206
|
+
return;
|
|
207
|
+
kernelApply(runtime, this.pendingObservations, { kind: "page_in", entries });
|
|
208
|
+
await this.opts.sessionLog.append(sessionId, withCategory({
|
|
209
|
+
kind: "page_in",
|
|
210
|
+
turn: runtime.turn(),
|
|
211
|
+
entry_count: entries.length,
|
|
212
|
+
}));
|
|
213
|
+
}
|
|
46
214
|
/** Push content into the Knowledge slot (memory retrievals, skill definitions, artifacts). */
|
|
47
215
|
pushKnowledge(message, tokens) {
|
|
48
216
|
if (!this.activeKernel)
|
|
@@ -69,19 +237,10 @@ export class RuntimeRunner {
|
|
|
69
237
|
parent_session_id: parentSessionId,
|
|
70
238
|
});
|
|
71
239
|
this.nextArchiveStart = await this.appendObservations(parentSessionId, runtime, this.nextArchiveStart);
|
|
72
|
-
const spawned = observations
|
|
240
|
+
const spawned = findSpawnProcessObservation(observations);
|
|
73
241
|
if (!spawned)
|
|
74
|
-
throw new Error("spawn_sub_agent did not emit
|
|
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
|
-
};
|
|
242
|
+
throw new Error("spawn_sub_agent did not emit agent_process_changed");
|
|
243
|
+
const manifest = spawnObservationToManifest(spawned, spec, parentSessionId);
|
|
85
244
|
const orchestrator = this.opts.subAgentOrchestrator ?? defaultSubAgentOrchestrator;
|
|
86
245
|
const result = await orchestrator.run({
|
|
87
246
|
parentOpts: this.opts,
|
|
@@ -150,7 +309,8 @@ export class RuntimeRunner {
|
|
|
150
309
|
if (action1.kind !== "synthesize_insights")
|
|
151
310
|
throw new Error(`unexpected: ${action1.kind}`);
|
|
152
311
|
let synthesisText = "";
|
|
153
|
-
const
|
|
312
|
+
const dreamProvider = this.opts.dreamProvider ?? this.opts.provider;
|
|
313
|
+
const providerState = dreamProvider.createRunState?.();
|
|
154
314
|
const synthMsgs = (action1.messages ?? []);
|
|
155
315
|
const kernelSystemText = synthMsgs.filter(m => m.role === "system").map(m => m.content).join("\n\n");
|
|
156
316
|
const synthContext = {
|
|
@@ -158,7 +318,7 @@ export class RuntimeRunner {
|
|
|
158
318
|
turns: synthMsgs.filter(m => m.role !== "system"),
|
|
159
319
|
};
|
|
160
320
|
let totalTokens = 0;
|
|
161
|
-
for await (const evt of
|
|
321
|
+
for await (const evt of dreamProvider.stream(synthContext, [], undefined, providerState)) {
|
|
162
322
|
if (evt.type === "text_delta") {
|
|
163
323
|
synthesisText += evt.delta;
|
|
164
324
|
yield evt;
|
|
@@ -194,10 +354,93 @@ export class RuntimeRunner {
|
|
|
194
354
|
},
|
|
195
355
|
};
|
|
196
356
|
}
|
|
357
|
+
/** Resolve in-kernel AskUser suspend; returns resume lists and stream events to yield. */
|
|
358
|
+
async resolveKernelSuspend(runtime, sessionId) {
|
|
359
|
+
const gated = this.pendingObservations.filter((o) => o.kind === "tool_gated" && typeof o.call_id === "string" && typeof o.tool === "string");
|
|
360
|
+
const approved = [];
|
|
361
|
+
const denied = [];
|
|
362
|
+
const events = [];
|
|
363
|
+
const runCtx = { onPermissionRequest: this.opts.onPermissionRequest };
|
|
364
|
+
for (const g of gated) {
|
|
365
|
+
const request = {
|
|
366
|
+
type: "permission_request",
|
|
367
|
+
callId: g.call_id,
|
|
368
|
+
toolName: g.tool,
|
|
369
|
+
arguments: "{}",
|
|
370
|
+
reason: typeof g.reason === "string" ? g.reason : "",
|
|
371
|
+
};
|
|
372
|
+
events.push(request);
|
|
373
|
+
const decision = await resolvePermissionRequest(request, runCtx);
|
|
374
|
+
events.push({
|
|
375
|
+
type: "permission_resolved",
|
|
376
|
+
callId: g.call_id,
|
|
377
|
+
toolName: g.tool,
|
|
378
|
+
approved: decision.approved,
|
|
379
|
+
responder: decision.responder ?? "host",
|
|
380
|
+
...(decision.reason ? { reason: decision.reason } : {}),
|
|
381
|
+
});
|
|
382
|
+
await this.opts.sessionLog.append(sessionId, {
|
|
383
|
+
kind: "permission_requested",
|
|
384
|
+
turn: runtime.turn(),
|
|
385
|
+
tool: g.tool,
|
|
386
|
+
arguments: "{}",
|
|
387
|
+
reason: request.reason,
|
|
388
|
+
});
|
|
389
|
+
await this.opts.sessionLog.append(sessionId, {
|
|
390
|
+
kind: "permission_resolved",
|
|
391
|
+
turn: runtime.turn(),
|
|
392
|
+
approved: decision.approved,
|
|
393
|
+
responder: decision.responder ?? "host",
|
|
394
|
+
});
|
|
395
|
+
if (decision.approved) {
|
|
396
|
+
approved.push(g.call_id);
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
denied.push(g.call_id);
|
|
400
|
+
const denyReason = decision.reason ?? "permission denied";
|
|
401
|
+
events.push({
|
|
402
|
+
type: "tool_denied",
|
|
403
|
+
callId: g.call_id,
|
|
404
|
+
toolName: g.tool,
|
|
405
|
+
reason: denyReason,
|
|
406
|
+
});
|
|
407
|
+
events.push({
|
|
408
|
+
type: "tool_result",
|
|
409
|
+
callId: g.call_id,
|
|
410
|
+
name: g.tool,
|
|
411
|
+
content: `permission denied: ${denyReason}`,
|
|
412
|
+
isError: true,
|
|
413
|
+
errorKind: "governance_denied",
|
|
414
|
+
});
|
|
415
|
+
await this.opts.sessionLog.append(sessionId, {
|
|
416
|
+
kind: "tool_denied",
|
|
417
|
+
turn: runtime.turn(),
|
|
418
|
+
call_id: g.call_id,
|
|
419
|
+
tool_name: g.tool,
|
|
420
|
+
reason: denyReason,
|
|
421
|
+
});
|
|
422
|
+
await this.opts.sessionLog.append(sessionId, {
|
|
423
|
+
kind: "tool_completed",
|
|
424
|
+
turn: runtime.turn(),
|
|
425
|
+
results: [{
|
|
426
|
+
call_id: g.call_id,
|
|
427
|
+
output: `permission denied: ${denyReason}`,
|
|
428
|
+
is_error: true,
|
|
429
|
+
error_kind: "governance_denied",
|
|
430
|
+
}],
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
return { approved, denied, events };
|
|
435
|
+
}
|
|
197
436
|
async *execute(sessionId, goal, criteria, extensions, priorEvents, resumeMidRun = false) {
|
|
198
437
|
this.interrupted = false;
|
|
199
438
|
this.pendingObservations = [];
|
|
439
|
+
this.pendingSpoolOutputs.clear();
|
|
200
440
|
this.currentSessionId = sessionId;
|
|
441
|
+
if (this.opts.enableDiagnosticsDashboard) {
|
|
442
|
+
this.dashboard = new KernelPrimitivesDashboard(sessionId);
|
|
443
|
+
}
|
|
201
444
|
const kernel = getKernel();
|
|
202
445
|
const ext = { ...this.opts.extensions, ...(extensions ?? {}) };
|
|
203
446
|
const providerState = this.opts.provider.createRunState?.();
|
|
@@ -212,7 +455,6 @@ export class RuntimeRunner {
|
|
|
212
455
|
});
|
|
213
456
|
this.activeKernel = runtime;
|
|
214
457
|
this.nextArchiveStart = nextCompressedArchiveStart;
|
|
215
|
-
const router = new kernel.SignalRouter(256);
|
|
216
458
|
if (this.opts.tokenizer) {
|
|
217
459
|
kernelApply(runtime, this.pendingObservations, {
|
|
218
460
|
kind: "set_tokenizer",
|
|
@@ -300,11 +542,36 @@ export class RuntimeRunner {
|
|
|
300
542
|
if (this.opts.runSpec) {
|
|
301
543
|
startPayload.run_spec = agentRunSpecToKernel(this.opts.runSpec);
|
|
302
544
|
}
|
|
545
|
+
const attentionPolicy = this.opts.attentionPolicy ?? DEFAULT_NATIVE_ATTENTION_POLICY;
|
|
546
|
+
const governancePolicy = this.opts.governancePolicy ?? DEFAULT_NATIVE_GOVERNANCE_POLICY;
|
|
547
|
+
// Load the declarative governance policy into the kernel before the run starts,
|
|
548
|
+
// so the in-kernel gate enforces deny/veto/rate-limit/param before any tool runs.
|
|
549
|
+
kernelApply(runtime, this.pendingObservations, governancePolicyToKernelEvent(governancePolicy));
|
|
550
|
+
// Enable in-kernel signal routing so the kernel owns disposition + queuing.
|
|
551
|
+
kernelApply(runtime, this.pendingObservations, {
|
|
552
|
+
kind: "set_attention_policy",
|
|
553
|
+
...(attentionPolicy.maxQueueSize !== undefined
|
|
554
|
+
? { max_queue_size: attentionPolicy.maxQueueSize }
|
|
555
|
+
: {}),
|
|
556
|
+
});
|
|
557
|
+
// Set optional wall-clock budget override.
|
|
558
|
+
if (this.opts.schedulerBudget) {
|
|
559
|
+
kernelApply(runtime, this.pendingObservations, {
|
|
560
|
+
kind: "set_scheduler_budget",
|
|
561
|
+
...(this.opts.schedulerBudget.maxWallMs !== undefined
|
|
562
|
+
? { max_wall_ms: this.opts.schedulerBudget.maxWallMs }
|
|
563
|
+
: {}),
|
|
564
|
+
});
|
|
565
|
+
}
|
|
303
566
|
let action = resumeMidRun
|
|
304
567
|
? kernelAction(runtime, this.pendingObservations, { kind: "resume" })
|
|
305
568
|
: kernelAction(runtime, this.pendingObservations, startPayload);
|
|
306
569
|
let hasAttemptedReactiveCompact = false;
|
|
307
570
|
while (!runtime.isTerminal()) {
|
|
571
|
+
// Page-in must run before appendObservations drains pending kernel observations.
|
|
572
|
+
if (action.kind === "execute_tool") {
|
|
573
|
+
await this.applyKernelPageIn(runtime, sessionId);
|
|
574
|
+
}
|
|
308
575
|
nextCompressedArchiveStart = await this.appendObservations(sessionId, runtime, nextCompressedArchiveStart);
|
|
309
576
|
this.nextArchiveStart = nextCompressedArchiveStart;
|
|
310
577
|
if (this.interrupted) {
|
|
@@ -314,30 +581,31 @@ export class RuntimeRunner {
|
|
|
314
581
|
if (this.opts.signalSource) {
|
|
315
582
|
const sig = await this.opts.signalSource.nextSignal();
|
|
316
583
|
if (sig) {
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
584
|
+
const id = crypto.randomUUID();
|
|
585
|
+
const source = sig.source ?? "custom";
|
|
586
|
+
const signalType = sig.signalType ?? "event";
|
|
587
|
+
const urgency = sig.urgency ?? "normal";
|
|
588
|
+
const summary = String(sig.payload?.goal ?? sig.kind ?? "signal");
|
|
589
|
+
// Kernel-routed: the kernel decides disposition (dedup/queue/interrupt)
|
|
590
|
+
// and emits `signal_disposed`. An actionable disposition yields a new
|
|
591
|
+
// action to adopt; queued/observed/ignored yields none (kernel buffers).
|
|
592
|
+
// Wire shape is snake_case RuntimeSignal with an object payload.
|
|
593
|
+
const sigAction = kernelMaybeAction(runtime, this.pendingObservations, {
|
|
594
|
+
kind: "signal",
|
|
595
|
+
signal: {
|
|
596
|
+
id,
|
|
597
|
+
source,
|
|
598
|
+
signal_type: signalType,
|
|
599
|
+
urgency,
|
|
600
|
+
summary,
|
|
601
|
+
payload: sig.payload ?? {},
|
|
602
|
+
...(sig.dedupeKey ? { dedupe_key: sig.dedupeKey } : {}),
|
|
603
|
+
timestamp_ms: Date.now(),
|
|
604
|
+
},
|
|
605
|
+
});
|
|
606
|
+
if (sigAction)
|
|
607
|
+
action = sigAction;
|
|
339
608
|
}
|
|
340
|
-
queued = router.next();
|
|
341
609
|
}
|
|
342
610
|
if (runtime.isTerminal())
|
|
343
611
|
break;
|
|
@@ -398,12 +666,25 @@ export class RuntimeRunner {
|
|
|
398
666
|
toolCalls: finalToolCalls,
|
|
399
667
|
tokenCount: turnOutputTokens || turnTokens || undefined,
|
|
400
668
|
};
|
|
401
|
-
|
|
669
|
+
const providerEvent = {
|
|
402
670
|
kind: "provider_result",
|
|
403
671
|
message: messageToKernelMessage(assistantMessage),
|
|
404
672
|
...(turnInputTokens > 0 ? { observed_input_tokens: turnInputTokens } : {}),
|
|
405
673
|
...(turnOutputTokens > 0 ? { observed_output_tokens: turnOutputTokens } : {}),
|
|
406
|
-
|
|
674
|
+
now_ms: Date.now(),
|
|
675
|
+
};
|
|
676
|
+
let nextAction = kernelMaybeAction(runtime, this.pendingObservations, providerEvent);
|
|
677
|
+
if (!nextAction && this.pendingObservations.some(o => o.kind === "suspended")) {
|
|
678
|
+
const resolved = await this.resolveKernelSuspend(runtime, sessionId);
|
|
679
|
+
for (const evt of resolved.events)
|
|
680
|
+
yield evt;
|
|
681
|
+
nextAction = kernelAction(runtime, this.pendingObservations, {
|
|
682
|
+
kind: "resume",
|
|
683
|
+
approved_calls: resolved.approved,
|
|
684
|
+
denied_calls: resolved.denied,
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
action = nextAction ?? kernelAction(runtime, this.pendingObservations, providerEvent);
|
|
407
688
|
const providerReplay = peekProviderReplay(this.opts.provider, finalText, finalToolCalls);
|
|
408
689
|
await this.opts.sessionLog.append(sessionId, buildLlmCompletedEvent({
|
|
409
690
|
turn: runtime.turn(),
|
|
@@ -421,8 +702,9 @@ export class RuntimeRunner {
|
|
|
421
702
|
skillDir: this.opts.skillDir,
|
|
422
703
|
dreamStore: this.opts.dreamStore,
|
|
423
704
|
knowledgeSource: this.opts.knowledgeSource,
|
|
424
|
-
governance: this.opts.governance,
|
|
425
705
|
onToolSuspend: this.opts.onToolSuspend,
|
|
706
|
+
onPermissionRequest: this.opts.onPermissionRequest,
|
|
707
|
+
resultSpool: this.opts.resultSpool ?? new LargeResultSpool(),
|
|
426
708
|
};
|
|
427
709
|
const toolResults = [];
|
|
428
710
|
const normalCalls = allCalls.filter(c => c.name !== "update_plan");
|
|
@@ -477,21 +759,18 @@ export class RuntimeRunner {
|
|
|
477
759
|
kind: "permission_requested",
|
|
478
760
|
turn,
|
|
479
761
|
tool: pre.toolName,
|
|
480
|
-
arguments:
|
|
762
|
+
arguments: pre.arguments,
|
|
481
763
|
reason: pre.reason,
|
|
482
764
|
});
|
|
765
|
+
}
|
|
766
|
+
else if (evt.type === "permission_resolved") {
|
|
767
|
+
const resolved = evt;
|
|
768
|
+
const turn = runtime.turn();
|
|
483
769
|
await this.opts.sessionLog.append(sessionId, {
|
|
484
770
|
kind: "permission_resolved",
|
|
485
771
|
turn,
|
|
486
|
-
approved:
|
|
487
|
-
responder:
|
|
488
|
-
});
|
|
489
|
-
await this.opts.sessionLog.append(sessionId, {
|
|
490
|
-
kind: "tool_denied",
|
|
491
|
-
turn,
|
|
492
|
-
call_id: pre.callId,
|
|
493
|
-
tool_name: pre.toolName,
|
|
494
|
-
reason: `permission denied by policy gate: ${pre.reason}`,
|
|
772
|
+
approved: resolved.approved,
|
|
773
|
+
responder: resolved.responder,
|
|
495
774
|
});
|
|
496
775
|
}
|
|
497
776
|
}
|
|
@@ -511,6 +790,12 @@ export class RuntimeRunner {
|
|
|
511
790
|
token_count: r.tokenCount,
|
|
512
791
|
})),
|
|
513
792
|
});
|
|
793
|
+
for (const call of normalCalls) {
|
|
794
|
+
const result = toolResults.find(r => r.callId === call.id);
|
|
795
|
+
if (result) {
|
|
796
|
+
this.pendingSpoolOutputs.set(call.id, { tool: call.name, output: result.output });
|
|
797
|
+
}
|
|
798
|
+
}
|
|
514
799
|
action = kernelAction(runtime, this.pendingObservations, {
|
|
515
800
|
kind: "tool_results",
|
|
516
801
|
results: toolResults.map(toolResultToKernel),
|
|
@@ -590,18 +875,18 @@ export class RuntimeRunner {
|
|
|
590
875
|
yield { type: "done", iterations: turnsUsed, totalTokens, status };
|
|
591
876
|
this.activeKernel = null;
|
|
592
877
|
this.currentSessionId = null;
|
|
878
|
+
this.dashboard = null;
|
|
593
879
|
}
|
|
594
880
|
async appendObservations(sessionId, runtime, nextArchiveStart) {
|
|
595
881
|
const turn = runtime.turn();
|
|
596
882
|
const preservedRefs = runtime.preservedRefs();
|
|
597
883
|
const observations = this.pendingObservations.splice(0);
|
|
598
|
-
for (
|
|
884
|
+
for (let obs of observations) {
|
|
885
|
+
if (obs.kind === "page_in_requested")
|
|
886
|
+
continue;
|
|
887
|
+
let archiveRef;
|
|
888
|
+
let spoolRef;
|
|
599
889
|
if (obs.kind === "compressed") {
|
|
600
|
-
const latest = await this.opts.sessionLog.latestSeq(sessionId);
|
|
601
|
-
if (latest < nextArchiveStart)
|
|
602
|
-
continue;
|
|
603
|
-
const end = latest;
|
|
604
|
-
let archiveRef = undefined;
|
|
605
890
|
const archived = obs.archived;
|
|
606
891
|
if (this.opts.compressionStore && archived && archived.length > 0) {
|
|
607
892
|
try {
|
|
@@ -613,91 +898,77 @@ export class RuntimeRunner {
|
|
|
613
898
|
// non-fatal
|
|
614
899
|
}
|
|
615
900
|
}
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
901
|
+
}
|
|
902
|
+
if (obs.kind === "page_out" && obs.archived) {
|
|
903
|
+
this.localPageOutCache.push(...obs.archived);
|
|
904
|
+
}
|
|
905
|
+
if (obs.kind === "large_result_spooled") {
|
|
906
|
+
const pending = this.pendingSpoolOutputs.get(obs.call_id ?? "");
|
|
907
|
+
if (pending) {
|
|
908
|
+
const spool = this.opts.resultSpool ?? new LargeResultSpool();
|
|
909
|
+
try {
|
|
910
|
+
spoolRef = await spool.persistOutput(obs.call_id ?? "", pending.output);
|
|
911
|
+
}
|
|
912
|
+
catch {
|
|
913
|
+
// non-fatal: preview remains in kernel context; full output still in tool_completed log
|
|
914
|
+
}
|
|
915
|
+
if (!obs.tool && pending.tool) {
|
|
916
|
+
obs = { ...obs, tool: pending.tool };
|
|
917
|
+
}
|
|
918
|
+
this.pendingSpoolOutputs.delete(obs.call_id ?? "");
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
const latest = obs.kind === "compressed" ? await this.opts.sessionLog.latestSeq(sessionId) : undefined;
|
|
922
|
+
const event = kernelObservationToSessionEvent(obs, turn, {
|
|
923
|
+
nextArchiveStart,
|
|
924
|
+
latestSeq: latest,
|
|
925
|
+
archiveRef,
|
|
926
|
+
spoolRef,
|
|
927
|
+
preservedRefs,
|
|
928
|
+
compressionAction,
|
|
929
|
+
});
|
|
930
|
+
if (!event)
|
|
931
|
+
continue;
|
|
932
|
+
const compressedSeq = await this.opts.sessionLog.append(sessionId, event);
|
|
933
|
+
if (event.kind === "compressed") {
|
|
626
934
|
nextArchiveStart = compressedSeq + 1;
|
|
935
|
+
const archived = obs.kind === "compressed" ? obs.archived : undefined;
|
|
627
936
|
if (this.opts.asyncSummarizer && archived && archived.length > 0) {
|
|
628
937
|
void this.upgradeCompressedSummary(sessionId, compressedSeq, archived, compressionAction(obs.action) ?? "auto_compact");
|
|
629
938
|
}
|
|
630
939
|
}
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
reason: obs.reason,
|
|
637
|
-
});
|
|
638
|
-
}
|
|
639
|
-
else if (obs.kind === "capability_changed") {
|
|
640
|
-
await this.opts.sessionLog.append(sessionId, {
|
|
641
|
-
kind: "capability_changed",
|
|
642
|
-
turn: obs.turn ?? turn,
|
|
643
|
-
added: obs.added ?? [],
|
|
644
|
-
removed: obs.removed ?? [],
|
|
645
|
-
...(obs.change_kind != null && { change_kind: obs.change_kind }),
|
|
646
|
-
...(obs.capability_id != null && { capability_id: obs.capability_id }),
|
|
647
|
-
...(obs.version != null && { version: obs.version }),
|
|
648
|
-
...(obs.mounted_by != null && { mounted_by: obs.mounted_by }),
|
|
649
|
-
...(obs.mount_reason != null && { mount_reason: obs.mount_reason }),
|
|
650
|
-
});
|
|
651
|
-
}
|
|
652
|
-
else if (obs.kind === "milestone_advanced") {
|
|
653
|
-
await this.opts.sessionLog.append(sessionId, {
|
|
654
|
-
kind: "milestone_advanced",
|
|
655
|
-
turn: obs.turn ?? turn,
|
|
656
|
-
phase_id: obs.phase_id ?? "",
|
|
657
|
-
capabilities_unlocked: obs.capabilities_unlocked ?? [],
|
|
658
|
-
});
|
|
659
|
-
}
|
|
660
|
-
else if (obs.kind === "milestone_blocked") {
|
|
661
|
-
await this.opts.sessionLog.append(sessionId, {
|
|
662
|
-
kind: "milestone_blocked",
|
|
663
|
-
turn: obs.turn ?? turn,
|
|
664
|
-
phase_id: obs.phase_id ?? "",
|
|
665
|
-
reason: typeof obs.reason === "string" ? obs.reason : "",
|
|
666
|
-
});
|
|
667
|
-
}
|
|
668
|
-
else if (obs.kind === "milestone_evidence") {
|
|
669
|
-
await this.opts.sessionLog.append(sessionId, {
|
|
670
|
-
kind: "milestone_evidence",
|
|
671
|
-
turn: obs.turn ?? turn,
|
|
672
|
-
phase_id: obs.phase_id ?? "",
|
|
673
|
-
evidence: obs.evidence ?? [],
|
|
674
|
-
});
|
|
675
|
-
}
|
|
676
|
-
else if (obs.kind === "checkpoint_taken") {
|
|
677
|
-
await this.opts.sessionLog.append(sessionId, {
|
|
678
|
-
kind: "checkpoint_taken",
|
|
679
|
-
turn: obs.turn ?? turn,
|
|
680
|
-
history_len: obs.history_len ?? 0,
|
|
681
|
-
});
|
|
682
|
-
}
|
|
683
|
-
else if (obs.kind === "agent_spawned") {
|
|
684
|
-
await this.opts.sessionLog.append(sessionId, {
|
|
685
|
-
kind: "agent_spawned",
|
|
686
|
-
turn: obs.turn ?? turn,
|
|
687
|
-
agent_id: obs.agent_id ?? "",
|
|
688
|
-
parent_session_id: obs.parent_session_id ?? "",
|
|
689
|
-
role: obs.role ?? "",
|
|
690
|
-
isolation: obs.isolation ?? "",
|
|
691
|
-
context_inheritance: obs.context_inheritance ?? "",
|
|
692
|
-
permitted_capability_ids: obs.permitted_capability_ids ?? [],
|
|
693
|
-
});
|
|
694
|
-
}
|
|
695
|
-
else if (obs.kind !== "renewed") {
|
|
696
|
-
console.warn(`[deepstrike] unhandled KernelObservation kind: ${obs.kind}`);
|
|
940
|
+
if (obs.kind === "page_out"
|
|
941
|
+
&& obs.tier_hint === "semantic"
|
|
942
|
+
&& Array.isArray(obs.archived)
|
|
943
|
+
&& obs.archived.length > 0) {
|
|
944
|
+
void this.archiveSemanticPageOut(obs.archived, compressionAction(obs.action));
|
|
697
945
|
}
|
|
698
946
|
}
|
|
699
947
|
return nextArchiveStart;
|
|
700
948
|
}
|
|
949
|
+
async archiveSemanticPageOut(archived, action) {
|
|
950
|
+
if (!this.opts.dreamStore || !this.opts.agentId)
|
|
951
|
+
return;
|
|
952
|
+
try {
|
|
953
|
+
const summary = this.opts.dreamSummarizer
|
|
954
|
+
? await this.opts.dreamSummarizer.summarize(archived, { action })
|
|
955
|
+
: await summarizeForLongTermMemory(this.opts.dreamProvider ?? this.opts.provider, archived, this.opts.dreamSystemPrompt);
|
|
956
|
+
const existing = await this.opts.dreamStore.loadMemories(this.opts.agentId);
|
|
957
|
+
await this.opts.dreamStore.commit(this.opts.agentId, {
|
|
958
|
+
toAdd: [{ text: summary, score: 1.0, metadata: { source: "semantic_page_out", action } }],
|
|
959
|
+
toRemoveIndices: [],
|
|
960
|
+
stats: {
|
|
961
|
+
insightsProcessed: 1,
|
|
962
|
+
duplicatesRemoved: 0,
|
|
963
|
+
conflictsResolved: 0,
|
|
964
|
+
entriesAdded: 1,
|
|
965
|
+
},
|
|
966
|
+
}, existing);
|
|
967
|
+
}
|
|
968
|
+
catch {
|
|
969
|
+
// non-fatal: in-context compression summary remains; long-term layer is best-effort
|
|
970
|
+
}
|
|
971
|
+
}
|
|
701
972
|
async upgradeCompressedSummary(sessionId, compressedSeq, archived, action) {
|
|
702
973
|
try {
|
|
703
974
|
const summary = await this.opts.asyncSummarizer.summarize(archived, action);
|
|
@@ -724,6 +995,25 @@ function compressionAction(action) {
|
|
|
724
995
|
}
|
|
725
996
|
return undefined;
|
|
726
997
|
}
|
|
998
|
+
async function summarizeForLongTermMemory(provider, archived, systemPrompt) {
|
|
999
|
+
const transcript = archived
|
|
1000
|
+
.map(m => `${m.role}: ${m.content}`)
|
|
1001
|
+
.join("\n");
|
|
1002
|
+
const context = {
|
|
1003
|
+
systemText: [
|
|
1004
|
+
systemPrompt,
|
|
1005
|
+
"Summarize the following conversation for long-term memory. Preserve key facts, decisions, and open questions.",
|
|
1006
|
+
].filter(Boolean).join("\n\n"),
|
|
1007
|
+
turns: [{ role: "user", content: transcript, toolCalls: [] }],
|
|
1008
|
+
};
|
|
1009
|
+
let text = "";
|
|
1010
|
+
const state = provider.createRunState?.();
|
|
1011
|
+
for await (const evt of provider.stream(context, [], undefined, state)) {
|
|
1012
|
+
if (evt.type === "text_delta" && "delta" in evt)
|
|
1013
|
+
text += evt.delta;
|
|
1014
|
+
}
|
|
1015
|
+
return text.trim() || transcript.slice(0, 2000);
|
|
1016
|
+
}
|
|
727
1017
|
export function replayMessages(events, maxBytes) {
|
|
728
1018
|
// Build upgraded-summary index: compressed_seq -> upgraded summary
|
|
729
1019
|
const upgradedSummaries = new Map();
|