@deepstrike/wasm 0.2.77 → 0.2.79

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 +63 -1
  35. package/dist/runtime/kernel-step.d.ts +15 -2
  36. package/dist/runtime/os-snapshot.js +1 -0
  37. package/dist/runtime/runner.d.ts +44 -26
  38. package/dist/runtime/runner.js +206 -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 +83 -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 +5 -1
  46. package/dist/runtime/sub-agent-orchestrator.js +4 -0
  47. package/dist/runtime/types/agent.d.ts +18 -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,22 @@ 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
+ }
761
+ for (const entry of this.opts.inheritedKnowledge ?? []) {
762
+ await this.commitKernelApply(runtime, this.pendingObservations, {
763
+ kind: "add_knowledge_message",
764
+ content: entry.content,
765
+ ...(entry.key ? { key: entry.key } : {}),
766
+ tokens: entry.tokens ?? Math.max(1, Math.ceil(entry.content.length / 4)),
767
+ ...(entry.pinned ? { pinned: true } : {}),
768
+ });
769
+ }
627
770
  if (this.opts.initialMemory) {
628
771
  for (const mem of this.opts.initialMemory) {
629
772
  await this.commitKernelApply(runtime, this.pendingObservations, {
@@ -652,7 +795,7 @@ export class RuntimeRunner {
652
795
  });
653
796
  }
654
797
  if (this.opts.memoryStore && this.opts.agentId) {
655
- await this.commitKernelApply(runtime, this.pendingObservations, { kind: "set_memory_enabled", enabled: true });
798
+ 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
799
  }
657
800
  if (this.opts.knowledgeSource) {
658
801
  await this.commitKernelApply(runtime, this.pendingObservations, { kind: "set_knowledge_enabled", enabled: true });
@@ -748,14 +891,9 @@ export class RuntimeRunner {
748
891
  }
749
892
  if (!resumeMidRun)
750
893
  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.
894
+ // I4: pre-fetch memory before the first LLM turn (mirrors Node). Each hit is an unpinned keyed
895
+ // `memory:<record_id>` knowledge entry, evicted at a boundary when usage needs the room.
755
896
  this.currentGoal = goal;
756
- if (!resumeMidRun) {
757
- await this.prefetchMemoryIntoHistory(runtime, "initial");
758
- }
759
897
  let action = resumeMidRun
760
898
  ? runtime.resumeAction() ?? (() => { throw new Error("restored canonical operation has no pending effect or terminal"); })()
761
899
  : await canonicalStartAgent(runtime, this.pendingObservations, startTask, startRunSpec);
@@ -885,18 +1023,6 @@ export class RuntimeRunner {
885
1023
  this.providerRetryPending = action.kind === "call_provider";
886
1024
  continue;
887
1025
  }
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
1026
  const abortSignal = this.abortController?.signal;
901
1027
  try {
902
1028
  for await (const evt of preparedRequest.stream(abortSignal)) {
@@ -1122,56 +1248,28 @@ export class RuntimeRunner {
1122
1248
  });
1123
1249
  }
1124
1250
  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
- });
1251
+ action = await this.commitKernelAction(runtime, this.pendingObservations, await this.persistMemoryEffectResult(runtime.operationId, action.effectId, action.memory));
1165
1252
  }
1166
1253
  else if (action.kind === "query_memory") {
1167
1254
  let hits = [];
1168
1255
  let error;
1169
1256
  try {
1257
+ if (!this.memoryPathEnabled("retrievalEnabled"))
1258
+ throw new Error("memory retrieval is disabled by memoryRuntimePolicy.retrieval");
1170
1259
  if (!this.opts.memoryStore || !this.opts.agentId)
1171
1260
  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
- });
1261
+ const recordId = typeof action.query.record_id === "string" ? action.query.record_id : undefined;
1262
+ const causation = action.query.causation;
1263
+ hits = causation?.kind === "kernel_recall"
1264
+ ? await this.kernelRecallHits(String(action.query.text ?? ""), action.requestedK, causation.sprint ? "renewal" : "initial")
1265
+ : recordId === undefined
1266
+ ? await this.opts.memoryStore.search(this.opts.agentId, {
1267
+ ...action.query, top_k: action.requestedK,
1268
+ })
1269
+ : memoryFetchRecall(await this.opts.memoryStore.get(this.opts.agentId, recordId), {
1270
+ scope: this.opts.memoryScope ?? { tenant_id: "default", namespace: this.opts.agentId },
1271
+ kinds: Array.isArray(action.query.kinds) ? action.query.kinds.map(String) : [],
1272
+ }, Date.now());
1175
1273
  }
1176
1274
  catch (cause) {
1177
1275
  error = formatToolError(cause);
@@ -1183,6 +1281,9 @@ export class RuntimeRunner {
1183
1281
  hits,
1184
1282
  ...(error ? { error } : {}),
1185
1283
  });
1284
+ if (!error && hits.length > 0 && this.currentSessionId) {
1285
+ await this.opts.sessionLog.append(this.currentSessionId, { kind: "memory_retrieval_result", hits });
1286
+ }
1186
1287
  // M3/M4: mirror the kernel's journaled recall lifecycle + surface promotion suggestions.
1187
1288
  for (const obs of this.pendingObservations.slice(obsStart)) {
1188
1289
  if (obs.kind === "memory_recalled" && obs.recalls?.length && this.opts.agentId) {
@@ -1196,7 +1297,7 @@ export class RuntimeRunner {
1196
1297
  else if (action.kind === "archive_page_out") {
1197
1298
  const archiveMeta = this.activePageOutArchive
1198
1299
  ?? this.pendingPageOutArchives.shift()
1199
- ?? { archiveStart: this.nextArchiveStart, compressedSeq: await this.opts.sessionLog.latestSeq(sessionId) };
1300
+ ?? { archiveStart: this.nextArchiveStart, compressedSeq: await this.opts.sessionLog.latestSeq(sessionId), action: action.action, tier: action.tier };
1200
1301
  this.activePageOutArchive = archiveMeta;
1201
1302
  let archiveRef;
1202
1303
  let error;
@@ -1212,9 +1313,6 @@ export class RuntimeRunner {
1212
1313
  catch (cause) {
1213
1314
  error = formatToolError(cause);
1214
1315
  }
1215
- const archived = action.archived;
1216
- const archiveAction = archiveMeta.action ?? "auto_compact";
1217
- const archiveTier = archiveMeta.tier;
1218
1316
  if (!error)
1219
1317
  this.activePageOutArchive = undefined;
1220
1318
  action = await this.commitKernelAction(runtime, this.pendingObservations, {
@@ -1223,9 +1321,6 @@ export class RuntimeRunner {
1223
1321
  ...(archiveRef ? { archive_ref: archiveRef } : {}),
1224
1322
  ...(error ? { error } : {}),
1225
1323
  });
1226
- if (!error && archiveTier === "semantic" && archived.length > 0) {
1227
- void this.archiveSemanticPageOut(archived, archiveAction);
1228
- }
1229
1324
  }
1230
1325
  else if (action.kind === "load_payload") {
1231
1326
  const content = await this.payloadStore().loadPayload(sessionId, action.payloadRef);
@@ -1552,11 +1647,6 @@ export class RuntimeRunner {
1552
1647
  updatedAtMs: Date.now(),
1553
1648
  };
1554
1649
  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
1650
  }
1561
1651
  catch { /* non-fatal */ }
1562
1652
  }
@@ -1579,24 +1669,21 @@ export class RuntimeRunner {
1579
1669
  * fed back on mismatch. If it still does not conform, the node is failed with the validation
1580
1670
  * reason (an `Error`-terminated result fails the node in-kernel, starving its dependents).
1581
1671
  */
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
- }
1672
+ async runWorkflowNode(node, parentSessionId, orchestrator, budget, abortSignal) {
1588
1673
  const baseSpec = workflowNodeToSpec(node, parentSessionId);
1589
1674
  const manifest = workflowNodeToManifest(node, parentSessionId);
1590
1675
  // G4: surface remaining workflow budget so a coordinator node can size its submission.
1591
1676
  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");
1677
+ // A DAG edge carries data: the kernel composes the dependency section of the brief.
1678
+ const withBudget = (goal) => [goal, node.dependency_brief, budgetNote].filter(Boolean).join("\n\n");
1596
1679
  const mkCtx = (goal) => ({
1597
1680
  parentOpts: this.opts,
1598
1681
  parentSessionId,
1599
1682
  spec: { ...baseSpec, goal: withBudget(goal) },
1683
+ ...(node.dependency_outputs
1684
+ ? { dependencyOutputs: node.dependency_outputs }
1685
+ : {}),
1686
+ ...(node.inherited_knowledge?.length ? { inheritedKnowledge: node.inherited_knowledge } : {}),
1600
1687
  manifest,
1601
1688
  sessionLog: this.opts.sessionLog,
1602
1689
  // This child is a workflow node, so capability resolution applies workflow-node quarantine
@@ -1609,39 +1696,6 @@ export class RuntimeRunner {
1609
1696
  // #2-B-ii: the per-node abort signal the driver fires when the kernel preempts this node.
1610
1697
  ...(abortSignal ? { abortSignal } : {}),
1611
1698
  });
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
1699
  const schema = node.output_schema;
1646
1700
  if (!schema)
1647
1701
  return orchestrator.run(mkCtx(baseSpec.goal));
@@ -1672,27 +1726,6 @@ export class RuntimeRunner {
1672
1726
  },
1673
1727
  };
1674
1728
  }
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
1729
  /**
1697
1730
  * W0-ABI: run a declarative workflow DAG. The kernel owns the DAG and gates every node spawn
1698
1731
  * through the syscall trap; this driver runs each kernel-emitted batch of nodes in parallel,
@@ -1715,6 +1748,10 @@ export class RuntimeRunner {
1715
1748
  if (this.opts.contextPolicy) {
1716
1749
  config.context_policy = normalizeContextPolicy(contextPolicy(this.opts.contextPolicy));
1717
1750
  }
1751
+ const delegated = delegatedContextTiers(this.opts.provider?.descriptor?.().nativeContext);
1752
+ if (delegated) {
1753
+ config.context_policy = { ...config.context_policy, delegated_tiers: delegated };
1754
+ }
1718
1755
  config.signal_policy = {
1719
1756
  queue_max: signalPolicy.queueMax,
1720
1757
  ...(signalPolicy.ttlMs !== undefined ? { ttl_ms: signalPolicy.ttlMs } : {}),
@@ -1971,7 +2008,7 @@ export class RuntimeRunner {
1971
2008
  }
1972
2009
  const controller = new AbortController();
1973
2010
  controllers.set(node.agent_id, controller);
1974
- const running = this.runWorkflowNode(node, parentSessionId, orchestrator, budget, outputs, controller.signal)
2011
+ const running = this.runWorkflowNode(node, parentSessionId, orchestrator, budget, controller.signal)
1975
2012
  .then(result => ({ kind: "node", node, result }))
1976
2013
  .finally(() => batchState.finished.add(node.agent_id));
1977
2014
  // A node can fail while the driver is busy feeding another completion; the race observes
@@ -2004,15 +2041,10 @@ export class RuntimeRunner {
2004
2041
  if (batchState.stopping)
2005
2042
  return { spawned: [], done: undefined };
2006
2043
  const { node, result } = landed;
2007
- // G2: record this node's output so a downstream reduce node can consume it.
2044
+ // Record this node's output so its dependents can consume it.
2008
2045
  const outContent = result.result.finalMessage?.content;
2009
2046
  const outText = typeof outContent === "string" ? outContent : outContent != null ? JSON.stringify(outContent) : "";
2010
2047
  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
2048
  const observationStart = this.pendingObservations.length;
2017
2049
  const completionAction = await this.commitKernelMaybeAction(runtime, this.pendingObservations, {
2018
2050
  kind: "sub_agent_completed",
@@ -2049,17 +2081,13 @@ export class RuntimeRunner {
2049
2081
  }));
2050
2082
  }
2051
2083
  }
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).
2084
+ // Persist node completion for resume recovery, with the output text so post-resume
2085
+ // dependents still see this node's output.
2055
2086
  await this.opts.sessionLog.append(parentSessionId, buildWorkflowNodeCompletedEvent({
2056
2087
  turn: runtime.turn(),
2057
2088
  agentId: result.agentId,
2058
2089
  status: workflowNodeStatusFromTermination(result.result.termination),
2059
2090
  termination: result.result.termination,
2060
- classifyBranch: result.result.classifyBranch,
2061
- tournamentWinner: result.result.tournamentWinner,
2062
- loopContinue: result.result.loopContinue,
2063
2091
  ...(result.result.finalMessage ? { output: result.result.finalMessage } : {}),
2064
2092
  }));
2065
2093
  return { spawned, done: findDone(obs) };
@@ -2112,94 +2140,32 @@ export class RuntimeRunner {
2112
2140
  }
2113
2141
  nextArchiveStart = compressedSeq + 1;
2114
2142
  }
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
2143
  }
2121
2144
  return nextArchiveStart;
2122
2145
  }
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())
2146
+ /** The kernel's own recall for the run goal (plan task 2.1). `preQueryMemory` may rewrite it
2147
+ * into several scoped queries; their hits are merged, one per record. */
2148
+ async kernelRecallHits(goal, requestedK, phase) {
2149
+ const scope = this.opts.memoryScope;
2150
+ const queries = this.opts.preQueryMemory
2151
+ ? await this.opts.preQueryMemory({ goal, phase }) ?? []
2152
+ : [{ scope, query: goal, top_k: requestedK, kinds: [] }];
2153
+ const seen = new Set();
2154
+ const hits = [];
2155
+ for (const query of queries) {
2156
+ if (!query.query.trim())
2157
+ continue;
2158
+ const limit = Math.min(query.top_k ?? requestedK, requestedK);
2159
+ for (const hit of (await this.opts.memoryStore.search(this.opts.agentId, { ...query, top_k: limit })).slice(0, limit)) {
2160
+ if (seen.has(hit.record.record_id))
2142
2161
  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
- });
2162
+ seen.add(hit.record.record_id);
2163
+ hits.push(hit);
2157
2164
  }
2158
- await this.recordHostMemoryRecall(recalled);
2159
- }
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
2165
  }
2166
+ return hits;
2182
2167
  }
2183
2168
  }
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;
2200
- }
2201
- return text.trim() || transcript.slice(0, 2000);
2202
- }
2203
2169
  function isMidRun(events) {
2204
2170
  return events.length > 0 && !events.some(e => e.event.kind === "run_terminal");
2205
2171
  }