@deepstrike/sdk 0.2.4 → 0.2.6
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 +305 -69
- 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 +14 -6
- package/dist/index.js +5 -1
- package/dist/kernel.d.ts +43 -0
- package/dist/memory/agent.d.ts +66 -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 +30 -0
- package/dist/runtime/os-profile.js +71 -0
- package/dist/runtime/os-snapshot.d.ts +35 -0
- package/dist/runtime/os-snapshot.js +128 -0
- package/dist/runtime/runner.d.ts +79 -9
- package/dist/runtime/runner.js +463 -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 { assertNativeProfile } 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",
|
|
@@ -262,6 +504,20 @@ export class RuntimeRunner {
|
|
|
262
504
|
if (this.opts.dreamStore && this.opts.agentId) {
|
|
263
505
|
kernelApply(runtime, this.pendingObservations, { kind: "set_memory_enabled", enabled: true });
|
|
264
506
|
}
|
|
507
|
+
// Install optional memory policy. Maps the ergonomic camelCase option onto the kernel's
|
|
508
|
+
// snake_case `set_memory_policy` event; omitted fields fall back to kernel defaults.
|
|
509
|
+
if (this.opts.memoryPolicy) {
|
|
510
|
+
const m = this.opts.memoryPolicy;
|
|
511
|
+
kernelApply(runtime, this.pendingObservations, {
|
|
512
|
+
kind: "set_memory_policy",
|
|
513
|
+
...(m.memoryPath !== undefined ? { memory_path: m.memoryPath } : {}),
|
|
514
|
+
...(m.staleWarningDays !== undefined ? { stale_warning_days: m.staleWarningDays } : {}),
|
|
515
|
+
...(m.retrievalTopK !== undefined ? { retrieval_top_k: m.retrievalTopK } : {}),
|
|
516
|
+
...(m.validationEnabled !== undefined ? { validation_enabled: m.validationEnabled } : {}),
|
|
517
|
+
...(m.maxContentBytes !== undefined ? { max_content_bytes: m.maxContentBytes } : {}),
|
|
518
|
+
...(m.maxNameLength !== undefined ? { max_name_length: m.maxNameLength } : {}),
|
|
519
|
+
});
|
|
520
|
+
}
|
|
265
521
|
if (this.opts.knowledgeSource) {
|
|
266
522
|
kernelApply(runtime, this.pendingObservations, { kind: "set_knowledge_enabled", enabled: true });
|
|
267
523
|
}
|
|
@@ -300,11 +556,60 @@ export class RuntimeRunner {
|
|
|
300
556
|
if (this.opts.runSpec) {
|
|
301
557
|
startPayload.run_spec = agentRunSpecToKernel(this.opts.runSpec);
|
|
302
558
|
}
|
|
559
|
+
const osProfile = assertNativeProfile(this.opts.osProfile ?? "native");
|
|
560
|
+
const attentionPolicy = this.opts.attentionPolicy ?? osProfile.attentionPolicy;
|
|
561
|
+
const governancePolicy = this.opts.governancePolicy ?? osProfile.governancePolicy;
|
|
562
|
+
// Load the declarative governance policy into the kernel before the run starts,
|
|
563
|
+
// so the in-kernel gate enforces deny/veto/rate-limit/param before any tool runs.
|
|
564
|
+
kernelApply(runtime, this.pendingObservations, governancePolicyToKernelEvent(governancePolicy));
|
|
565
|
+
// Enable in-kernel signal routing so the kernel owns disposition + queuing.
|
|
566
|
+
kernelApply(runtime, this.pendingObservations, {
|
|
567
|
+
kind: "set_attention_policy",
|
|
568
|
+
...(attentionPolicy.maxQueueSize !== undefined
|
|
569
|
+
? { max_queue_size: attentionPolicy.maxQueueSize }
|
|
570
|
+
: {}),
|
|
571
|
+
});
|
|
572
|
+
// Set optional wall-clock budget override.
|
|
573
|
+
if (this.opts.schedulerBudget) {
|
|
574
|
+
kernelApply(runtime, this.pendingObservations, {
|
|
575
|
+
kind: "set_scheduler_budget",
|
|
576
|
+
...(this.opts.schedulerBudget.maxWallMs !== undefined
|
|
577
|
+
? { max_wall_ms: this.opts.schedulerBudget.maxWallMs }
|
|
578
|
+
: {}),
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
// Install optional resource quotas at the syscall trap (M2). Maps the ergonomic camelCase
|
|
582
|
+
// option onto the kernel's snake_case quota shape; the write-rate window is the serde tuple
|
|
583
|
+
// `[maxWrites, windowMs]`. Omitting the option leaves spawn / memory writes unbounded.
|
|
584
|
+
if (this.opts.resourceQuota) {
|
|
585
|
+
const q = this.opts.resourceQuota;
|
|
586
|
+
kernelApply(runtime, this.pendingObservations, {
|
|
587
|
+
kind: "set_resource_quota",
|
|
588
|
+
quota: {
|
|
589
|
+
...(q.maxConcurrentSubagents !== undefined
|
|
590
|
+
? { max_concurrent_subagents: q.maxConcurrentSubagents }
|
|
591
|
+
: {}),
|
|
592
|
+
...(q.maxSpawnDepth !== undefined ? { max_spawn_depth: q.maxSpawnDepth } : {}),
|
|
593
|
+
...(q.memoryWritesPerWindow !== undefined
|
|
594
|
+
? {
|
|
595
|
+
memory_writes_per_window: [
|
|
596
|
+
q.memoryWritesPerWindow.maxWrites,
|
|
597
|
+
q.memoryWritesPerWindow.windowMs,
|
|
598
|
+
],
|
|
599
|
+
}
|
|
600
|
+
: {}),
|
|
601
|
+
},
|
|
602
|
+
});
|
|
603
|
+
}
|
|
303
604
|
let action = resumeMidRun
|
|
304
605
|
? kernelAction(runtime, this.pendingObservations, { kind: "resume" })
|
|
305
606
|
: kernelAction(runtime, this.pendingObservations, startPayload);
|
|
306
607
|
let hasAttemptedReactiveCompact = false;
|
|
307
608
|
while (!runtime.isTerminal()) {
|
|
609
|
+
// Page-in must run before appendObservations drains pending kernel observations.
|
|
610
|
+
if (action.kind === "execute_tool") {
|
|
611
|
+
await this.applyKernelPageIn(runtime, sessionId);
|
|
612
|
+
}
|
|
308
613
|
nextCompressedArchiveStart = await this.appendObservations(sessionId, runtime, nextCompressedArchiveStart);
|
|
309
614
|
this.nextArchiveStart = nextCompressedArchiveStart;
|
|
310
615
|
if (this.interrupted) {
|
|
@@ -314,30 +619,31 @@ export class RuntimeRunner {
|
|
|
314
619
|
if (this.opts.signalSource) {
|
|
315
620
|
const sig = await this.opts.signalSource.nextSignal();
|
|
316
621
|
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
|
-
|
|
622
|
+
const id = crypto.randomUUID();
|
|
623
|
+
const source = sig.source ?? "custom";
|
|
624
|
+
const signalType = sig.signalType ?? "event";
|
|
625
|
+
const urgency = sig.urgency ?? "normal";
|
|
626
|
+
const summary = String(sig.payload?.goal ?? sig.kind ?? "signal");
|
|
627
|
+
// Kernel-routed: the kernel decides disposition (dedup/queue/interrupt)
|
|
628
|
+
// and emits `signal_disposed`. An actionable disposition yields a new
|
|
629
|
+
// action to adopt; queued/observed/ignored yields none (kernel buffers).
|
|
630
|
+
// Wire shape is snake_case RuntimeSignal with an object payload.
|
|
631
|
+
const sigAction = kernelMaybeAction(runtime, this.pendingObservations, {
|
|
632
|
+
kind: "signal",
|
|
633
|
+
signal: {
|
|
634
|
+
id,
|
|
635
|
+
source,
|
|
636
|
+
signal_type: signalType,
|
|
637
|
+
urgency,
|
|
638
|
+
summary,
|
|
639
|
+
payload: sig.payload ?? {},
|
|
640
|
+
...(sig.dedupeKey ? { dedupe_key: sig.dedupeKey } : {}),
|
|
641
|
+
timestamp_ms: Date.now(),
|
|
642
|
+
},
|
|
643
|
+
});
|
|
644
|
+
if (sigAction)
|
|
645
|
+
action = sigAction;
|
|
339
646
|
}
|
|
340
|
-
queued = router.next();
|
|
341
647
|
}
|
|
342
648
|
if (runtime.isTerminal())
|
|
343
649
|
break;
|
|
@@ -398,12 +704,25 @@ export class RuntimeRunner {
|
|
|
398
704
|
toolCalls: finalToolCalls,
|
|
399
705
|
tokenCount: turnOutputTokens || turnTokens || undefined,
|
|
400
706
|
};
|
|
401
|
-
|
|
707
|
+
const providerEvent = {
|
|
402
708
|
kind: "provider_result",
|
|
403
709
|
message: messageToKernelMessage(assistantMessage),
|
|
404
710
|
...(turnInputTokens > 0 ? { observed_input_tokens: turnInputTokens } : {}),
|
|
405
711
|
...(turnOutputTokens > 0 ? { observed_output_tokens: turnOutputTokens } : {}),
|
|
406
|
-
|
|
712
|
+
now_ms: Date.now(),
|
|
713
|
+
};
|
|
714
|
+
let nextAction = kernelMaybeAction(runtime, this.pendingObservations, providerEvent);
|
|
715
|
+
if (!nextAction && this.pendingObservations.some(o => o.kind === "suspended")) {
|
|
716
|
+
const resolved = await this.resolveKernelSuspend(runtime, sessionId);
|
|
717
|
+
for (const evt of resolved.events)
|
|
718
|
+
yield evt;
|
|
719
|
+
nextAction = kernelAction(runtime, this.pendingObservations, {
|
|
720
|
+
kind: "resume",
|
|
721
|
+
approved_calls: resolved.approved,
|
|
722
|
+
denied_calls: resolved.denied,
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
action = nextAction ?? kernelAction(runtime, this.pendingObservations, providerEvent);
|
|
407
726
|
const providerReplay = peekProviderReplay(this.opts.provider, finalText, finalToolCalls);
|
|
408
727
|
await this.opts.sessionLog.append(sessionId, buildLlmCompletedEvent({
|
|
409
728
|
turn: runtime.turn(),
|
|
@@ -421,8 +740,9 @@ export class RuntimeRunner {
|
|
|
421
740
|
skillDir: this.opts.skillDir,
|
|
422
741
|
dreamStore: this.opts.dreamStore,
|
|
423
742
|
knowledgeSource: this.opts.knowledgeSource,
|
|
424
|
-
governance: this.opts.governance,
|
|
425
743
|
onToolSuspend: this.opts.onToolSuspend,
|
|
744
|
+
onPermissionRequest: this.opts.onPermissionRequest,
|
|
745
|
+
resultSpool: this.opts.resultSpool ?? new LargeResultSpool(),
|
|
426
746
|
};
|
|
427
747
|
const toolResults = [];
|
|
428
748
|
const normalCalls = allCalls.filter(c => c.name !== "update_plan");
|
|
@@ -477,21 +797,18 @@ export class RuntimeRunner {
|
|
|
477
797
|
kind: "permission_requested",
|
|
478
798
|
turn,
|
|
479
799
|
tool: pre.toolName,
|
|
480
|
-
arguments:
|
|
800
|
+
arguments: pre.arguments,
|
|
481
801
|
reason: pre.reason,
|
|
482
802
|
});
|
|
803
|
+
}
|
|
804
|
+
else if (evt.type === "permission_resolved") {
|
|
805
|
+
const resolved = evt;
|
|
806
|
+
const turn = runtime.turn();
|
|
483
807
|
await this.opts.sessionLog.append(sessionId, {
|
|
484
808
|
kind: "permission_resolved",
|
|
485
809
|
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}`,
|
|
810
|
+
approved: resolved.approved,
|
|
811
|
+
responder: resolved.responder,
|
|
495
812
|
});
|
|
496
813
|
}
|
|
497
814
|
}
|
|
@@ -511,6 +828,12 @@ export class RuntimeRunner {
|
|
|
511
828
|
token_count: r.tokenCount,
|
|
512
829
|
})),
|
|
513
830
|
});
|
|
831
|
+
for (const call of normalCalls) {
|
|
832
|
+
const result = toolResults.find(r => r.callId === call.id);
|
|
833
|
+
if (result) {
|
|
834
|
+
this.pendingSpoolOutputs.set(call.id, { tool: call.name, output: result.output });
|
|
835
|
+
}
|
|
836
|
+
}
|
|
514
837
|
action = kernelAction(runtime, this.pendingObservations, {
|
|
515
838
|
kind: "tool_results",
|
|
516
839
|
results: toolResults.map(toolResultToKernel),
|
|
@@ -590,18 +913,18 @@ export class RuntimeRunner {
|
|
|
590
913
|
yield { type: "done", iterations: turnsUsed, totalTokens, status };
|
|
591
914
|
this.activeKernel = null;
|
|
592
915
|
this.currentSessionId = null;
|
|
916
|
+
this.dashboard = null;
|
|
593
917
|
}
|
|
594
918
|
async appendObservations(sessionId, runtime, nextArchiveStart) {
|
|
595
919
|
const turn = runtime.turn();
|
|
596
920
|
const preservedRefs = runtime.preservedRefs();
|
|
597
921
|
const observations = this.pendingObservations.splice(0);
|
|
598
|
-
for (
|
|
922
|
+
for (let obs of observations) {
|
|
923
|
+
if (obs.kind === "page_in_requested")
|
|
924
|
+
continue;
|
|
925
|
+
let archiveRef;
|
|
926
|
+
let spoolRef;
|
|
599
927
|
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
928
|
const archived = obs.archived;
|
|
606
929
|
if (this.opts.compressionStore && archived && archived.length > 0) {
|
|
607
930
|
try {
|
|
@@ -613,91 +936,77 @@ export class RuntimeRunner {
|
|
|
613
936
|
// non-fatal
|
|
614
937
|
}
|
|
615
938
|
}
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
939
|
+
}
|
|
940
|
+
if (obs.kind === "page_out" && obs.archived) {
|
|
941
|
+
this.localPageOutCache.push(...obs.archived);
|
|
942
|
+
}
|
|
943
|
+
if (obs.kind === "large_result_spooled") {
|
|
944
|
+
const pending = this.pendingSpoolOutputs.get(obs.call_id ?? "");
|
|
945
|
+
if (pending) {
|
|
946
|
+
const spool = this.opts.resultSpool ?? new LargeResultSpool();
|
|
947
|
+
try {
|
|
948
|
+
spoolRef = await spool.persistOutput(obs.call_id ?? "", pending.output);
|
|
949
|
+
}
|
|
950
|
+
catch {
|
|
951
|
+
// non-fatal: preview remains in kernel context; full output still in tool_completed log
|
|
952
|
+
}
|
|
953
|
+
if (!obs.tool && pending.tool) {
|
|
954
|
+
obs = { ...obs, tool: pending.tool };
|
|
955
|
+
}
|
|
956
|
+
this.pendingSpoolOutputs.delete(obs.call_id ?? "");
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
const latest = obs.kind === "compressed" ? await this.opts.sessionLog.latestSeq(sessionId) : undefined;
|
|
960
|
+
const event = kernelObservationToSessionEvent(obs, turn, {
|
|
961
|
+
nextArchiveStart,
|
|
962
|
+
latestSeq: latest,
|
|
963
|
+
archiveRef,
|
|
964
|
+
spoolRef,
|
|
965
|
+
preservedRefs,
|
|
966
|
+
compressionAction,
|
|
967
|
+
});
|
|
968
|
+
if (!event)
|
|
969
|
+
continue;
|
|
970
|
+
const compressedSeq = await this.opts.sessionLog.append(sessionId, event);
|
|
971
|
+
if (event.kind === "compressed") {
|
|
626
972
|
nextArchiveStart = compressedSeq + 1;
|
|
973
|
+
const archived = obs.kind === "compressed" ? obs.archived : undefined;
|
|
627
974
|
if (this.opts.asyncSummarizer && archived && archived.length > 0) {
|
|
628
975
|
void this.upgradeCompressedSummary(sessionId, compressedSeq, archived, compressionAction(obs.action) ?? "auto_compact");
|
|
629
976
|
}
|
|
630
977
|
}
|
|
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}`);
|
|
978
|
+
if (obs.kind === "page_out"
|
|
979
|
+
&& obs.tier_hint === "semantic"
|
|
980
|
+
&& Array.isArray(obs.archived)
|
|
981
|
+
&& obs.archived.length > 0) {
|
|
982
|
+
void this.archiveSemanticPageOut(obs.archived, compressionAction(obs.action));
|
|
697
983
|
}
|
|
698
984
|
}
|
|
699
985
|
return nextArchiveStart;
|
|
700
986
|
}
|
|
987
|
+
async archiveSemanticPageOut(archived, action) {
|
|
988
|
+
if (!this.opts.dreamStore || !this.opts.agentId)
|
|
989
|
+
return;
|
|
990
|
+
try {
|
|
991
|
+
const summary = this.opts.dreamSummarizer
|
|
992
|
+
? await this.opts.dreamSummarizer.summarize(archived, { action })
|
|
993
|
+
: await summarizeForLongTermMemory(this.opts.dreamProvider ?? this.opts.provider, archived, this.opts.dreamSystemPrompt);
|
|
994
|
+
const existing = await this.opts.dreamStore.loadMemories(this.opts.agentId);
|
|
995
|
+
await this.opts.dreamStore.commit(this.opts.agentId, {
|
|
996
|
+
toAdd: [{ text: summary, score: 1.0, metadata: { source: "semantic_page_out", action } }],
|
|
997
|
+
toRemoveIndices: [],
|
|
998
|
+
stats: {
|
|
999
|
+
insightsProcessed: 1,
|
|
1000
|
+
duplicatesRemoved: 0,
|
|
1001
|
+
conflictsResolved: 0,
|
|
1002
|
+
entriesAdded: 1,
|
|
1003
|
+
},
|
|
1004
|
+
}, existing);
|
|
1005
|
+
}
|
|
1006
|
+
catch {
|
|
1007
|
+
// non-fatal: in-context compression summary remains; long-term layer is best-effort
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
701
1010
|
async upgradeCompressedSummary(sessionId, compressedSeq, archived, action) {
|
|
702
1011
|
try {
|
|
703
1012
|
const summary = await this.opts.asyncSummarizer.summarize(archived, action);
|
|
@@ -724,6 +1033,25 @@ function compressionAction(action) {
|
|
|
724
1033
|
}
|
|
725
1034
|
return undefined;
|
|
726
1035
|
}
|
|
1036
|
+
async function summarizeForLongTermMemory(provider, archived, systemPrompt) {
|
|
1037
|
+
const transcript = archived
|
|
1038
|
+
.map(m => `${m.role}: ${m.content}`)
|
|
1039
|
+
.join("\n");
|
|
1040
|
+
const context = {
|
|
1041
|
+
systemText: [
|
|
1042
|
+
systemPrompt,
|
|
1043
|
+
"Summarize the following conversation for long-term memory. Preserve key facts, decisions, and open questions.",
|
|
1044
|
+
].filter(Boolean).join("\n\n"),
|
|
1045
|
+
turns: [{ role: "user", content: transcript, toolCalls: [] }],
|
|
1046
|
+
};
|
|
1047
|
+
let text = "";
|
|
1048
|
+
const state = provider.createRunState?.();
|
|
1049
|
+
for await (const evt of provider.stream(context, [], undefined, state)) {
|
|
1050
|
+
if (evt.type === "text_delta" && "delta" in evt)
|
|
1051
|
+
text += evt.delta;
|
|
1052
|
+
}
|
|
1053
|
+
return text.trim() || transcript.slice(0, 2000);
|
|
1054
|
+
}
|
|
727
1055
|
export function replayMessages(events, maxBytes) {
|
|
728
1056
|
// Build upgraded-summary index: compressed_seq -> upgraded summary
|
|
729
1057
|
const upgradedSummaries = new Map();
|