@deepstrike/sdk 0.2.52 → 0.2.61
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 +28 -28
- package/dist/agent-ir.d.ts +103 -0
- package/dist/agent-ir.js +134 -0
- package/dist/agent.d.ts +67 -0
- package/dist/agent.js +36 -0
- package/dist/collaboration/harness.js +1 -1
- package/dist/collaboration/modes/creator-verifier.d.ts +2 -7
- package/dist/collaboration/modes/creator-verifier.js +4 -6
- package/dist/collaboration/pool.d.ts +8 -20
- package/dist/collaboration/pool.js +27 -97
- package/dist/compat/anthropic/mcp.d.ts +15 -0
- package/dist/compat/anthropic/mcp.js +10 -0
- package/dist/compat/openai/agent.d.ts +34 -0
- package/dist/compat/openai/agent.js +24 -0
- package/dist/governance.d.ts +1 -17
- package/dist/governance.js +1 -34
- package/dist/guardrail.d.ts +6 -0
- package/dist/guardrail.js +1 -0
- package/dist/handoff-target.d.ts +12 -0
- package/dist/handoff-target.js +1 -0
- package/dist/harness/manifest.js +0 -4
- package/dist/index.d.ts +19 -7
- package/dist/index.js +8 -5
- package/dist/kernel.d.ts +2 -20
- package/dist/knowledge/public.d.ts +29 -0
- package/dist/knowledge/public.js +1 -0
- package/dist/mcp-server.d.ts +28 -0
- package/dist/mcp-server.js +1 -0
- package/dist/memory/agent.d.ts +2 -2
- package/dist/memory/agent.js +2 -2
- package/dist/memory/durable.d.ts +16 -0
- package/dist/memory/durable.js +46 -0
- package/dist/memory/in-memory-store.d.ts +9 -7
- package/dist/memory/in-memory-store.js +8 -2
- package/dist/memory/protocols.d.ts +22 -4
- package/dist/memory/public.d.ts +4 -3
- package/dist/memory/public.js +3 -2
- package/dist/os/public.d.ts +1 -1
- package/dist/os/public.js +1 -1
- package/dist/providers/anthropic-adapter.d.ts +59 -0
- package/dist/providers/anthropic-adapter.js +530 -0
- package/dist/providers/anthropic-compatible.d.ts +2 -3
- package/dist/providers/anthropic-compatible.js +8 -5
- package/dist/providers/anthropic.d.ts +20 -23
- package/dist/providers/anthropic.js +176 -395
- package/dist/providers/base.d.ts +2 -2
- package/dist/providers/base.js +50 -8
- package/dist/providers/capability-router.d.ts +29 -0
- package/dist/providers/capability-router.js +43 -0
- package/dist/providers/catalog.d.ts +16 -4
- package/dist/providers/catalog.js +112 -36
- package/dist/providers/content-normalization.d.ts +57 -0
- package/dist/providers/content-normalization.js +238 -0
- package/dist/providers/content-policy.d.ts +16 -0
- package/dist/providers/content-policy.js +39 -0
- package/dist/providers/credentials.d.ts +83 -0
- package/dist/providers/credentials.js +190 -0
- package/dist/providers/endpoints.d.ts +137 -0
- package/dist/providers/endpoints.js +128 -0
- package/dist/providers/factories.js +25 -9
- package/dist/providers/gemini-adapter.d.ts +33 -0
- package/dist/providers/gemini-adapter.js +264 -0
- package/dist/providers/gemini.d.ts +16 -3
- package/dist/providers/gemini.js +97 -195
- package/dist/providers/model-catalog.d.ts +37 -0
- package/dist/providers/model-catalog.js +62 -0
- package/dist/providers/model-registry.d.ts +119 -0
- package/dist/providers/model-registry.js +379 -0
- package/dist/providers/ollama-adapter.d.ts +65 -0
- package/dist/providers/ollama-adapter.js +188 -0
- package/dist/providers/ollama.d.ts +9 -4
- package/dist/providers/ollama.js +96 -109
- package/dist/providers/openai-chat-dialects.d.ts +154 -0
- package/dist/providers/openai-chat-dialects.js +179 -0
- package/dist/providers/openai-chat.d.ts +46 -18
- package/dist/providers/openai-chat.js +416 -51
- package/dist/providers/openai-responses-adapter.d.ts +42 -0
- package/dist/providers/openai-responses-adapter.js +343 -0
- package/dist/providers/openai-responses.d.ts +19 -33
- package/dist/providers/openai-responses.js +164 -264
- package/dist/providers/openai.d.ts +29 -76
- package/dist/providers/openai.js +195 -292
- package/dist/providers/protocol-adapter.d.ts +39 -0
- package/dist/providers/protocol-adapter.js +13 -0
- package/dist/providers/protocol-capabilities.d.ts +34 -0
- package/dist/providers/protocol-capabilities.js +44 -0
- package/dist/providers/provider-error.d.ts +31 -0
- package/dist/providers/provider-error.js +153 -0
- package/dist/providers/public.d.ts +26 -3
- package/dist/providers/public.js +13 -1
- package/dist/providers/registry.d.ts +7 -6
- package/dist/providers/registry.js +47 -20
- package/dist/providers/request-plan.d.ts +89 -0
- package/dist/providers/request-plan.js +199 -0
- package/dist/providers/usage-normalizer.d.ts +48 -0
- package/dist/providers/usage-normalizer.js +139 -0
- package/dist/providers/vendor-profiles.d.ts +2 -15
- package/dist/providers/vendor-profiles.js +14 -60
- package/dist/runtime/canonical-kernel-step.d.ts +1 -2
- package/dist/runtime/canonical-kernel-step.js +47 -12
- package/dist/runtime/context-policy.d.ts +10 -12
- package/dist/runtime/context-policy.js +6 -8
- package/dist/runtime/durable-content.d.ts +50 -0
- package/dist/runtime/durable-content.js +159 -0
- package/dist/runtime/execution-plane.d.ts +2 -2
- package/dist/runtime/execution-plane.js +2 -2
- package/dist/runtime/kernel-event-log.js +0 -1
- package/dist/runtime/kernel-step.d.ts +0 -1
- package/dist/runtime/kernel-step.js +4 -2
- package/dist/runtime/mcp-proxy-plane.d.ts +25 -1
- package/dist/runtime/mcp-proxy-plane.js +44 -6
- package/dist/runtime/output-schema.d.ts +1 -2
- package/dist/runtime/provider-replay.d.ts +5 -1
- package/dist/runtime/provider-replay.js +26 -27
- package/dist/runtime/reactive-session.d.ts +1 -1
- package/dist/runtime/reactive-session.js +2 -3
- package/dist/runtime/run-group.d.ts +1 -1
- package/dist/runtime/runner.d.ts +31 -45
- package/dist/runtime/runner.js +178 -63
- package/dist/runtime/session-log.d.ts +8 -1
- package/dist/runtime/session-log.js +42 -2
- package/dist/runtime/session-repair.d.ts +1 -1
- package/dist/runtime/session-repair.js +1 -1
- package/dist/runtime/sub-agent-orchestrator.d.ts +1 -1
- package/dist/runtime/sub-agent-orchestrator.js +8 -11
- package/dist/runtime/workflow-control-flow.d.ts +0 -4
- package/dist/runtime/workflow-control-flow.js +0 -16
- package/dist/session.d.ts +11 -0
- package/dist/session.js +1 -0
- package/dist/skill.d.ts +17 -0
- package/dist/skill.js +16 -0
- package/dist/skills/loader.d.ts +3 -0
- package/dist/tools/errors.d.ts +1 -3
- package/dist/tools/errors.js +1 -3
- package/dist/tools/index.d.ts +3 -0
- package/dist/types/agent.d.ts +21 -9
- package/dist/types/agent.js +30 -4
- package/dist/types.d.ts +135 -17
- package/package.json +4 -4
- package/dist/providers/deepseek.d.ts +0 -46
- package/dist/providers/deepseek.js +0 -97
- package/dist/providers/glm.d.ts +0 -25
- package/dist/providers/glm.js +0 -48
- package/dist/providers/kimi.d.ts +0 -23
- package/dist/providers/kimi.js +0 -30
- package/dist/providers/minimax.d.ts +0 -49
- package/dist/providers/minimax.js +0 -98
- package/dist/providers/profiles.d.ts +0 -1992
- package/dist/providers/profiles.js +0 -796
- package/dist/providers/qwen.d.ts +0 -38
- package/dist/providers/qwen.js +0 -97
package/dist/runtime/runner.js
CHANGED
|
@@ -13,29 +13,37 @@ import { agentRunSpecToKernel, MILESTONE_UNVERIFIED_REASON, milestoneCheckFail,
|
|
|
13
13
|
import { defaultSubAgentOrchestrator } from "./sub-agent-orchestrator.js";
|
|
14
14
|
import { extractJsonValue, schemaInstruction, schemaRetryInstruction, validateAgainstSchema, } from "./output-schema.js";
|
|
15
15
|
import { resolveReducer } from "./reducers.js";
|
|
16
|
-
import { loopInstruction, classifyInstruction, judgeGoal, dependencyOutputsNote,
|
|
16
|
+
import { loopInstruction, classifyInstruction, judgeGoal, dependencyOutputsNote, extractClassifyBranch, extractJudgeWinner, } from "./workflow-control-flow.js";
|
|
17
17
|
import { governancePolicyToKernelEvent, governanceFilterSchema } from "../governance.js";
|
|
18
|
+
import { createProviderRequestPlanForProvider, estimateProviderPromptTokens, measurementForPlan, recordPromptMeasurement, } from "../providers/request-plan.js";
|
|
18
19
|
import { kernelObservationToSessionEvent } from "./kernel-event-log.js";
|
|
19
20
|
import { assertNativeProfile } from "./os-profile.js";
|
|
20
21
|
import { PayloadStore } from "./payload-store.js";
|
|
21
22
|
import { formatToolError } from "../tools/errors.js";
|
|
22
23
|
import { ManagedTaskScope } from "./reliability.js";
|
|
23
|
-
import {
|
|
24
|
+
import { contextPolicy, normalizeContextPolicy, } from "./context-policy.js";
|
|
24
25
|
import { composeSystemPrompt } from "../harness/manifest.js";
|
|
26
|
+
import { attachToolOutputOverlay, normalizeToolResultContent } from "../providers/content-normalization.js";
|
|
27
|
+
import { decodeDurableContent, decodeDurableToolResult, durableBlocksToToolOutput, toolOutputBlocksToDurable } from "./durable-content.js";
|
|
28
|
+
import { classifyProviderError, providerErrorEventFields } from "../providers/provider-error.js";
|
|
25
29
|
import { NudgeEngine } from "../harness/nudge.js";
|
|
26
30
|
export function schedulerPolicyToKernel(policy) {
|
|
27
31
|
const allowed = new Set([
|
|
28
|
-
"
|
|
32
|
+
"criticalPathWeight", "fanoutWeight", "ageWeight", "tokenCostWeight",
|
|
33
|
+
"deadlineWeight", "processPriorityWeight", "resourcePressureWeight", "budgetPressureWeight",
|
|
29
34
|
]);
|
|
30
35
|
const unknown = Object.keys(policy).filter(key => !allowed.has(key));
|
|
31
36
|
if (unknown.length > 0)
|
|
32
37
|
throw new TypeError(`unknown scheduler policy field(s): ${unknown.join(", ")}`);
|
|
33
38
|
return {
|
|
34
|
-
version: policy.version,
|
|
35
39
|
critical_path_weight: policy.criticalPathWeight,
|
|
36
40
|
fanout_weight: policy.fanoutWeight,
|
|
37
41
|
age_weight: policy.ageWeight,
|
|
38
42
|
token_cost_weight: policy.tokenCostWeight,
|
|
43
|
+
...(policy.deadlineWeight !== undefined ? { deadline_weight: policy.deadlineWeight } : {}),
|
|
44
|
+
...(policy.processPriorityWeight !== undefined ? { process_priority_weight: policy.processPriorityWeight } : {}),
|
|
45
|
+
...(policy.resourcePressureWeight !== undefined ? { resource_pressure_weight: policy.resourcePressureWeight } : {}),
|
|
46
|
+
...(policy.budgetPressureWeight !== undefined ? { budget_pressure_weight: policy.budgetPressureWeight } : {}),
|
|
39
47
|
};
|
|
40
48
|
}
|
|
41
49
|
function kernelReliabilityToKernel(policy) {
|
|
@@ -214,19 +222,19 @@ export class RuntimeRunner {
|
|
|
214
222
|
return this.fallbackPayloadStore;
|
|
215
223
|
}
|
|
216
224
|
async persistMemoryToStore(memory, agentId) {
|
|
217
|
-
if (!this.opts.
|
|
218
|
-
throw new Error("memory persistence requires
|
|
219
|
-
await this.opts.
|
|
225
|
+
if (!this.opts.memoryStore)
|
|
226
|
+
throw new Error("memory persistence requires memoryStore");
|
|
227
|
+
await this.opts.memoryStore.put(agentId, memory);
|
|
220
228
|
}
|
|
221
229
|
async retrieveMemoryFromStore(query, requestedK, agentId) {
|
|
222
|
-
if (!this.opts.
|
|
223
|
-
throw new Error("memory queries require
|
|
224
|
-
return (await this.opts.
|
|
230
|
+
if (!this.opts.memoryStore)
|
|
231
|
+
throw new Error("memory queries require memoryStore");
|
|
232
|
+
return (await this.opts.memoryStore.search(agentId, { ...query, top_k: requestedK }))
|
|
225
233
|
.slice(0, requestedK);
|
|
226
234
|
}
|
|
227
235
|
/**
|
|
228
236
|
* Route a host-originated renewal prefetch into canonical knowledge commands. This is not an
|
|
229
|
-
* agent syscall: the host selects records from its store, then the kernel owns the
|
|
237
|
+
* agent syscall: the host selects records from its store, then the kernel owns the write
|
|
230
238
|
* of live semantic context. `seenRecordIds` is the prefetch's dedupe horizon.
|
|
231
239
|
*/
|
|
232
240
|
async prefetchMemoryIntoKnowledge(runtime, query, agentId, sessionId, seenRecordIds, leftovers) {
|
|
@@ -261,7 +269,7 @@ export class RuntimeRunner {
|
|
|
261
269
|
async writeMemory(memory, opts = {}) {
|
|
262
270
|
const sessionId = opts.sessionId ?? this.currentSessionId;
|
|
263
271
|
const agentId = opts.agentId ?? this.opts.agentId;
|
|
264
|
-
if (!this.opts.
|
|
272
|
+
if (!this.opts.memoryStore || !agentId)
|
|
265
273
|
return;
|
|
266
274
|
const policy = this.opts.memoryPolicy;
|
|
267
275
|
if (policy?.validationEnabled !== false) {
|
|
@@ -300,7 +308,7 @@ export class RuntimeRunner {
|
|
|
300
308
|
async queryMemory(query, opts = {}) {
|
|
301
309
|
const sessionId = opts.sessionId ?? this.currentSessionId;
|
|
302
310
|
const agentId = opts.agentId ?? this.opts.agentId;
|
|
303
|
-
if (!this.opts.
|
|
311
|
+
if (!this.opts.memoryStore || !agentId)
|
|
304
312
|
return [];
|
|
305
313
|
const hits = await this.retrieveMemoryFromStore(query, query.top_k, agentId);
|
|
306
314
|
await this.applyHostMemoryRecallLifecycle(hits, agentId);
|
|
@@ -315,7 +323,7 @@ export class RuntimeRunner {
|
|
|
315
323
|
recall_count: hit.record.recall_count + 1,
|
|
316
324
|
last_recalled_at: Date.now(),
|
|
317
325
|
}));
|
|
318
|
-
await this.opts.
|
|
326
|
+
await this.opts.memoryStore?.recordRecall?.(agentId, recalls);
|
|
319
327
|
const threshold = this.opts.memoryPolicy?.promotionRecallThreshold;
|
|
320
328
|
if (threshold === undefined)
|
|
321
329
|
return;
|
|
@@ -423,13 +431,12 @@ export class RuntimeRunner {
|
|
|
423
431
|
const { kind: _govKind, ...governance } = governancePolicyToKernelEvent(governancePolicy);
|
|
424
432
|
const config = { governance };
|
|
425
433
|
if (this.opts.contextPolicy) {
|
|
426
|
-
config.context_policy =
|
|
434
|
+
config.context_policy = normalizeContextPolicy(contextPolicy(this.opts.contextPolicy));
|
|
427
435
|
}
|
|
428
436
|
if (this.opts.kernelReliability) {
|
|
429
437
|
config.reliability = kernelReliabilityToKernel(this.opts.kernelReliability);
|
|
430
438
|
}
|
|
431
439
|
config.signal_policy = {
|
|
432
|
-
version: 1,
|
|
433
440
|
queue_max: signalPolicy.queueMax,
|
|
434
441
|
...(signalPolicy.ttlMs !== undefined ? { ttl_ms: signalPolicy.ttlMs } : {}),
|
|
435
442
|
...(signalPolicy.deadlineEscalation !== undefined
|
|
@@ -484,11 +491,6 @@ export class RuntimeRunner {
|
|
|
484
491
|
if (this.opts.criteriaGate !== undefined) {
|
|
485
492
|
config.criteria_gate = this.opts.criteriaGate;
|
|
486
493
|
}
|
|
487
|
-
// P1: fail-closed dispatch selector (absent ⇒ kernel default "exposed"). "registered" is the
|
|
488
|
-
// escape hatch back to permissive dispatch; the kernel rejects any other value.
|
|
489
|
-
if (this.opts.toolDispatchGate !== undefined) {
|
|
490
|
-
config.tool_dispatch_gate = this.opts.toolDispatchGate;
|
|
491
|
-
}
|
|
492
494
|
// K2: knowledge budget ratio (absent ⇒ kernel default 0.25; 0 disables).
|
|
493
495
|
if (this.opts.knowledgeBudgetRatio !== undefined) {
|
|
494
496
|
config.knowledge_budget_ratio = this.opts.knowledgeBudgetRatio;
|
|
@@ -517,8 +519,8 @@ export class RuntimeRunner {
|
|
|
517
519
|
async mirrorMemoryLifecycle(obs) {
|
|
518
520
|
if (obs.kind === "memory_recalled" && obs.recalls?.length) {
|
|
519
521
|
const agentId = this.opts.agentId;
|
|
520
|
-
if (agentId && this.opts.
|
|
521
|
-
await this.opts.
|
|
522
|
+
if (agentId && this.opts.memoryStore?.recordRecall) {
|
|
523
|
+
await this.opts.memoryStore.recordRecall(agentId, obs.recalls);
|
|
522
524
|
}
|
|
523
525
|
}
|
|
524
526
|
if (obs.kind === "promotion_suggested" && obs.record_id) {
|
|
@@ -642,20 +644,15 @@ export class RuntimeRunner {
|
|
|
642
644
|
const winnerId = winner === "right" ? node.judge_match.right : node.judge_match.left;
|
|
643
645
|
return withSignal(result, { tournamentWinner: winnerId });
|
|
644
646
|
}
|
|
645
|
-
//
|
|
647
|
+
// loop-control loop iteration: run the increment under the armed pacing trap (workflowNodeToSpec set
|
|
646
648
|
// `loopRound`, and the iteration resumes the loop's stable session — transcript-as-carry).
|
|
647
|
-
//
|
|
648
|
-
//
|
|
649
|
-
// when no pace decision arrives (stub orchestrators, harness children), where no signal still
|
|
650
|
-
// means "run to max_iters" (v1).
|
|
649
|
+
// The kernel-adjudicated `pace` verb is the only continuation signal. Ending without a pace
|
|
650
|
+
// decision completes the loop.
|
|
651
651
|
if (node.loop_max_iters != null) {
|
|
652
652
|
const iteration = Number(/-i(\d+)$/.exec(node.agent_id)?.[1] ?? "0");
|
|
653
653
|
const result = await orchestrator.run(mkCtx(`${baseSpec.goal}\n\n${loopInstruction(node.loop_max_iters, iteration)}`));
|
|
654
654
|
const pace = result.result.paceDecision;
|
|
655
|
-
|
|
656
|
-
return withSignal(result, { loopContinue: pace.action !== "stop" });
|
|
657
|
-
const cont = extractLoopContinue(textOf(result));
|
|
658
|
-
return cont === undefined ? result : withSignal(result, { loopContinue: cont });
|
|
655
|
+
return withSignal(result, { loopContinue: pace?.action === "continue" });
|
|
659
656
|
}
|
|
660
657
|
// A#2 classify: run the classifier, then extract the chosen branch label; the kernel runs that
|
|
661
658
|
// branch and prunes the rest. No recognizable choice ⇒ leave unset (kernel prunes all branches).
|
|
@@ -1298,6 +1295,7 @@ export class RuntimeRunner {
|
|
|
1298
1295
|
output: `permission denied: ${denyReason}`,
|
|
1299
1296
|
is_error: true,
|
|
1300
1297
|
error_kind: "governance_denied",
|
|
1298
|
+
content: { blocks: [{ type: "text", text: `permission denied: ${denyReason}` }] },
|
|
1301
1299
|
}],
|
|
1302
1300
|
});
|
|
1303
1301
|
}
|
|
@@ -1317,7 +1315,16 @@ export class RuntimeRunner {
|
|
|
1317
1315
|
}
|
|
1318
1316
|
const ext = { ...this.opts.extensions, ...(extensions ?? {}) };
|
|
1319
1317
|
const providerState = this.opts.provider.createRunState?.();
|
|
1318
|
+
// Structured blocks are intentionally operation-local. The durable kernel wire carries the
|
|
1319
|
+
// text projection; a wake (same or new Runner) therefore has identical semantics.
|
|
1320
|
+
const toolOutputOverlay = new Map();
|
|
1320
1321
|
let nextCompressedArchiveStart = nextArchivedSeqStart(priorEvents);
|
|
1322
|
+
const recordedMeasurements = new Map();
|
|
1323
|
+
for (const entry of priorEvents ?? []) {
|
|
1324
|
+
if (entry.event.kind === "prompt_measured") {
|
|
1325
|
+
recordedMeasurements.set(entry.event.measurement.requestFingerprint, entry.event.measurement);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1321
1328
|
const providerPolicy = this.opts.provider.runtimePolicy?.() ?? {};
|
|
1322
1329
|
const effectiveMaxTurns = this.opts.maxTurns ?? providerPolicy.maxTurns ?? 25;
|
|
1323
1330
|
const effectiveTimeoutMs = this.opts.timeoutMs ?? providerPolicy.timeoutMs;
|
|
@@ -1393,7 +1400,7 @@ export class RuntimeRunner {
|
|
|
1393
1400
|
tool_ids: this.opts.stableCoreToolIds,
|
|
1394
1401
|
});
|
|
1395
1402
|
}
|
|
1396
|
-
if (this.opts.
|
|
1403
|
+
if (this.opts.memoryStore && this.opts.agentId) {
|
|
1397
1404
|
await this.commitKernelApply(runtime, this.pendingObservations, { kind: "set_memory_enabled", enabled: true });
|
|
1398
1405
|
}
|
|
1399
1406
|
// Install optional memory policy. Maps the ergonomic camelCase option onto the kernel's
|
|
@@ -1439,18 +1446,15 @@ export class RuntimeRunner {
|
|
|
1439
1446
|
const startTask = { goal, criteria };
|
|
1440
1447
|
let startRunSpec;
|
|
1441
1448
|
// P0-A: lower an explicit `runSpec`, the `allowedToolIds` ceiling, and/or the `baselineToolIds`
|
|
1442
|
-
// pre-activation surface to the kernel run spec. Each
|
|
1443
|
-
//
|
|
1444
|
-
// run_spec wire — no new ABI). Unset on all ⇒ no run_spec ⇒ no gating (铁律: no config = old
|
|
1445
|
-
// behavior).
|
|
1449
|
+
// pre-activation surface to the kernel run spec. Each run has a run spec; an omitted baseline
|
|
1450
|
+
// is the canonical minimal surface.
|
|
1446
1451
|
const allowedToolIds = this.opts.allowedToolIds;
|
|
1447
1452
|
const hasProfile = allowedToolIds !== undefined && allowedToolIds.length > 0;
|
|
1448
1453
|
// NOT the `length > 0` idiom above: `baselineToolIds: []` is the legitimate minimal surface
|
|
1449
1454
|
// (meta + stable-core only), so mere presence triggers the lowering.
|
|
1450
|
-
const baselineToolIds = this.opts.baselineToolIds;
|
|
1451
|
-
const hasBaseline = baselineToolIds !== undefined;
|
|
1455
|
+
const baselineToolIds = this.opts.baselineToolIds ?? this.opts.runSpec?.exposureBaseline ?? [];
|
|
1452
1456
|
const hasMilestoneContract = this.opts.milestoneContract !== undefined;
|
|
1453
|
-
|
|
1457
|
+
{
|
|
1454
1458
|
const baseSpec = this.opts.runSpec ?? {
|
|
1455
1459
|
identity: { agentId: this.opts.agentId ?? "root", sessionId, isSubAgent: false },
|
|
1456
1460
|
role: "custom",
|
|
@@ -1459,8 +1463,7 @@ export class RuntimeRunner {
|
|
|
1459
1463
|
let spec = hasProfile
|
|
1460
1464
|
? { ...baseSpec, capabilityFilter: { ...baseSpec.capabilityFilter, allowedIds: allowedToolIds } }
|
|
1461
1465
|
: baseSpec;
|
|
1462
|
-
|
|
1463
|
-
spec = { ...spec, exposureBaseline: baselineToolIds };
|
|
1466
|
+
spec = { ...spec, exposureBaseline: baselineToolIds };
|
|
1464
1467
|
if (hasMilestoneContract && !spec.verificationContractId) {
|
|
1465
1468
|
spec = { ...spec, verificationContractId: "node-default" };
|
|
1466
1469
|
}
|
|
@@ -1507,7 +1510,7 @@ export class RuntimeRunner {
|
|
|
1507
1510
|
// I4/T5: pre-fetch memory before root start so the model sees it on turn 1 instead of
|
|
1508
1511
|
// discovering it via the `memory` tool later. Accepted hits enter `initial_context.messages`
|
|
1509
1512
|
// and are therefore frozen into the canonical start record. Skipped on restore (the checkpoint
|
|
1510
|
-
// or journal already owns them) and when
|
|
1513
|
+
// or journal already owns them) and when memoryStore/agentId is absent.
|
|
1511
1514
|
// P0-C: the skill loaded and in effect going into the current turn (updated when the model's
|
|
1512
1515
|
// `skill` tool call resolves). Drives the per-turn `activeSkill` metric → dwell measurement.
|
|
1513
1516
|
let activeSkill;
|
|
@@ -1552,7 +1555,7 @@ export class RuntimeRunner {
|
|
|
1552
1555
|
// model sees them — the model can't plan a call it doesn't know about, so the rollback
|
|
1553
1556
|
// overhead disappears. The list of denied names is appended to systemKnowledge so the
|
|
1554
1557
|
// model knows not to plan around them.
|
|
1555
|
-
let context = action.context;
|
|
1558
|
+
let context = attachToolOutputOverlay(action.context, toolOutputOverlay);
|
|
1556
1559
|
let tools = action.tools;
|
|
1557
1560
|
if (this.opts.governancePolicy && this.opts.governancePolicy.surfaceDeniedInSystem !== false) {
|
|
1558
1561
|
const { allowed, denied } = governanceFilterSchema(tools, this.opts.governancePolicy);
|
|
@@ -1572,8 +1575,57 @@ export class RuntimeRunner {
|
|
|
1572
1575
|
let turnOutputTokens = 0;
|
|
1573
1576
|
let turnCacheReadTokens = 0;
|
|
1574
1577
|
let turnCacheCreationTokens = 0;
|
|
1578
|
+
let turnCacheTelemetryStatus = "unavailable";
|
|
1579
|
+
let turnCacheTelemetrySource;
|
|
1575
1580
|
let turnCacheReadBySlot;
|
|
1576
1581
|
let turnStopReason;
|
|
1582
|
+
const providerPlan = createProviderRequestPlanForProvider(this.opts.provider, context, tools, ext);
|
|
1583
|
+
const recorded = measurementForPlan(providerPlan, recordedMeasurements.get(providerPlan.fingerprint));
|
|
1584
|
+
let promptMeasurement = recorded;
|
|
1585
|
+
if (!promptMeasurement && !context.budgetOverflow) {
|
|
1586
|
+
try {
|
|
1587
|
+
const count = this.opts.provider.countTokens
|
|
1588
|
+
? await withTimeout(this.opts.provider.countTokens(context, tools, Object.keys(ext).length ? ext : undefined), 5_000)
|
|
1589
|
+
: undefined;
|
|
1590
|
+
promptMeasurement = recordPromptMeasurement(providerPlan, count ?? {
|
|
1591
|
+
inputTokens: estimateProviderPromptTokens(context, tools),
|
|
1592
|
+
source: { kind: "heuristic" },
|
|
1593
|
+
confidence: "low_confidence",
|
|
1594
|
+
});
|
|
1595
|
+
}
|
|
1596
|
+
catch {
|
|
1597
|
+
promptMeasurement = recordPromptMeasurement(providerPlan, {
|
|
1598
|
+
inputTokens: estimateProviderPromptTokens(context, tools),
|
|
1599
|
+
source: { kind: "heuristic" },
|
|
1600
|
+
confidence: "low_confidence",
|
|
1601
|
+
});
|
|
1602
|
+
}
|
|
1603
|
+
recordedMeasurements.set(providerPlan.fingerprint, promptMeasurement);
|
|
1604
|
+
await this.opts.sessionLog.append(sessionId, {
|
|
1605
|
+
kind: "prompt_measured",
|
|
1606
|
+
turn: runtime.turn(),
|
|
1607
|
+
measurement: promptMeasurement,
|
|
1608
|
+
});
|
|
1609
|
+
}
|
|
1610
|
+
const reservedPromptTokens = (this.opts.promptBudget?.promptOverheadTokens ?? 0)
|
|
1611
|
+
+ (this.opts.promptBudget?.outputReserveTokens ?? 0)
|
|
1612
|
+
+ (this.opts.promptBudget?.safetyMarginTokens ?? 0);
|
|
1613
|
+
// Heuristic and non-native counts are advisory: their purpose is to choose a better
|
|
1614
|
+
// compaction path, not to turn a coarse byte estimate into a false context-overflow
|
|
1615
|
+
// terminal. A trustworthy native/local-exact measurement can block the provider call.
|
|
1616
|
+
const measuredOverflow = promptMeasurement
|
|
1617
|
+
&& promptMeasurement.source.kind !== "heuristic"
|
|
1618
|
+
&& promptMeasurement.inputTokens + reservedPromptTokens > this.opts.maxTokens;
|
|
1619
|
+
if (context.budgetOverflow || measuredOverflow) {
|
|
1620
|
+
action = await this.commitKernelAction(runtime, this.pendingObservations, {
|
|
1621
|
+
kind: "provider_error",
|
|
1622
|
+
effect_id: providerEffectId,
|
|
1623
|
+
message: "provider-visible prompt exceeds the configured context budget",
|
|
1624
|
+
error_kind: "context_overflow",
|
|
1625
|
+
retryable: false,
|
|
1626
|
+
});
|
|
1627
|
+
continue;
|
|
1628
|
+
}
|
|
1577
1629
|
const abortSignal = this.abortController?.signal;
|
|
1578
1630
|
try {
|
|
1579
1631
|
for await (const evt of this.opts.provider.stream(context, tools, Object.keys(ext).length ? ext : undefined, providerState, abortSignal)) {
|
|
@@ -1590,9 +1642,36 @@ export class RuntimeRunner {
|
|
|
1590
1642
|
// P0-C: capture the prompt-cache split for the tool-gating hit-rate baseline.
|
|
1591
1643
|
turnCacheReadTokens = usageEvt.cacheReadInputTokens ?? 0;
|
|
1592
1644
|
turnCacheCreationTokens = usageEvt.cacheCreationInputTokens ?? 0;
|
|
1645
|
+
turnCacheTelemetryStatus = usageEvt.cacheTelemetryStatus
|
|
1646
|
+
?? usageEvt.providerUsage?.cacheTelemetryStatus
|
|
1647
|
+
?? ((usageEvt.cacheReadInputTokens ?? 0) > 0 || (usageEvt.cacheCreationInputTokens ?? 0) > 0
|
|
1648
|
+
? "measured"
|
|
1649
|
+
: "unavailable");
|
|
1650
|
+
turnCacheTelemetrySource = usageEvt.cacheTelemetrySource ?? usageEvt.providerUsage?.cacheTelemetrySource;
|
|
1593
1651
|
// I1: per-slot attribution forwarded into TurnMetrics. Undefined when the provider
|
|
1594
1652
|
// doesn't honor cache_control (OpenAI-family auto-cache).
|
|
1595
1653
|
turnCacheReadBySlot = usageEvt.cacheReadInputTokensBySlot;
|
|
1654
|
+
// spc_024-06: postflight observed input is the authority (INV-024-08). Feed it back
|
|
1655
|
+
// as a durable measurement fact for this exact request fingerprint so replay reuses
|
|
1656
|
+
// the observed truth instead of re-counting (or trusting the preflight estimate).
|
|
1657
|
+
if (turnInputTokens > 0) {
|
|
1658
|
+
const postflight = recordPromptMeasurement(providerPlan, {
|
|
1659
|
+
inputTokens: turnInputTokens,
|
|
1660
|
+
source: { kind: "postflight" },
|
|
1661
|
+
confidence: "exact",
|
|
1662
|
+
});
|
|
1663
|
+
recordedMeasurements.set(providerPlan.fingerprint, postflight);
|
|
1664
|
+
await this.opts.sessionLog.append(sessionId, {
|
|
1665
|
+
kind: "prompt_measured",
|
|
1666
|
+
turn: runtime.turn(),
|
|
1667
|
+
measurement: {
|
|
1668
|
+
requestFingerprint: postflight.requestFingerprint,
|
|
1669
|
+
inputTokens: postflight.inputTokens,
|
|
1670
|
+
source: postflight.source,
|
|
1671
|
+
confidence: postflight.confidence,
|
|
1672
|
+
},
|
|
1673
|
+
});
|
|
1674
|
+
}
|
|
1596
1675
|
// Phase 4: stop_reason drives the kernel's max-output-tokens recovery. The closing
|
|
1597
1676
|
// usage frame carries it; keep the last non-empty value seen this turn.
|
|
1598
1677
|
if (usageEvt.stopReason)
|
|
@@ -1615,6 +1694,9 @@ export class RuntimeRunner {
|
|
|
1615
1694
|
this.cancellationReason ??= "user";
|
|
1616
1695
|
}
|
|
1617
1696
|
else {
|
|
1697
|
+
const provider = this.opts.provider.descriptor?.().provider ?? "unknown";
|
|
1698
|
+
const providerError = classifyProviderError(provider, err);
|
|
1699
|
+
const message = providerError.message;
|
|
1618
1700
|
// Reactive recovery is now a kernel decision. Forward the raw provider error and
|
|
1619
1701
|
// dispatch whatever the kernel returns: `call_provider` to retry with a freshly
|
|
1620
1702
|
// compacted context, or `done` to terminate with an honest `ContextOverflow`. The
|
|
@@ -1625,14 +1707,15 @@ export class RuntimeRunner {
|
|
|
1625
1707
|
action = await this.commitKernelAction(runtime, this.pendingObservations, {
|
|
1626
1708
|
kind: "provider_error",
|
|
1627
1709
|
effect_id: providerEffectId,
|
|
1628
|
-
message
|
|
1710
|
+
message,
|
|
1711
|
+
...providerErrorEventFields(providerError),
|
|
1629
1712
|
});
|
|
1630
1713
|
// Withholding (query.ts parity): surface the raw provider error only when the kernel
|
|
1631
1714
|
// could NOT recover (it returned a terminal). On a recovered retry (`call_provider`)
|
|
1632
1715
|
// the error stays hidden, so embedders that terminate on `error` events don't see a
|
|
1633
1716
|
// phantom failure mid-recovery.
|
|
1634
1717
|
if (action.kind === "done") {
|
|
1635
|
-
yield { type: "error", message
|
|
1718
|
+
yield { type: "error", message };
|
|
1636
1719
|
}
|
|
1637
1720
|
continue;
|
|
1638
1721
|
}
|
|
@@ -1728,6 +1811,10 @@ export class RuntimeRunner {
|
|
|
1728
1811
|
inputTokens: turnInputTokens,
|
|
1729
1812
|
cacheReadTokens: turnCacheReadTokens,
|
|
1730
1813
|
cacheCreationTokens: turnCacheCreationTokens,
|
|
1814
|
+
cacheTelemetryStatus: turnCacheTelemetryStatus,
|
|
1815
|
+
...(turnCacheTelemetrySource ? { cacheTelemetrySource: turnCacheTelemetrySource } : {}),
|
|
1816
|
+
requestFingerprint: providerPlan.fingerprint,
|
|
1817
|
+
stablePrefixFingerprint: providerPlan.stablePrefixFingerprint,
|
|
1731
1818
|
...(turnCacheReadBySlot ? { cacheReadTokensBySlot: turnCacheReadBySlot } : {}),
|
|
1732
1819
|
});
|
|
1733
1820
|
}
|
|
@@ -1918,7 +2005,7 @@ export class RuntimeRunner {
|
|
|
1918
2005
|
agentId: this.opts.agentId,
|
|
1919
2006
|
memoryScope: this.opts.memoryScope,
|
|
1920
2007
|
skillDir: this.opts.skillDir,
|
|
1921
|
-
|
|
2008
|
+
memoryStore: this.opts.memoryStore,
|
|
1922
2009
|
knowledgeSource: this.opts.knowledgeSource,
|
|
1923
2010
|
onToolSuspend: this.opts.onToolSuspend,
|
|
1924
2011
|
onPermissionRequest: this.opts.onPermissionRequest,
|
|
@@ -1944,8 +2031,7 @@ export class RuntimeRunner {
|
|
|
1944
2031
|
}
|
|
1945
2032
|
// O5 (PreToolUse-hook analog): give the host a STATEFUL veto over each kernel-approved
|
|
1946
2033
|
// call. A blocked call never executes; its reason reaches the model as a committed
|
|
1947
|
-
// governance-denied tool result. Decision failures are closed
|
|
1948
|
-
// unless the host explicitly marks this hook advisory with `onToolCallFailure: "open"`.
|
|
2034
|
+
// governance-denied tool result. Decision failures are closed.
|
|
1949
2035
|
let executableCalls = normalCalls;
|
|
1950
2036
|
if (this.opts.onToolCall) {
|
|
1951
2037
|
const allowed = [];
|
|
@@ -1955,9 +2041,7 @@ export class RuntimeRunner {
|
|
|
1955
2041
|
decision = await this.opts.onToolCall({ callId: call.id, name: call.name, arguments: call.arguments });
|
|
1956
2042
|
}
|
|
1957
2043
|
catch (cause) {
|
|
1958
|
-
decision =
|
|
1959
|
-
? undefined
|
|
1960
|
-
: { block: true, reason: `onToolCall hook failed: ${formatToolError(cause)}` };
|
|
2044
|
+
decision = { block: true, reason: `onToolCall hook failed: ${formatToolError(cause)}` };
|
|
1961
2045
|
}
|
|
1962
2046
|
if (decision?.block) {
|
|
1963
2047
|
const reason = decision.reason ?? "blocked by host onToolCall hook";
|
|
@@ -1979,12 +2063,17 @@ export class RuntimeRunner {
|
|
|
1979
2063
|
yield evt;
|
|
1980
2064
|
if (evt.type === "tool_result") {
|
|
1981
2065
|
const tre = evt;
|
|
2066
|
+
if (tre.contentParts !== undefined) {
|
|
2067
|
+
const canonical = normalizeToolResultContent(tre.callId, tre.content, tre.isError, tre.contentParts);
|
|
2068
|
+
toolOutputOverlay.set(tre.callId, canonical.blocks);
|
|
2069
|
+
}
|
|
1982
2070
|
toolResults.push({
|
|
1983
2071
|
callId: tre.callId,
|
|
1984
2072
|
output: tre.content,
|
|
1985
2073
|
isError: tre.isError,
|
|
1986
2074
|
isFatal: tre.isFatal,
|
|
1987
2075
|
errorKind: tre.errorKind,
|
|
2076
|
+
...(tre.contentParts?.length ? { contentParts: tre.contentParts } : {}),
|
|
1988
2077
|
});
|
|
1989
2078
|
}
|
|
1990
2079
|
else if (evt.type === "tool_argument_repaired") {
|
|
@@ -2069,6 +2158,7 @@ export class RuntimeRunner {
|
|
|
2069
2158
|
output: r.output,
|
|
2070
2159
|
is_error: r.isError,
|
|
2071
2160
|
token_count: r.tokenCount,
|
|
2161
|
+
content: { blocks: toolOutputBlocksToDurable(r.contentParts?.length ? r.contentParts : [{ type: "text", text: r.output }]) },
|
|
2072
2162
|
})),
|
|
2073
2163
|
});
|
|
2074
2164
|
// The canonical provider resolution already activates a successfully resolved `skill` call.
|
|
@@ -2256,7 +2346,7 @@ export class RuntimeRunner {
|
|
|
2256
2346
|
});
|
|
2257
2347
|
this.activeGroupBudgetScope = undefined;
|
|
2258
2348
|
}
|
|
2259
|
-
if (this.opts.
|
|
2349
|
+
if (this.opts.memoryStore && this.opts.agentId) {
|
|
2260
2350
|
const newMsgs = runtime.drainNewMessages().map(m => ({
|
|
2261
2351
|
role: m.role,
|
|
2262
2352
|
content: m.content,
|
|
@@ -2274,9 +2364,9 @@ export class RuntimeRunner {
|
|
|
2274
2364
|
createdAtMs: sessionStart,
|
|
2275
2365
|
updatedAtMs: Date.now(),
|
|
2276
2366
|
};
|
|
2277
|
-
await this.opts.
|
|
2367
|
+
await this.opts.memoryStore.saveSession(completedSession);
|
|
2278
2368
|
if (this.opts.memoryScope) {
|
|
2279
|
-
const extracted = await extractSessionMemories(this.opts.
|
|
2369
|
+
const extracted = await extractSessionMemories(this.opts.memoryProvider ?? this.opts.provider, completedSession, this.opts.memoryScope, this.opts.memorySystemPrompt);
|
|
2280
2370
|
for (const memory of extracted) {
|
|
2281
2371
|
await this.writeMemory(memory, { sessionId, agentId: this.opts.agentId });
|
|
2282
2372
|
}
|
|
@@ -2313,7 +2403,7 @@ export class RuntimeRunner {
|
|
|
2313
2403
|
* after each sprint renewal (`phase: "renewal"`): renewal drops the old history INCLUDING the
|
|
2314
2404
|
* earlier memory hits, so the new sprint gets a fresh recall pass. Errs-open throughout. */
|
|
2315
2405
|
async prefetchMemoryIntoHistory(runtime, phase) {
|
|
2316
|
-
if (!this.opts.
|
|
2406
|
+
if (!this.opts.memoryStore || !this.opts.agentId || !this.opts.memoryScope)
|
|
2317
2407
|
return undefined;
|
|
2318
2408
|
// P10: recall is default-on (CC session-start recall) — with no hook configured,
|
|
2319
2409
|
// the goal itself is the query. preQueryMemory stays as the targeting override.
|
|
@@ -2351,7 +2441,7 @@ export class RuntimeRunner {
|
|
|
2351
2441
|
return undefined;
|
|
2352
2442
|
}
|
|
2353
2443
|
async prefetchMemoryIntoInitialContext(runtime) {
|
|
2354
|
-
if (!this.opts.
|
|
2444
|
+
if (!this.opts.memoryStore || !this.opts.agentId || !this.opts.memoryScope)
|
|
2355
2445
|
return;
|
|
2356
2446
|
const preQuery = this.opts.preQueryMemory
|
|
2357
2447
|
?? ((ctx) => [{
|
|
@@ -2440,11 +2530,11 @@ export class RuntimeRunner {
|
|
|
2440
2530
|
return nextArchiveStart;
|
|
2441
2531
|
}
|
|
2442
2532
|
async archiveSemanticPageOut(archived, action, sessionId) {
|
|
2443
|
-
if (!this.opts.
|
|
2533
|
+
if (!this.opts.memoryStore || !this.opts.agentId || !this.opts.memoryScope)
|
|
2444
2534
|
return;
|
|
2445
|
-
const summary = this.opts.
|
|
2446
|
-
? await this.opts.
|
|
2447
|
-
: await summarizeForLongTermMemory(this.opts.
|
|
2535
|
+
const summary = this.opts.memorySummarizer
|
|
2536
|
+
? await this.opts.memorySummarizer.summarize(archived, { action })
|
|
2537
|
+
: await summarizeForLongTermMemory(this.opts.memoryProvider ?? this.opts.provider, archived, this.opts.memorySystemPrompt);
|
|
2448
2538
|
// P2 write-funnel: route through the ONE gated WriteMemory syscall so validation,
|
|
2449
2539
|
// the rolling write quota, dedup, and the memory_written audit all apply. Score is
|
|
2450
2540
|
// advisory (0.6) — an automatic summary must never outrank curated content.
|
|
@@ -2666,11 +2756,16 @@ export function replayMessages(events, maxBytes) {
|
|
|
2666
2756
|
}
|
|
2667
2757
|
else if (e.kind === "tool_completed") {
|
|
2668
2758
|
for (const r of e.results) {
|
|
2759
|
+
const durable = decodeDurableToolResult({
|
|
2760
|
+
call_id: r.call_id,
|
|
2761
|
+
is_error: r.is_error ?? false,
|
|
2762
|
+
blocks: decodeDurableContent(r.content).blocks,
|
|
2763
|
+
});
|
|
2669
2764
|
messages.push({
|
|
2670
2765
|
role: "tool",
|
|
2671
2766
|
content: "",
|
|
2672
2767
|
toolCalls: [],
|
|
2673
|
-
contentParts: [{ type: "tool_result", callId:
|
|
2768
|
+
contentParts: [{ type: "tool_result", callId: durable.call_id, output: sanitizeReplayText(r.output, maxBytes), isError: durable.is_error, ...(durable.blocks.length ? { contentParts: durableBlocksToToolOutput(durable.blocks) } : {}) }],
|
|
2674
2769
|
tokenCount: r.token_count,
|
|
2675
2770
|
});
|
|
2676
2771
|
}
|
|
@@ -2764,11 +2859,16 @@ export async function replayMessagesAsync(events, maxBytes, loadArchive) {
|
|
|
2764
2859
|
}
|
|
2765
2860
|
else if (e.kind === "tool_completed") {
|
|
2766
2861
|
for (const r of e.results) {
|
|
2862
|
+
const durable = decodeDurableToolResult({
|
|
2863
|
+
call_id: r.call_id,
|
|
2864
|
+
is_error: r.is_error ?? false,
|
|
2865
|
+
blocks: decodeDurableContent(r.content).blocks,
|
|
2866
|
+
});
|
|
2767
2867
|
messages.push({
|
|
2768
2868
|
role: "tool",
|
|
2769
2869
|
content: "",
|
|
2770
2870
|
toolCalls: [],
|
|
2771
|
-
contentParts: [{ type: "tool_result", callId:
|
|
2871
|
+
contentParts: [{ type: "tool_result", callId: durable.call_id, output: sanitizeReplayText(r.output, maxBytes), isError: durable.is_error, ...(durable.blocks.length ? { contentParts: durableBlocksToToolOutput(durable.blocks) } : {}) }],
|
|
2772
2872
|
tokenCount: r.token_count,
|
|
2773
2873
|
});
|
|
2774
2874
|
}
|
|
@@ -2790,6 +2890,21 @@ function nextArchivedSeqStart(events) {
|
|
|
2790
2890
|
}
|
|
2791
2891
|
return next;
|
|
2792
2892
|
}
|
|
2893
|
+
async function withTimeout(promise, timeoutMs) {
|
|
2894
|
+
let timer;
|
|
2895
|
+
try {
|
|
2896
|
+
return await Promise.race([
|
|
2897
|
+
promise,
|
|
2898
|
+
new Promise((_, reject) => {
|
|
2899
|
+
timer = setTimeout(() => reject(new Error("provider token measurement timed out")), timeoutMs);
|
|
2900
|
+
}),
|
|
2901
|
+
]);
|
|
2902
|
+
}
|
|
2903
|
+
finally {
|
|
2904
|
+
if (timer !== undefined)
|
|
2905
|
+
clearTimeout(timer);
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
2793
2908
|
/** Collect all text_delta events from a run into a single string. */
|
|
2794
2909
|
export async function collectText(stream) {
|
|
2795
2910
|
let text = "";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { KernelPrimitive } from "./kernel-event-log.js";
|
|
2
2
|
import type { ContentPart, ProviderReplay, ToolCall, ToolErrorKind } from "../types.js";
|
|
3
|
+
import type { RecordedPromptMeasurement } from "../providers/request-plan.js";
|
|
3
4
|
import type { MemoryRecall, MemoryScope } from "../memory/protocols.js";
|
|
4
5
|
import type { KernelJournal } from "./kernel-journal.js";
|
|
5
6
|
export type RollbackReason = {
|
|
@@ -36,6 +37,10 @@ export type SessionEvent = {
|
|
|
36
37
|
token_count?: number;
|
|
37
38
|
tool_calls: ToolCall[];
|
|
38
39
|
provider_replay?: ProviderReplay;
|
|
40
|
+
} | {
|
|
41
|
+
kind: "prompt_measured";
|
|
42
|
+
turn: number;
|
|
43
|
+
measurement: RecordedPromptMeasurement;
|
|
39
44
|
} | {
|
|
40
45
|
kind: "tool_requested";
|
|
41
46
|
turn: number;
|
|
@@ -50,6 +55,9 @@ export type SessionEvent = {
|
|
|
50
55
|
is_fatal?: boolean;
|
|
51
56
|
error_kind?: ToolErrorKind;
|
|
52
57
|
token_count?: number;
|
|
58
|
+
content: {
|
|
59
|
+
blocks: Record<string, unknown>[];
|
|
60
|
+
};
|
|
53
61
|
}>;
|
|
54
62
|
} | {
|
|
55
63
|
kind: "tool_argument_repaired";
|
|
@@ -177,7 +185,6 @@ export type SessionEvent = {
|
|
|
177
185
|
kind: "entropy_sample";
|
|
178
186
|
turn: number;
|
|
179
187
|
score: number;
|
|
180
|
-
score_version: number;
|
|
181
188
|
rho: number;
|
|
182
189
|
repeat_pressure: number;
|
|
183
190
|
failure_rate: number;
|
|
@@ -5,6 +5,7 @@ import { createInterface } from "node:readline";
|
|
|
5
5
|
import { primitiveForKind } from "./kernel-event-log.js";
|
|
6
6
|
import { KeyedSerialExecutor } from "./reliability.js";
|
|
7
7
|
import { FileKernelJournal, InMemoryKernelJournal } from "./kernel-journal.js";
|
|
8
|
+
import { decodeDurableContent } from "./durable-content.js";
|
|
8
9
|
/**
|
|
9
10
|
* **Single-process dev/test implementation** of both capabilities (spec §9.4: one class may
|
|
10
11
|
* implement several capabilities; the *interfaces* stay separate). Its `KernelJournal` half is
|
|
@@ -84,7 +85,7 @@ export class FileSessionLog {
|
|
|
84
85
|
async read(sessionId, fromSeq = 0, primitiveFilter) {
|
|
85
86
|
const results = [];
|
|
86
87
|
for (const record of await this.readRecords(sessionId)) {
|
|
87
|
-
if (
|
|
88
|
+
if (record.seq < fromSeq)
|
|
88
89
|
continue;
|
|
89
90
|
if (primitiveFilter && primitiveForKind(record.event.kind) !== primitiveFilter)
|
|
90
91
|
continue;
|
|
@@ -133,7 +134,7 @@ export class FileSessionLog {
|
|
|
133
134
|
});
|
|
134
135
|
for await (const line of rl) {
|
|
135
136
|
if (line.trim())
|
|
136
|
-
records.push(JSON.parse(line));
|
|
137
|
+
records.push(decodePersistedSessionRecord(JSON.parse(line)));
|
|
137
138
|
}
|
|
138
139
|
}
|
|
139
140
|
catch (err) {
|
|
@@ -143,3 +144,42 @@ export class FileSessionLog {
|
|
|
143
144
|
return records;
|
|
144
145
|
}
|
|
145
146
|
}
|
|
147
|
+
function decodePersistedSessionRecord(value) {
|
|
148
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
149
|
+
throw new Error("session record must be an object");
|
|
150
|
+
const record = value;
|
|
151
|
+
if (Object.keys(record).some(key => key !== "seq" && key !== "event"))
|
|
152
|
+
throw new Error("session record has unknown fields");
|
|
153
|
+
if (!Number.isInteger(record.seq) || record.seq < 0)
|
|
154
|
+
throw new Error("session record seq must be a non-negative integer");
|
|
155
|
+
if (!record.event || typeof record.event !== "object" || Array.isArray(record.event))
|
|
156
|
+
throw new Error("session record event must be an object");
|
|
157
|
+
const event = record.event;
|
|
158
|
+
if (event.kind === "llm_completed" && event.provider_replay !== undefined) {
|
|
159
|
+
assertCanonicalProviderReplay(event.provider_replay);
|
|
160
|
+
}
|
|
161
|
+
if (event.kind === "tool_completed") {
|
|
162
|
+
if (!Array.isArray(event.results))
|
|
163
|
+
throw new Error("tool_completed results must be an array");
|
|
164
|
+
for (const result of event.results) {
|
|
165
|
+
if (!result || typeof result !== "object" || Array.isArray(result))
|
|
166
|
+
throw new Error("tool_completed result must be an object");
|
|
167
|
+
const wire = result;
|
|
168
|
+
if ("blocks" in wire)
|
|
169
|
+
throw new Error("tool_completed result has removed blocks field");
|
|
170
|
+
decodeDurableContent(wire.content);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return { seq: record.seq, event: event };
|
|
174
|
+
}
|
|
175
|
+
const REPLAY_KEYS = new Set(["protocol", "provider", "model", "native_blocks", "reasoning_content", "reasoning_details", "native_message", "tool_calls"]);
|
|
176
|
+
function assertCanonicalProviderReplay(value) {
|
|
177
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
178
|
+
throw new Error("provider replay must be an object");
|
|
179
|
+
const replay = value;
|
|
180
|
+
for (const key of Object.keys(replay))
|
|
181
|
+
if (!REPLAY_KEYS.has(key))
|
|
182
|
+
throw new Error(`provider replay has unknown field ${key}`);
|
|
183
|
+
if (typeof replay.protocol !== "string" || replay.protocol.length === 0)
|
|
184
|
+
throw new Error("provider replay protocol is required");
|
|
185
|
+
}
|