@deepstrike/wasm 0.2.76 → 0.2.78

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.
Files changed (62) hide show
  1. package/dist/agent.d.ts +6 -2
  2. package/dist/agent.js +59 -19
  3. package/dist/index.d.ts +7 -9
  4. package/dist/index.js +3 -5
  5. package/dist/memory/attestation.d.ts +38 -0
  6. package/dist/memory/attestation.js +43 -0
  7. package/dist/memory/authority.d.ts +2 -0
  8. package/dist/memory/authority.js +3 -1
  9. package/dist/memory/candidate-sink.d.ts +66 -0
  10. package/dist/memory/candidate-sink.js +82 -0
  11. package/dist/memory/durable.d.ts +4 -2
  12. package/dist/memory/durable.js +2 -0
  13. package/dist/memory/index.d.ts +9 -1
  14. package/dist/memory/index.js +5 -0
  15. package/dist/memory/lifecycle.d.ts +82 -0
  16. package/dist/memory/lifecycle.js +46 -0
  17. package/dist/memory/recall-projection.d.ts +5 -0
  18. package/dist/memory/recall-projection.js +18 -0
  19. package/dist/memory/runtime-policy.d.ts +15 -0
  20. package/dist/memory/runtime-policy.js +26 -0
  21. package/dist/memory/writer.d.ts +22 -0
  22. package/dist/memory/writer.js +41 -0
  23. package/dist/providers/anthropic-compaction.d.ts +84 -0
  24. package/dist/providers/anthropic-compaction.js +222 -0
  25. package/dist/providers/anthropic.d.ts +41 -1
  26. package/dist/providers/anthropic.js +187 -18
  27. package/dist/providers/base.js +11 -1
  28. package/dist/runtime/canonical-kernel-step.d.ts +1 -2
  29. package/dist/runtime/canonical-kernel-step.js +74 -8
  30. package/dist/runtime/execution-plane.js +1 -1
  31. package/dist/runtime/index.d.ts +0 -2
  32. package/dist/runtime/index.js +0 -1
  33. package/dist/runtime/kernel-event-log.d.ts +8 -0
  34. package/dist/runtime/kernel-event-log.js +61 -1
  35. package/dist/runtime/kernel-step.d.ts +13 -2
  36. package/dist/runtime/os-snapshot.js +1 -0
  37. package/dist/runtime/runner.d.ts +40 -25
  38. package/dist/runtime/runner.js +196 -240
  39. package/dist/runtime/session-event-registry.generated.d.ts +212 -0
  40. package/dist/runtime/session-event-registry.generated.js +112 -0
  41. package/dist/runtime/session-log.d.ts +69 -16
  42. package/dist/runtime/session-log.js +10 -58
  43. package/dist/runtime/session-repair.d.ts +0 -3
  44. package/dist/runtime/session-repair.js +0 -3
  45. package/dist/runtime/sub-agent-orchestrator.d.ts +2 -0
  46. package/dist/runtime/sub-agent-orchestrator.js +2 -0
  47. package/dist/runtime/types/agent.d.ts +9 -52
  48. package/dist/runtime/types/agent.js +8 -45
  49. package/dist/types.d.ts +10 -5
  50. package/package.json +3 -3
  51. package/dist/memory/extraction.d.ts +0 -4
  52. package/dist/memory/extraction.js +0 -48
  53. package/dist/runtime/context.d.ts +0 -28
  54. package/dist/runtime/context.js +0 -16
  55. package/dist/runtime/evolution.d.ts +0 -189
  56. package/dist/runtime/evolution.js +0 -25
  57. package/dist/runtime/reducers.d.ts +0 -15
  58. package/dist/runtime/reducers.js +0 -57
  59. package/dist/runtime/verifiable-report.d.ts +0 -59
  60. package/dist/runtime/verifiable-report.js +0 -64
  61. package/dist/runtime/workflow-control-flow.d.ts +0 -21
  62. package/dist/runtime/workflow-control-flow.js +0 -83
@@ -1,13 +1,14 @@
1
1
  import { prepareProviderRequest } from "../providers/prepared-request.js";
2
- import { createNativeContextPreparationAdapter } from "./context.js";
3
- import { sha256Hex } from "./sha256.js";
4
- import { extractSessionMemories } from "../memory/extraction.js";
2
+ import { resolveMemoryRuntimePolicy } from "../memory/runtime-policy.js";
3
+ import { memoryCandidateId } from "../memory/candidate-sink.js";
5
4
  import { resolvePermissionRequest } from "./execution-plane.js";
6
5
  import { governancePolicyToKernelEvent } from "../governance.js";
7
6
  import { parseToolCallArguments } from "./tool-arguments.js";
8
7
  import { stripSkillFrontmatter } from "./execution-plane.js";
9
8
  import { getKernel } from "./kernel.js";
10
9
  import { checkMemoryWrite, deriveMemoryRecall } from "../memory/authority.js";
10
+ import { memoryFetchRecall } from "../memory/recall-projection.js";
11
+ import { MemoryWriter } from "../memory/writer.js";
11
12
  import { CanonicalRunnerRuntime, canonicalKernelAction, canonicalKernelApply, isInvalidInputError, canonicalKernelMaybeAction, canonicalStartAgent, canonicalStartWorkflow, sha256 as canonicalSha256, } from "./canonical-kernel-step.js";
12
13
  import { peekProviderReplay, seedProviderReplayFromEvents } from "./provider-replay.js";
13
14
  import { sanitizeReplayText } from "./replay-sanitize.js";
@@ -18,8 +19,6 @@ import { messageToKernelMessage, archivePresentationFromObservations, skillMetad
18
19
  import { agentRunSpecToKernel, MILESTONE_UNVERIFIED_REASON, milestoneCheckFail, milestoneCheckPass, milestoneCheckResultToKernel, subAgentResultToKernel, workflowBudgetNote, workflowNodeSpecToKernel, workflowNodeOutcomeFromKernel, workflowNodeStatusFromTermination, workflowNodeToManifest, workflowNodeToSpec, workflowSpecToKernel, } from "./types/agent.js";
19
20
  import { defaultSubAgentOrchestrator } from "./sub-agent-orchestrator.js";
20
21
  import { extractJsonValue, schemaInstruction, schemaRetryInstruction, validateAgainstSchema, } from "./output-schema.js";
21
- import { resolveReducer } from "./reducers.js";
22
- import { loopInstruction, classifyInstruction, judgeGoal, dependencyOutputsNote, extractClassifyBranch, extractJudgeWinner, } from "./workflow-control-flow.js";
23
22
  import { kernelObservationToSessionEvent } from "./kernel-event-log.js";
24
23
  import { assertNativeProfile } from "./os-profile.js";
25
24
  import { classifyProviderError, providerErrorEventFields } from "../providers/provider-error.js";
@@ -107,8 +106,19 @@ function controlRequestRejection(observations, operation) {
107
106
  reason: typeof rejected.reason === "string" ? rejected.reason : "request denied",
108
107
  };
109
108
  }
109
+ /** The kernel's wire form of the tiers a provider performs natively, or nothing when it performs
110
+ * none — an operation that delegates nothing configures exactly as before. */
111
+ export function delegatedContextTiers(native) {
112
+ if (!native?.toolResultClearing && !native?.compaction)
113
+ return undefined;
114
+ return {
115
+ ...(native.toolResultClearing ? { tool_result_clearing: true } : {}),
116
+ ...(native.compaction ? { compaction: true } : {}),
117
+ };
118
+ }
110
119
  export class RuntimeRunner {
111
120
  opts;
121
+ memoryRuntimeGates;
112
122
  interrupted = false;
113
123
  cancellationReason;
114
124
  /** #2-B-ii: aborts the in-flight provider stream on interrupt/preempt. Recreated per `execute`. */
@@ -147,6 +157,10 @@ export class RuntimeRunner {
147
157
  activeProviderInvocationId;
148
158
  constructor(opts) {
149
159
  this.opts = opts;
160
+ this.memoryRuntimeGates = resolveMemoryRuntimePolicy(opts.memoryRuntimePolicy);
161
+ if (this.memoryPathToCandidateSink("modelWrites") && !opts.memoryCandidateSink) {
162
+ throw new Error('memory runtime policy modelWrites = "candidate_sink" needs RuntimeOptions.memoryCandidateSink');
163
+ }
150
164
  const schemaAttempts = opts.workflowSchemaValidationAttempts ?? 2;
151
165
  if (!Number.isInteger(schemaAttempts) || schemaAttempts < 1 || schemaAttempts > 16) {
152
166
  throw new RangeError("workflowSchemaValidationAttempts must be an integer between 1 and 16");
@@ -181,7 +195,6 @@ export class RuntimeRunner {
181
195
  maxTurns: this.opts.maxTurns,
182
196
  maxTotalTokens: this.opts.maxTotalTokens,
183
197
  maxWallMs: this.opts.timeoutMs,
184
- artifactSetDigest: this.opts.artifactSetDigest,
185
198
  memoryBindingId: `wasm-memory-${this.opts.agentId ?? "root"}`,
186
199
  persistPayload: async (_callId, content, previewBytes) => {
187
200
  const digest = canonicalSha256(content);
@@ -346,9 +359,9 @@ export class RuntimeRunner {
346
359
  * §22.13 · a host-authored memory write. The kernel decides whether it may happen: a live
347
360
  * operation answers through `admit_memory_write` (the model's validation rule and rolling write
348
361
  * quota); with no live operation the same kernel rule answers statelessly. Resolves `true` when
349
- * the record was persisted.
362
+ * the record was persisted or submitted as a pending candidate.
350
363
  */
351
- async writeMemory(memory, sessionId) {
364
+ async writeMemory(memory, sessionId, candidate) {
352
365
  if (!this.opts.memoryStore || !this.opts.agentId)
353
366
  return false;
354
367
  const turn = this.activeKernel?.turn() ?? 0;
@@ -361,6 +374,12 @@ export class RuntimeRunner {
361
374
  }
362
375
  return false;
363
376
  }
377
+ if (candidate) {
378
+ const receipt = await this.memoryWriter().propose(this.opts.agentId, { candidate_id: candidate.candidateId, source: candidate.source, record: memory });
379
+ if (sessionId)
380
+ await this.opts.sessionLog.append(sessionId, this.memoryCandidateEvent(receipt, candidate.source, memory));
381
+ return receipt.status !== "rejected";
382
+ }
364
383
  try {
365
384
  await this.opts.memoryStore.put(this.opts.agentId, memory);
366
385
  }
@@ -376,6 +395,114 @@ export class RuntimeRunner {
376
395
  }
377
396
  return true;
378
397
  }
398
+ /**
399
+ * M4 · a host-initiated recall (an explicit `recall`). Same path as the prefetch: the retrieval
400
+ * gate, the policy's `retrievalTopK`, the kernel-derived recall lifecycle, and a
401
+ * `memory_retrieval_result` audit.
402
+ */
403
+ async queryMemory(query, sessionId) {
404
+ if (!this.opts.memoryStore || !this.opts.agentId)
405
+ return [];
406
+ if (!this.memoryPathEnabled("retrievalEnabled"))
407
+ return [];
408
+ const policyLimit = this.opts.memoryPolicy?.retrievalTopK;
409
+ const limit = policyLimit === undefined ? query.top_k : Math.min(query.top_k, policyLimit);
410
+ if (limit <= 0)
411
+ return [];
412
+ const hits = (await this.opts.memoryStore.search(this.opts.agentId, { ...query, top_k: limit })).slice(0, limit);
413
+ await this.recordHostMemoryRecall(hits);
414
+ if (sessionId)
415
+ await this.opts.sessionLog.append(sessionId, { kind: "memory_retrieval_result", hits });
416
+ return hits;
417
+ }
418
+ async persistMemoryEffectResult(operationId, effectId, canonicalMemory) {
419
+ const agentId = this.opts.agentId;
420
+ const record = {
421
+ record_id: `memory:${crypto.randomUUID()}`,
422
+ scope: this.opts.memoryScope ?? { tenant_id: "default", namespace: agentId ?? "default" },
423
+ name: String(canonicalMemory.name ?? ""),
424
+ kind: String(canonicalMemory.kind ?? "reference"),
425
+ content: String(canonicalMemory.content ?? ""),
426
+ description: String(canonicalMemory.description ?? ""),
427
+ provenance: {
428
+ author: "model",
429
+ trust: "untrusted",
430
+ evidence_refs: Array.isArray(canonicalMemory.evidence_refs)
431
+ ? canonicalMemory.evidence_refs.map(String)
432
+ : [],
433
+ },
434
+ created_at: Number(canonicalMemory.accepted_at_ms ?? Date.now()),
435
+ updated_at: Number(canonicalMemory.accepted_at_ms ?? Date.now()),
436
+ recall_count: 0,
437
+ confidence: 1,
438
+ links: [],
439
+ pinned: false,
440
+ };
441
+ if (this.memoryPathToCandidateSink("modelWrites")) {
442
+ try {
443
+ if (!agentId)
444
+ throw new Error("memory persistence requires RuntimeOptions.agentId");
445
+ const modelProposal = Boolean(canonicalMemory.causation && typeof canonicalMemory.causation === "object" && canonicalMemory.causation.kind === "provider_tool");
446
+ const source = modelProposal ? "model_proposal" : "child_request";
447
+ // Child requests keep their v2 identity so in-flight redeliveries still match the sink.
448
+ const candidateId = modelProposal
449
+ ? await memoryCandidateId(["v3", agentId, "model_proposal", operationId, effectId, "0"])
450
+ : await memoryCandidateId(["v2", agentId, "child_request", operationId, effectId, "0"]);
451
+ const receipt = await this.memoryWriter().propose(agentId, { candidate_id: candidateId, source, record });
452
+ return { kind: "memory_candidate_result", effect_id: effectId, ...receipt };
453
+ }
454
+ catch (cause) {
455
+ return { kind: "memory_candidate_result", effect_id: effectId, error: formatToolError(cause) };
456
+ }
457
+ }
458
+ try {
459
+ if (!this.memoryPathEnabled("modelWritesEnabled")) {
460
+ throw new Error("model memory writes are disabled by memoryRuntimePolicy.modelWrites");
461
+ }
462
+ if (!agentId)
463
+ throw new Error("memory persistence requires RuntimeOptions.agentId");
464
+ // The receipt names what the store now holds: the record's id and its content digest.
465
+ const receipt = await this.memoryWriter().persist(agentId, record);
466
+ return { kind: "memory_persist_result", effect_id: effectId, ...receipt };
467
+ }
468
+ catch (cause) {
469
+ return { kind: "memory_persist_result", effect_id: effectId, error: formatToolError(cause) };
470
+ }
471
+ }
472
+ /** The host's side of every memory write: the store, or the candidate sink's receipt contract. */
473
+ memoryWriter() {
474
+ return new MemoryWriter({ store: this.opts.memoryStore, sink: this.opts.memoryCandidateSink });
475
+ }
476
+ /** The audit fact for a sink's answer. Only an accepted candidate is a written record. */
477
+ memoryCandidateEvent(receipt, source, memory) {
478
+ const turn = this.activeKernel?.turn() ?? 0;
479
+ switch (receipt.status) {
480
+ case "pending":
481
+ return {
482
+ kind: "memory_candidate_pending", turn, candidate_id: receipt.candidate_id, source,
483
+ name: memory.name, receipt_id: receipt.receipt_id,
484
+ };
485
+ case "rejected":
486
+ return {
487
+ kind: "memory_candidate_rejected", turn, candidate_id: receipt.candidate_id, source,
488
+ name: memory.name, receipt_id: receipt.receipt_id, reason_code: receipt.reason_code,
489
+ };
490
+ case "accepted":
491
+ return {
492
+ kind: "memory_written", turn, record_id: receipt.record_ref, scope: memory.scope,
493
+ memory_kind: memory.kind, name: memory.name, size_bytes: new TextEncoder().encode(memory.content).byteLength,
494
+ };
495
+ }
496
+ }
497
+ /** Host memory-policy gate: a path is on unless the host set its switch to `"disabled"`. */
498
+ memoryPathEnabled(path) {
499
+ return this.memoryRuntimeGates?.[path] ?? true;
500
+ }
501
+ /** M2 · whether a write path hands its writes to the candidate sink. Off unless the host set the
502
+ * path to `"candidate_sink"` — unlike the on/off gates, the default here is the store. */
503
+ memoryPathToCandidateSink(path) {
504
+ return this.memoryRuntimeGates?.[`${path}ToCandidateSink`] ?? false;
505
+ }
379
506
  async admitMemoryWrite(memory) {
380
507
  const runtime = this.activeKernel;
381
508
  if (runtime && runtime.hasStarted() && !runtime.isTerminal()) {
@@ -624,6 +751,13 @@ export class RuntimeRunner {
624
751
  tokens: Math.max(1, Math.ceil(this.opts.systemPrompt.length / 4)),
625
752
  });
626
753
  }
754
+ for (const [agentId, content] of Object.entries(this.opts.dependencyOutputs ?? {})) {
755
+ await this.commitKernelApply(runtime, this.pendingObservations, {
756
+ kind: "add_dependency_output",
757
+ agent_id: agentId,
758
+ content,
759
+ });
760
+ }
627
761
  if (this.opts.initialMemory) {
628
762
  for (const mem of this.opts.initialMemory) {
629
763
  await this.commitKernelApply(runtime, this.pendingObservations, {
@@ -652,7 +786,7 @@ export class RuntimeRunner {
652
786
  });
653
787
  }
654
788
  if (this.opts.memoryStore && this.opts.agentId) {
655
- await this.commitKernelApply(runtime, this.pendingObservations, { kind: "set_memory_enabled", enabled: true });
789
+ await this.commitKernelApply(runtime, this.pendingObservations, { kind: "set_memory_enabled", enabled: true, read: this.memoryPathEnabled("retrievalEnabled"), write: this.memoryPathEnabled("modelWritesEnabled"), propose_memory: Boolean(this.opts.memoryCandidateSink && this.memoryPathToCandidateSink("modelWrites")), recall: Boolean(this.opts.memoryScope) });
656
790
  }
657
791
  if (this.opts.knowledgeSource) {
658
792
  await this.commitKernelApply(runtime, this.pendingObservations, { kind: "set_knowledge_enabled", enabled: true });
@@ -748,14 +882,9 @@ export class RuntimeRunner {
748
882
  }
749
883
  if (!resumeMidRun)
750
884
  await this.applyKernelPolicies(runtime);
751
- // I4: pre-fetch memory before the first LLM turn (mirrors Node). Strict dynamic context
752
- // control: single-use retrieval content, not a stable skill — lands in `history` like an
753
- // ordinary `memory` tool result, so it decays with the compression pyramid instead of
754
- // pinning itself in `knowledge` forever.
885
+ // I4: pre-fetch memory before the first LLM turn (mirrors Node). Each hit is an unpinned keyed
886
+ // `memory:<record_id>` knowledge entry, evicted at a boundary when usage needs the room.
755
887
  this.currentGoal = goal;
756
- if (!resumeMidRun) {
757
- await this.prefetchMemoryIntoHistory(runtime, "initial");
758
- }
759
888
  let action = resumeMidRun
760
889
  ? runtime.resumeAction() ?? (() => { throw new Error("restored canonical operation has no pending effect or terminal"); })()
761
890
  : await canonicalStartAgent(runtime, this.pendingObservations, startTask, startRunSpec);
@@ -885,18 +1014,6 @@ export class RuntimeRunner {
885
1014
  this.providerRetryPending = action.kind === "call_provider";
886
1015
  continue;
887
1016
  }
888
- if (!promptMeasurement)
889
- throw new Error("context preparation requires prompt measurement");
890
- const contextAdapter = await createNativeContextPreparationAdapter();
891
- const preparation = contextAdapter.prepare({
892
- effect: action.contextEffect,
893
- request_fingerprint: providerPlan.fingerprint,
894
- provider_route: { ...this.providerRoute, request_fingerprint_scope: preparedRequest.scope },
895
- prompt_measurement: promptMeasurement,
896
- });
897
- await this.opts.sessionLog.append(sessionId, {
898
- kind: "context_prepared", turn: runtime.turn(), effect_id: providerEffectId, preparation,
899
- });
900
1017
  const abortSignal = this.abortController?.signal;
901
1018
  try {
902
1019
  for await (const evt of preparedRequest.stream(abortSignal)) {
@@ -1122,56 +1239,28 @@ export class RuntimeRunner {
1122
1239
  });
1123
1240
  }
1124
1241
  else if (action.kind === "persist_memory") {
1125
- // The kernel hands over the validated canonical write, which carries no store identity:
1126
- // build the full record here, and let the receipt name exactly what the store now holds.
1127
- const canonicalMemory = action.memory;
1128
- const acceptedAt = Number(canonicalMemory.accepted_at_ms ?? Date.now());
1129
- const record = {
1130
- record_id: `memory:${crypto.randomUUID()}`,
1131
- scope: this.opts.memoryScope ?? { tenant_id: "default", namespace: this.opts.agentId ?? "default" },
1132
- name: String(canonicalMemory.name ?? ""),
1133
- kind: String(canonicalMemory.kind ?? "reference"),
1134
- content: String(canonicalMemory.content ?? ""),
1135
- description: String(canonicalMemory.description ?? ""),
1136
- provenance: {
1137
- author: "model",
1138
- trust: "untrusted",
1139
- evidence_refs: Array.isArray(canonicalMemory.evidence_refs) ? canonicalMemory.evidence_refs.map(String) : [],
1140
- },
1141
- created_at: acceptedAt,
1142
- updated_at: acceptedAt,
1143
- recall_count: 0,
1144
- confidence: 1,
1145
- links: [],
1146
- pinned: false,
1147
- };
1148
- let error;
1149
- try {
1150
- if (!this.opts.memoryStore || !this.opts.agentId)
1151
- throw new Error("WASM memory persistence requires memoryStore and agentId");
1152
- await this.opts.memoryStore.put(this.opts.agentId, record);
1153
- }
1154
- catch (cause) {
1155
- error = formatToolError(cause);
1156
- }
1157
- action = await this.commitKernelAction(runtime, this.pendingObservations, {
1158
- kind: "memory_persist_result",
1159
- effect_id: action.effectId,
1160
- ...(error ? { error } : {
1161
- record_ref: record.record_id,
1162
- digest: `sha256:${sha256Hex(record.content)}`,
1163
- }),
1164
- });
1242
+ action = await this.commitKernelAction(runtime, this.pendingObservations, await this.persistMemoryEffectResult(runtime.operationId, action.effectId, action.memory));
1165
1243
  }
1166
1244
  else if (action.kind === "query_memory") {
1167
1245
  let hits = [];
1168
1246
  let error;
1169
1247
  try {
1248
+ if (!this.memoryPathEnabled("retrievalEnabled"))
1249
+ throw new Error("memory retrieval is disabled by memoryRuntimePolicy.retrieval");
1170
1250
  if (!this.opts.memoryStore || !this.opts.agentId)
1171
1251
  throw new Error("WASM memory queries require memoryStore and agentId");
1172
- hits = await this.opts.memoryStore.search(this.opts.agentId, {
1173
- ...action.query, top_k: action.requestedK,
1174
- });
1252
+ const recordId = typeof action.query.record_id === "string" ? action.query.record_id : undefined;
1253
+ const causation = action.query.causation;
1254
+ hits = causation?.kind === "kernel_recall"
1255
+ ? await this.kernelRecallHits(String(action.query.text ?? ""), action.requestedK, causation.sprint ? "renewal" : "initial")
1256
+ : recordId === undefined
1257
+ ? await this.opts.memoryStore.search(this.opts.agentId, {
1258
+ ...action.query, top_k: action.requestedK,
1259
+ })
1260
+ : memoryFetchRecall(await this.opts.memoryStore.get(this.opts.agentId, recordId), {
1261
+ scope: this.opts.memoryScope ?? { tenant_id: "default", namespace: this.opts.agentId },
1262
+ kinds: Array.isArray(action.query.kinds) ? action.query.kinds.map(String) : [],
1263
+ }, Date.now());
1175
1264
  }
1176
1265
  catch (cause) {
1177
1266
  error = formatToolError(cause);
@@ -1183,6 +1272,9 @@ export class RuntimeRunner {
1183
1272
  hits,
1184
1273
  ...(error ? { error } : {}),
1185
1274
  });
1275
+ if (!error && hits.length > 0 && this.currentSessionId) {
1276
+ await this.opts.sessionLog.append(this.currentSessionId, { kind: "memory_retrieval_result", hits });
1277
+ }
1186
1278
  // M3/M4: mirror the kernel's journaled recall lifecycle + surface promotion suggestions.
1187
1279
  for (const obs of this.pendingObservations.slice(obsStart)) {
1188
1280
  if (obs.kind === "memory_recalled" && obs.recalls?.length && this.opts.agentId) {
@@ -1196,7 +1288,7 @@ export class RuntimeRunner {
1196
1288
  else if (action.kind === "archive_page_out") {
1197
1289
  const archiveMeta = this.activePageOutArchive
1198
1290
  ?? this.pendingPageOutArchives.shift()
1199
- ?? { archiveStart: this.nextArchiveStart, compressedSeq: await this.opts.sessionLog.latestSeq(sessionId) };
1291
+ ?? { archiveStart: this.nextArchiveStart, compressedSeq: await this.opts.sessionLog.latestSeq(sessionId), action: action.action, tier: action.tier };
1200
1292
  this.activePageOutArchive = archiveMeta;
1201
1293
  let archiveRef;
1202
1294
  let error;
@@ -1212,9 +1304,6 @@ export class RuntimeRunner {
1212
1304
  catch (cause) {
1213
1305
  error = formatToolError(cause);
1214
1306
  }
1215
- const archived = action.archived;
1216
- const archiveAction = archiveMeta.action ?? "auto_compact";
1217
- const archiveTier = archiveMeta.tier;
1218
1307
  if (!error)
1219
1308
  this.activePageOutArchive = undefined;
1220
1309
  action = await this.commitKernelAction(runtime, this.pendingObservations, {
@@ -1223,9 +1312,6 @@ export class RuntimeRunner {
1223
1312
  ...(archiveRef ? { archive_ref: archiveRef } : {}),
1224
1313
  ...(error ? { error } : {}),
1225
1314
  });
1226
- if (!error && archiveTier === "semantic" && archived.length > 0) {
1227
- void this.archiveSemanticPageOut(archived, archiveAction);
1228
- }
1229
1315
  }
1230
1316
  else if (action.kind === "load_payload") {
1231
1317
  const content = await this.payloadStore().loadPayload(sessionId, action.payloadRef);
@@ -1552,11 +1638,6 @@ export class RuntimeRunner {
1552
1638
  updatedAtMs: Date.now(),
1553
1639
  };
1554
1640
  await this.opts.memoryStore.saveSession(completedSession);
1555
- if (this.opts.memoryScope) {
1556
- const extracted = await extractSessionMemories(this.opts.memoryProvider ?? this.opts.provider, completedSession, this.opts.memoryScope, this.opts.memorySystemPrompt);
1557
- for (const memory of extracted)
1558
- await this.writeMemory(memory, sessionId);
1559
- }
1560
1641
  }
1561
1642
  catch { /* non-fatal */ }
1562
1643
  }
@@ -1579,24 +1660,20 @@ export class RuntimeRunner {
1579
1660
  * fed back on mismatch. If it still does not conform, the node is failed with the validation
1580
1661
  * reason (an `Error`-terminated result fails the node in-kernel, starving its dependents).
1581
1662
  */
1582
- async runWorkflowNode(node, parentSessionId, orchestrator, budget, outputs, abortSignal) {
1583
- // G2: a reduce node runs no LLM — execute the registered pure function over its dependency
1584
- // outputs and feed the result back as an ordinary completion. Deterministic; no agent burned.
1585
- if (node.reducer) {
1586
- return this.runReduceNode(node, outputs ?? new Map());
1587
- }
1663
+ async runWorkflowNode(node, parentSessionId, orchestrator, budget, abortSignal) {
1588
1664
  const baseSpec = workflowNodeToSpec(node, parentSessionId);
1589
1665
  const manifest = workflowNodeToManifest(node, parentSessionId);
1590
1666
  // G4: surface remaining workflow budget so a coordinator node can size its submission.
1591
1667
  const budgetNote = workflowBudgetNote(budget);
1592
- // W-N2: a DAG edge carries data — every dependent node sees its dependencies' outputs (the
1593
- // kernel sends `input_agent_ids` for all dependents; judges/reduce keep their special paths).
1594
- const depsNote = dependencyOutputsNote(node.input_agent_ids, outputs);
1595
- const withBudget = (goal) => [goal, depsNote, budgetNote].filter(Boolean).join("\n\n");
1668
+ // A DAG edge carries data: the kernel composes the dependency section of the brief.
1669
+ const withBudget = (goal) => [goal, node.dependency_brief, budgetNote].filter(Boolean).join("\n\n");
1596
1670
  const mkCtx = (goal) => ({
1597
1671
  parentOpts: this.opts,
1598
1672
  parentSessionId,
1599
1673
  spec: { ...baseSpec, goal: withBudget(goal) },
1674
+ ...(node.dependency_outputs
1675
+ ? { dependencyOutputs: node.dependency_outputs }
1676
+ : {}),
1600
1677
  manifest,
1601
1678
  sessionLog: this.opts.sessionLog,
1602
1679
  // This child is a workflow node, so capability resolution applies workflow-node quarantine
@@ -1609,39 +1686,6 @@ export class RuntimeRunner {
1609
1686
  // #2-B-ii: the per-node abort signal the driver fires when the kernel preempts this node.
1610
1687
  ...(abortSignal ? { abortSignal } : {}),
1611
1688
  });
1612
- const textOf = (r) => {
1613
- const c = r.result.finalMessage?.content;
1614
- return typeof c === "string" ? c : c != null ? JSON.stringify(c) : "";
1615
- };
1616
- const withSignal = (r, patch) => ({ ...r, result: { ...r.result, ...patch } });
1617
- // A#2 tournament judge: compare two entrants' produced outputs rather than running the node's own
1618
- // goal. Look up both candidates, judge over the controller's criterion, and report the winner's id.
1619
- if (node.judge_match) {
1620
- const out = outputs ?? new Map();
1621
- const left = out.get(node.judge_match.left) ?? "";
1622
- const right = out.get(node.judge_match.right) ?? "";
1623
- const result = await orchestrator.run(mkCtx(judgeGoal(baseSpec.goal, left, right)));
1624
- const winner = extractJudgeWinner(textOf(result));
1625
- const winnerId = winner === "right" ? node.judge_match.right : node.judge_match.left;
1626
- return withSignal(result, { tournamentWinner: winnerId });
1627
- }
1628
- // loop-control loop iteration: run the increment under the armed pacing trap (workflowNodeToSpec set
1629
- // `loopRound`, and the iteration resumes the loop's stable session — transcript-as-carry).
1630
- // The kernel-adjudicated `pace` verb is the only continuation signal. Ending without a pace
1631
- // decision completes the loop.
1632
- if (node.loop_max_iters != null) {
1633
- const iteration = Number(/-i(\d+)$/.exec(node.agent_id)?.[1] ?? "0");
1634
- const result = await orchestrator.run(mkCtx(`${baseSpec.goal}\n\n${loopInstruction(node.loop_max_iters, iteration)}`));
1635
- const pace = result.result.paceDecision;
1636
- return withSignal(result, { loopContinue: pace?.action === "continue" });
1637
- }
1638
- // A#2 classify: run the classifier, then extract the chosen branch label (kernel prunes the rest).
1639
- if (node.classify_labels && node.classify_labels.length) {
1640
- const labels = node.classify_labels;
1641
- const result = await orchestrator.run(mkCtx(`${baseSpec.goal}\n\n${classifyInstruction(labels)}`));
1642
- const branch = extractClassifyBranch(textOf(result), labels);
1643
- return branch === undefined ? result : withSignal(result, { classifyBranch: branch });
1644
- }
1645
1689
  const schema = node.output_schema;
1646
1690
  if (!schema)
1647
1691
  return orchestrator.run(mkCtx(baseSpec.goal));
@@ -1672,27 +1716,6 @@ export class RuntimeRunner {
1672
1716
  },
1673
1717
  };
1674
1718
  }
1675
- /**
1676
- * G2: execute a deterministic reduce node — run the named reducer (built-ins overlaid with
1677
- * `opts.reducers`) over its dependency outputs and return a synthetic completion. No LLM, zero
1678
- * tokens. An unknown reducer or a thrown reducer fails the node (`Error` → starves dependents).
1679
- */
1680
- runReduceNode(node, outputs) {
1681
- const ok = (content, termination) => ({
1682
- agentId: node.agent_id,
1683
- result: { termination, finalMessage: { role: "assistant", content, toolCalls: [] }, turnsUsed: 0, totalTokensUsed: 0 },
1684
- });
1685
- const reducer = resolveReducer(node.reducer, this.opts.reducers);
1686
- if (!reducer)
1687
- return ok(`unknown reducer "${node.reducer}"`, "error");
1688
- const inputs = (node.input_agent_ids ?? []).map(agentId => ({ agentId, output: outputs.get(agentId) ?? "" }));
1689
- try {
1690
- return ok(reducer(inputs), "completed");
1691
- }
1692
- catch (err) {
1693
- return ok(`reducer "${node.reducer}" threw: ${formatToolError(err)}`, "error");
1694
- }
1695
- }
1696
1719
  /**
1697
1720
  * W0-ABI: run a declarative workflow DAG. The kernel owns the DAG and gates every node spawn
1698
1721
  * through the syscall trap; this driver runs each kernel-emitted batch of nodes in parallel,
@@ -1715,6 +1738,10 @@ export class RuntimeRunner {
1715
1738
  if (this.opts.contextPolicy) {
1716
1739
  config.context_policy = normalizeContextPolicy(contextPolicy(this.opts.contextPolicy));
1717
1740
  }
1741
+ const delegated = delegatedContextTiers(this.opts.provider?.descriptor?.().nativeContext);
1742
+ if (delegated) {
1743
+ config.context_policy = { ...config.context_policy, delegated_tiers: delegated };
1744
+ }
1718
1745
  config.signal_policy = {
1719
1746
  queue_max: signalPolicy.queueMax,
1720
1747
  ...(signalPolicy.ttlMs !== undefined ? { ttl_ms: signalPolicy.ttlMs } : {}),
@@ -1971,7 +1998,7 @@ export class RuntimeRunner {
1971
1998
  }
1972
1999
  const controller = new AbortController();
1973
2000
  controllers.set(node.agent_id, controller);
1974
- const running = this.runWorkflowNode(node, parentSessionId, orchestrator, budget, outputs, controller.signal)
2001
+ const running = this.runWorkflowNode(node, parentSessionId, orchestrator, budget, controller.signal)
1975
2002
  .then(result => ({ kind: "node", node, result }))
1976
2003
  .finally(() => batchState.finished.add(node.agent_id));
1977
2004
  // A node can fail while the driver is busy feeding another completion; the race observes
@@ -2004,15 +2031,10 @@ export class RuntimeRunner {
2004
2031
  if (batchState.stopping)
2005
2032
  return { spawned: [], done: undefined };
2006
2033
  const { node, result } = landed;
2007
- // G2: record this node's output so a downstream reduce node can consume it.
2034
+ // Record this node's output so its dependents can consume it.
2008
2035
  const outContent = result.result.finalMessage?.content;
2009
2036
  const outText = typeof outContent === "string" ? outContent : outContent != null ? JSON.stringify(outContent) : "";
2010
2037
  outputs.set(result.agentId, outText);
2011
- // A loop iteration completes under `wf-node{N}-i{k}` but its dependents consume the STABLE
2012
- // node id `wf-node{N}` — alias it so the LAST iteration's output is what dependents see.
2013
- const stableId = result.agentId.replace(/-i\d+$/, "");
2014
- if (stableId !== result.agentId)
2015
- outputs.set(stableId, outText);
2016
2038
  const observationStart = this.pendingObservations.length;
2017
2039
  const completionAction = await this.commitKernelMaybeAction(runtime, this.pendingObservations, {
2018
2040
  kind: "sub_agent_completed",
@@ -2049,17 +2071,13 @@ export class RuntimeRunner {
2049
2071
  }));
2050
2072
  }
2051
2073
  }
2052
- // Persist node completion for resume recovery. W-1: the result-borne control signals ride
2053
- // along (a resumed classifier re-prunes; a recorded loop stop is honored) plus the output
2054
- // text (post-resume dependents/reduce still see this node's output).
2074
+ // Persist node completion for resume recovery, with the output text so post-resume
2075
+ // dependents still see this node's output.
2055
2076
  await this.opts.sessionLog.append(parentSessionId, buildWorkflowNodeCompletedEvent({
2056
2077
  turn: runtime.turn(),
2057
2078
  agentId: result.agentId,
2058
2079
  status: workflowNodeStatusFromTermination(result.result.termination),
2059
2080
  termination: result.result.termination,
2060
- classifyBranch: result.result.classifyBranch,
2061
- tournamentWinner: result.result.tournamentWinner,
2062
- loopContinue: result.result.loopContinue,
2063
2081
  ...(result.result.finalMessage ? { output: result.result.finalMessage } : {}),
2064
2082
  }));
2065
2083
  return { spawned, done: findDone(obs) };
@@ -2112,93 +2130,31 @@ export class RuntimeRunner {
2112
2130
  }
2113
2131
  nextArchiveStart = compressedSeq + 1;
2114
2132
  }
2115
- // K4: a sprint renewal dropped the old history — including any earlier memory hits — so
2116
- // re-run the preQueryMemory prefetch for the new sprint (live observations only).
2117
- if (obs.kind === "renewed") {
2118
- await this.prefetchMemoryIntoHistory(runtime, "renewal");
2119
- }
2120
2133
  }
2121
2134
  return nextArchiveStart;
2122
2135
  }
2123
- /** I4 + K4: fetch long-term memory hits for the current goal and land them in `history` as an
2124
- * ordinary user turn — single-use retrieval content that decays with the compression pyramid,
2125
- * never pinned into `knowledge`. `phase: "initial"` = once before turn 1; `phase: "renewal"` =
2126
- * re-fired after each sprint renewal (renewal drops the old history INCLUDING earlier memory
2127
- * hits). Errs-open throughout. */
2128
- async prefetchMemoryIntoHistory(runtime, phase) {
2129
- if (!this.opts.memoryStore || !this.opts.agentId || !this.opts.memoryScope)
2130
- return;
2131
- // P10: recall is default-on (CC session-start recall) — with no hook configured,
2132
- // the goal itself is the query. preQueryMemory stays as the targeting override.
2133
- const preQuery = this.opts.preQueryMemory
2134
- ?? ((ctx) => [{ scope: this.opts.memoryScope, query: ctx.goal, top_k: 5, kinds: [] }]);
2135
- try {
2136
- const queries = await preQuery({ goal: this.currentGoal, phase });
2137
- const lines = [];
2138
- const seen = new Set();
2139
- const recalled = [];
2140
- for (const q of queries ?? []) {
2141
- if (!q.query.trim())
2136
+ /** The kernel's own recall for the run goal (plan task 2.1). `preQueryMemory` may rewrite it
2137
+ * into several scoped queries; their hits are merged, one per record. */
2138
+ async kernelRecallHits(goal, requestedK, phase) {
2139
+ const scope = this.opts.memoryScope;
2140
+ const queries = this.opts.preQueryMemory
2141
+ ? await this.opts.preQueryMemory({ goal, phase }) ?? []
2142
+ : [{ scope, query: goal, top_k: requestedK, kinds: [] }];
2143
+ const seen = new Set();
2144
+ const hits = [];
2145
+ for (const query of queries) {
2146
+ if (!query.query.trim())
2147
+ continue;
2148
+ const limit = Math.min(query.top_k ?? requestedK, requestedK);
2149
+ for (const hit of (await this.opts.memoryStore.search(this.opts.agentId, { ...query, top_k: limit })).slice(0, limit)) {
2150
+ if (seen.has(hit.record.record_id))
2142
2151
  continue;
2143
- const hits = await this.opts.memoryStore.search(this.opts.agentId, q);
2144
- for (const hit of hits) {
2145
- if (seen.has(hit.record.record_id))
2146
- continue;
2147
- seen.add(hit.record.record_id);
2148
- recalled.push(hit);
2149
- lines.push(`[memory record_id=${hit.record.record_id} trust=${hit.record.provenance.trust} score=${hit.score.toFixed(3)}] ${hit.record.content}`);
2150
- }
2151
- }
2152
- if (lines.length > 0) {
2153
- await this.commitKernelApply(runtime, this.pendingObservations, {
2154
- kind: "add_history_message",
2155
- message: { role: "user", content: lines.join("\n") },
2156
- });
2152
+ seen.add(hit.record.record_id);
2153
+ hits.push(hit);
2157
2154
  }
2158
- await this.recordHostMemoryRecall(recalled);
2159
2155
  }
2160
- catch { /* errs-open */ }
2161
- }
2162
- async archiveSemanticPageOut(archived, action) {
2163
- if (!this.opts.memoryStore || !this.opts.agentId || !this.opts.memoryScope)
2164
- return;
2165
- try {
2166
- const summary = this.opts.memorySummarizer
2167
- ? await this.opts.memorySummarizer.summarize(archived, { action })
2168
- : await summarizeForLongTermMemory(this.opts.memoryProvider ?? this.opts.provider, archived, this.opts.memorySystemPrompt);
2169
- const now = Date.now();
2170
- const name = `page-out-${now}`;
2171
- await this.writeMemory({
2172
- record_id: `${this.opts.memoryScope.tenant_id}:${this.opts.memoryScope.namespace}:project:${name}`,
2173
- scope: this.opts.memoryScope, name, kind: "project", content: summary,
2174
- description: `auto summary of ${action ?? "compaction"} archive`,
2175
- provenance: { author: "extraction", trust: "untrusted", evidence_refs: [] },
2176
- created_at: now, updated_at: now, recall_count: 0, confidence: 0.6, links: [], pinned: false,
2177
- }, this.currentSessionId ?? undefined);
2178
- }
2179
- catch {
2180
- // non-fatal
2181
- }
2182
- }
2183
- }
2184
- async function summarizeForLongTermMemory(provider, archived, systemPrompt) {
2185
- const transcript = archived
2186
- .map(m => `${m.role}: ${m.content}`)
2187
- .join("\n");
2188
- const context = {
2189
- systemText: [
2190
- systemPrompt,
2191
- "Summarize the following conversation for long-term memory. Preserve key facts, decisions, and open questions.",
2192
- ].filter(Boolean).join("\n\n"),
2193
- turns: [{ role: "user", content: transcript, toolCalls: [] }],
2194
- };
2195
- let text = "";
2196
- const state = provider.createRunState?.();
2197
- for await (const evt of provider.stream(context, [], undefined, state)) {
2198
- if (evt.type === "text_delta")
2199
- text += evt.delta;
2156
+ return hits;
2200
2157
  }
2201
- return text.trim() || transcript.slice(0, 2000);
2202
2158
  }
2203
2159
  function isMidRun(events) {
2204
2160
  return events.length > 0 && !events.some(e => e.event.kind === "run_terminal");