@deepstrike/sdk 0.2.61 → 0.2.63

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.
@@ -7,15 +7,25 @@ import { peekProviderReplay, seedProviderReplayFromEvents } from "./provider-rep
7
7
  import { sanitizeReplayText } from "./replay-sanitize.js";
8
8
  import { buildLlmCompletedEvent, buildRunTerminalEvent, buildWorkflowNodeCompletedEvent, buildWorkflowNodesSubmittedEvent, } from "./session-repair.js";
9
9
  import { KernelPrimitivesDashboard } from "./kernel-primitives-dashboard.js";
10
- import { capabilityMarker, capabilitySkill, capabilityTool, capabilityCommandMount, capabilityCommandUnmount, entropySampleFromObservation, messageToKernelMessage, skillMetadataToKernel, taskUpdateToKernel, toolResultToKernel, toolSchemaToKernel, } from "./kernel-step.js";
10
+ import { capabilityMarker, capabilitySkill, capabilityTool, capabilityCommandMount, capabilityCommandUnmount, archivePresentationFromObservations, entropySampleFromObservation, messageToKernelMessage, skillMetadataToKernel, taskUpdateToKernel, toolResultToKernel, toolSchemaToKernel, } from "./kernel-step.js";
11
11
  import { CanonicalKernelRejectedError, CanonicalRunnerRuntime, canonicalKernelAction, canonicalKernelApply, canonicalKernelMaybeAction, canonicalStartAgent, canonicalStartWorkflow, } from "./canonical-kernel-step.js";
12
+ export function stableSemanticArchiveName(effectId) {
13
+ const stableEffectId = effectId.replace(/[^a-zA-Z0-9._:-]/g, "_");
14
+ return `page-out-${stableEffectId || "unknown"}`;
15
+ }
16
+ function compressionAction(action) {
17
+ return action === "snip_compact" || action === "micro_compact" || action === "context_collapse" || action === "auto_compact"
18
+ ? action
19
+ : undefined;
20
+ }
12
21
  import { agentRunSpecToKernel, MILESTONE_UNVERIFIED_REASON, milestoneCheckFail, milestoneCheckPass, milestoneCheckResultToKernel, subAgentResultToKernel, workflowBudgetNote, workflowNodeSpecToKernel, workflowNodeOutcomeFromKernel, workflowNodeStatusFromTermination, workflowNodeToManifest, workflowNodeToSpec, workflowSpecToKernel, } from "../types/agent.js";
13
22
  import { defaultSubAgentOrchestrator } from "./sub-agent-orchestrator.js";
14
23
  import { extractJsonValue, schemaInstruction, schemaRetryInstruction, validateAgainstSchema, } from "./output-schema.js";
15
24
  import { resolveReducer } from "./reducers.js";
16
25
  import { loopInstruction, classifyInstruction, judgeGoal, dependencyOutputsNote, extractClassifyBranch, extractJudgeWinner, } from "./workflow-control-flow.js";
17
26
  import { governancePolicyToKernelEvent, governanceFilterSchema } from "../governance.js";
18
- import { createProviderRequestPlanForProvider, estimateProviderPromptTokens, measurementForPlan, recordPromptMeasurement, } from "../providers/request-plan.js";
27
+ import { createProviderRequestPlanForProvider, estimateProviderPromptTokens, measurementForPlan, recordPromptMeasurement, resolveProviderRoute, } from "../providers/request-plan.js";
28
+ import { FULL_FOOTPRINT_USAGE_ACCOUNTING_POLICY, providerAttemptToRecord, tryNormalizeProviderUsage, } from "./execution-evidence.js";
19
29
  import { kernelObservationToSessionEvent } from "./kernel-event-log.js";
20
30
  import { assertNativeProfile } from "./os-profile.js";
21
31
  import { PayloadStore } from "./payload-store.js";
@@ -155,6 +165,16 @@ export class RuntimeRunner {
155
165
  composedSystemPrompt;
156
166
  /** H1.2: present only when `opts.nudges` is non-empty; else null and the append funnel is untouched. */
157
167
  nudgeEngine;
168
+ /** P4-S1: the run's resolved provider route, assembled once at construction (P4 §0.2 — the
169
+ * provider is fixed for the run today; every attempt references this same route object). */
170
+ providerRoute;
171
+ /** P4-S2: measurement→settlement policy; default = the exact implicit behavior (numbers unchanged). */
172
+ usageAccountingPolicy;
173
+ /** P4 §1.1: the active invocation's derived identity = its chain's FIRST effect_id. Tracked
174
+ * across kernel-driven provider retries (a provider_error commit arms `providerRetryPending`;
175
+ * the next call_provider adopts the pending invocation instead of opening a new one). */
176
+ activeProviderInvocationId;
177
+ providerRetryPending = false;
158
178
  constructor(opts) {
159
179
  this.opts = opts;
160
180
  const schemaAttempts = opts.workflowSchemaValidationAttempts ?? 2;
@@ -166,6 +186,8 @@ export class RuntimeRunner {
166
186
  if (opts.memoryPolicy)
167
187
  memoryPolicyToKernel(opts.memoryPolicy);
168
188
  this.composedSystemPrompt = composeSystemPrompt(opts.systemPrompt, opts.instructions);
189
+ this.providerRoute = resolveProviderRoute(opts.provider);
190
+ this.usageAccountingPolicy = opts.usageAccountingPolicy ?? FULL_FOOTPRINT_USAGE_ACCOUNTING_POLICY;
169
191
  if (opts.enableDiagnosticsDashboard) {
170
192
  const originalAppend = opts.sessionLog.append.bind(opts.sessionLog);
171
193
  opts.sessionLog.append = async (sessionId, event) => {
@@ -221,6 +243,18 @@ export class RuntimeRunner {
221
243
  this.fallbackPayloadStore ??= new PayloadStore();
222
244
  return this.fallbackPayloadStore;
223
245
  }
246
+ /**
247
+ * P4-S1 (G1): land one provider_attempt evidence record per effect execution. Pure host
248
+ * evidence (B7) — appended AFTER the transport fact exists (success/failure/abort), never
249
+ * consulted for kernel input. The accounting policy id pins only when a measurement exists,
250
+ * so (usage, policy_id) deterministically recomputes the settlement that crossed the wire.
251
+ */
252
+ async appendProviderAttempt(sessionId, attempt) {
253
+ await this.opts.sessionLog.append(sessionId, {
254
+ kind: "provider_attempt",
255
+ ...providerAttemptToRecord(attempt, attempt.usage ? this.usageAccountingPolicy.policyId : undefined),
256
+ });
257
+ }
224
258
  async persistMemoryToStore(memory, agentId) {
225
259
  if (!this.opts.memoryStore)
226
260
  throw new Error("memory persistence requires memoryStore");
@@ -745,6 +779,7 @@ export class RuntimeRunner {
745
779
  goal: `workflow:${spec.nodes.length} nodes`,
746
780
  criteria: [],
747
781
  agent_id: this.opts.agentId,
782
+ route: this.providerRoute,
748
783
  });
749
784
  await this.initializeWorkflowKernel(sessionId, runId, groupBudgetScope);
750
785
  }
@@ -1195,6 +1230,7 @@ export class RuntimeRunner {
1195
1230
  agent_id: this.opts.agentId,
1196
1231
  system_prompt: this.composedSystemPrompt,
1197
1232
  ...(attachments ? { attachments } : {}),
1233
+ route: this.providerRoute,
1198
1234
  });
1199
1235
  }
1200
1236
  yield* this.execute(req.sessionId, req.goal, req.criteria ?? [], req.extensions, prior.length > 0 ? prior : undefined, midRun, attachments, runId);
@@ -1224,7 +1260,10 @@ export class RuntimeRunner {
1224
1260
  yield* this.execute(sessionId, start.goal, start.criteria, extensions, events, true, start.attachments, start.run_id);
1225
1261
  }
1226
1262
  /** Execute a kernel-owned approval effect and return the correlated decision lists. */
1227
- async resolveApprovalRequests(requests, runtime, sessionId) {
1263
+ async resolveApprovalRequests(requests, runtime, sessionId,
1264
+ /** P3-S2 (G4): the request_approval effect's id — pinned on the denial's tool_completed
1265
+ * so the denial evidence joins the journal effect chain like an executed tool's does. */
1266
+ effectId) {
1228
1267
  const approved = [];
1229
1268
  const denied = [];
1230
1269
  const events = [];
@@ -1297,6 +1336,7 @@ export class RuntimeRunner {
1297
1336
  error_kind: "governance_denied",
1298
1337
  content: { blocks: [{ type: "text", text: `permission denied: ${denyReason}` }] },
1299
1338
  }],
1339
+ ...(effectId !== undefined ? { effect_id: effectId } : {}),
1300
1340
  });
1301
1341
  }
1302
1342
  }
@@ -1310,6 +1350,8 @@ export class RuntimeRunner {
1310
1350
  this.pendingPageOutArchives = [];
1311
1351
  this.activePageOutArchive = undefined;
1312
1352
  this.currentSessionId = sessionId;
1353
+ this.activeProviderInvocationId = undefined;
1354
+ this.providerRetryPending = false;
1313
1355
  if (this.opts.enableDiagnosticsDashboard) {
1314
1356
  this.dashboard = new KernelPrimitivesDashboard(sessionId);
1315
1357
  }
@@ -1548,6 +1590,18 @@ export class RuntimeRunner {
1548
1590
  break;
1549
1591
  if (action.kind === "call_provider") {
1550
1592
  const providerEffectId = action.effectId;
1593
+ // P4 §1.1: invocation identity is derived, never minted — the chain's FIRST effect_id.
1594
+ // A provider_error commit armed `providerRetryPending`, so this effect CONTINUES the
1595
+ // pending invocation; otherwise it opens a new one. The chain itself lives in the
1596
+ // journal (each hop a Failed resolution input → new effect); this is the SessionLog
1597
+ // evidence projection of it.
1598
+ if (!this.providerRetryPending || this.activeProviderInvocationId === undefined) {
1599
+ this.activeProviderInvocationId = providerEffectId;
1600
+ }
1601
+ this.providerRetryPending = false;
1602
+ const invocationId = this.activeProviderInvocationId;
1603
+ // Host wall-clock, pure evidence (B7/DEC-2): never crosses into kernel input.
1604
+ const attemptStartedAtMs = Date.now();
1551
1605
  const finalToolCalls = [];
1552
1606
  let finalText = "";
1553
1607
  // I5: governance schema-level pre-filter. When a declarative GovernancePolicy is loaded
@@ -1579,6 +1633,9 @@ export class RuntimeRunner {
1579
1633
  let turnCacheTelemetrySource;
1580
1634
  let turnCacheReadBySlot;
1581
1635
  let turnStopReason;
1636
+ // P4 §2: the raw postflight provider usage frame, kept whole so the attempt's
1637
+ // measurement carries full fields (only the settlement crosses the kernel boundary, B4).
1638
+ let turnProviderUsage;
1582
1639
  const providerPlan = createProviderRequestPlanForProvider(this.opts.provider, context, tools, ext);
1583
1640
  const recorded = measurementForPlan(providerPlan, recordedMeasurements.get(providerPlan.fingerprint));
1584
1641
  let promptMeasurement = recorded;
@@ -1605,6 +1662,7 @@ export class RuntimeRunner {
1605
1662
  kind: "prompt_measured",
1606
1663
  turn: runtime.turn(),
1607
1664
  measurement: promptMeasurement,
1665
+ effect_id: providerEffectId,
1608
1666
  });
1609
1667
  }
1610
1668
  const reservedPromptTokens = (this.opts.promptBudget?.promptOverheadTokens ?? 0)
@@ -1617,6 +1675,23 @@ export class RuntimeRunner {
1617
1675
  && promptMeasurement.source.kind !== "heuristic"
1618
1676
  && promptMeasurement.inputTokens + reservedPromptTokens > this.opts.maxTokens;
1619
1677
  if (context.budgetOverflow || measuredOverflow) {
1678
+ // P4 §1.2: blocked BEFORE any transport — zero rungs, status rejected. The
1679
+ // fingerprint still binds the would-be request to its prompt_measured record (G2).
1680
+ await this.appendProviderAttempt(sessionId, {
1681
+ effectId: providerEffectId,
1682
+ attemptSeq: 1,
1683
+ route: this.providerRoute,
1684
+ requestFingerprint: providerPlan.fingerprint,
1685
+ status: "rejected",
1686
+ transportRungs: 0,
1687
+ lastErrorClass: "context_overflow",
1688
+ startedAtMs: attemptStartedAtMs,
1689
+ finishedAtMs: Date.now(),
1690
+ wireEvidence: {
1691
+ protocol: this.providerRoute.protocol,
1692
+ request_fingerprint: providerPlan.fingerprint,
1693
+ },
1694
+ });
1620
1695
  action = await this.commitKernelAction(runtime, this.pendingObservations, {
1621
1696
  kind: "provider_error",
1622
1697
  effect_id: providerEffectId,
@@ -1624,6 +1699,7 @@ export class RuntimeRunner {
1624
1699
  error_kind: "context_overflow",
1625
1700
  retryable: false,
1626
1701
  });
1702
+ this.providerRetryPending = action.kind === "call_provider";
1627
1703
  continue;
1628
1704
  }
1629
1705
  const abortSignal = this.abortController?.signal;
@@ -1639,6 +1715,7 @@ export class RuntimeRunner {
1639
1715
  turnTokens = usageEvt.totalTokens;
1640
1716
  turnInputTokens = usageEvt.inputTokens ?? 0;
1641
1717
  turnOutputTokens = usageEvt.outputTokens ?? 0;
1718
+ turnProviderUsage = usageEvt.providerUsage ?? turnProviderUsage;
1642
1719
  // P0-C: capture the prompt-cache split for the tool-gating hit-rate baseline.
1643
1720
  turnCacheReadTokens = usageEvt.cacheReadInputTokens ?? 0;
1644
1721
  turnCacheCreationTokens = usageEvt.cacheCreationInputTokens ?? 0;
@@ -1670,6 +1747,7 @@ export class RuntimeRunner {
1670
1747
  source: postflight.source,
1671
1748
  confidence: postflight.confidence,
1672
1749
  },
1750
+ effect_id: providerEffectId,
1673
1751
  });
1674
1752
  }
1675
1753
  // Phase 4: stop_reason drives the kernel's max-output-tokens recovery. The closing
@@ -1697,6 +1775,26 @@ export class RuntimeRunner {
1697
1775
  const provider = this.opts.provider.descriptor?.().provider ?? "unknown";
1698
1776
  const providerError = classifyProviderError(provider, err);
1699
1777
  const message = providerError.message;
1778
+ // P4 §1.2: the transport ladder is exhausted — one attempt record, rung count from
1779
+ // provider telemetry (1 on the single-shot stream path), error CLASS only (B1:
1780
+ // never the raw vendor text).
1781
+ const telemetry = this.opts.provider.peekTransportTelemetry?.();
1782
+ await this.appendProviderAttempt(sessionId, {
1783
+ effectId: providerEffectId,
1784
+ attemptSeq: 1,
1785
+ route: this.providerRoute,
1786
+ requestFingerprint: providerPlan.fingerprint,
1787
+ status: "transport_exhausted",
1788
+ transportRungs: telemetry?.rungs ?? 1,
1789
+ lastErrorClass: providerError.kind,
1790
+ startedAtMs: attemptStartedAtMs,
1791
+ finishedAtMs: Date.now(),
1792
+ wireEvidence: {
1793
+ protocol: this.providerRoute.protocol,
1794
+ request_fingerprint: providerPlan.fingerprint,
1795
+ ...(telemetry?.responseId !== undefined ? { response_id: telemetry.responseId } : {}),
1796
+ },
1797
+ });
1700
1798
  // Reactive recovery is now a kernel decision. Forward the raw provider error and
1701
1799
  // dispatch whatever the kernel returns: `call_provider` to retry with a freshly
1702
1800
  // compacted context, or `done` to terminate with an honest `ContextOverflow`. The
@@ -1710,6 +1808,9 @@ export class RuntimeRunner {
1710
1808
  message,
1711
1809
  ...providerErrorEventFields(providerError),
1712
1810
  });
1811
+ // P4 §1.1: a kernel-recovered retry CONTINUES this invocation (the journal holds the
1812
+ // causation hop); a terminal closes it.
1813
+ this.providerRetryPending = action.kind === "call_provider";
1713
1814
  // Withholding (query.ts parity): surface the raw provider error only when the kernel
1714
1815
  // could NOT recover (it returned a terminal). On a recovered retry (`call_provider`)
1715
1816
  // the error stays hidden, so embedders that terminate on `error` events don't see a
@@ -1722,6 +1823,23 @@ export class RuntimeRunner {
1722
1823
  }
1723
1824
  // Do not commit partial provider output after host cancellation.
1724
1825
  if (abortSignal?.aborted) {
1826
+ // P4 §1.2: host cancellation mid-stream — the attempt is evidence too.
1827
+ const telemetry = this.opts.provider.peekTransportTelemetry?.();
1828
+ await this.appendProviderAttempt(sessionId, {
1829
+ effectId: providerEffectId,
1830
+ attemptSeq: 1,
1831
+ route: this.providerRoute,
1832
+ requestFingerprint: providerPlan.fingerprint,
1833
+ status: "aborted",
1834
+ transportRungs: telemetry?.rungs ?? 1,
1835
+ startedAtMs: attemptStartedAtMs,
1836
+ finishedAtMs: Date.now(),
1837
+ wireEvidence: {
1838
+ protocol: this.providerRoute.protocol,
1839
+ request_fingerprint: providerPlan.fingerprint,
1840
+ ...(telemetry?.responseId !== undefined ? { response_id: telemetry.responseId } : {}),
1841
+ },
1842
+ });
1725
1843
  action = await this.commitKernelAction(runtime, this.pendingObservations, {
1726
1844
  kind: "cancel_operation",
1727
1845
  reason: this.cancellationReason ?? "user",
@@ -1753,12 +1871,37 @@ export class RuntimeRunner {
1753
1871
  toolCalls: canonicalToolCalls,
1754
1872
  tokenCount: turnOutputTokens || turnTokens || undefined,
1755
1873
  };
1874
+ // P4 §2: assemble the measurement from the exact numbers that cross the boundary today
1875
+ // (inputTokens/outputTokens turn counters), enriched with the raw provider frame's cache
1876
+ // split and reasoning fields. An invalid frame degrades to no measurement (evidence
1877
+ // never breaks a run); the settlement then falls back to the raw counts below.
1878
+ const attemptUsage = (turnInputTokens > 0 || turnOutputTokens > 0)
1879
+ ? tryNormalizeProviderUsage({
1880
+ inputTokens: turnInputTokens,
1881
+ outputTokens: turnOutputTokens,
1882
+ ...(turnProviderUsage?.cacheReadInputTokens !== undefined
1883
+ ? { cacheReadInputTokens: turnProviderUsage.cacheReadInputTokens }
1884
+ : turnCacheReadTokens > 0 ? { cacheReadInputTokens: turnCacheReadTokens } : {}),
1885
+ ...(turnProviderUsage?.cacheCreationInputTokens !== undefined
1886
+ ? { cacheCreationInputTokens: turnProviderUsage.cacheCreationInputTokens }
1887
+ : turnCacheCreationTokens > 0 ? { cacheCreationInputTokens: turnCacheCreationTokens } : {}),
1888
+ ...(turnProviderUsage?.reasoningTokens !== undefined
1889
+ ? { reasoningTokens: turnProviderUsage.reasoningTokens } : {}),
1890
+ cacheTelemetryStatus: turnCacheTelemetryStatus,
1891
+ ...(turnCacheTelemetrySource !== undefined
1892
+ ? { cacheTelemetrySource: turnCacheTelemetrySource } : {}),
1893
+ })
1894
+ : undefined;
1895
+ const settlement = attemptUsage ? this.usageAccountingPolicy.settle(attemptUsage) : undefined;
1756
1896
  const providerEvent = {
1757
1897
  kind: "provider_result",
1758
1898
  effect_id: providerEffectId,
1759
1899
  message: messageToKernelMessage(assistantMessage),
1760
- ...(turnInputTokens > 0 ? { observed_input_tokens: turnInputTokens } : {}),
1761
- ...(turnOutputTokens > 0 ? { observed_output_tokens: turnOutputTokens } : {}),
1900
+ // P4-S2: observed_* now comes from the pinned policy's settlement of the measurement.
1901
+ // Under the default full-footprint policy these are provably the numbers the runner
1902
+ // has always fed (inputTokens/outputTokens verbatim under the same >0 gates).
1903
+ ...(turnInputTokens > 0 ? { observed_input_tokens: settlement?.observed_input_tokens ?? turnInputTokens } : {}),
1904
+ ...(turnOutputTokens > 0 ? { observed_output_tokens: settlement?.observed_output_tokens ?? turnOutputTokens } : {}),
1762
1905
  ...(turnStopReason ? { stop_reason: turnStopReason } : {}),
1763
1906
  };
1764
1907
  if (this.opts.skillDir) {
@@ -1789,14 +1932,38 @@ export class RuntimeRunner {
1789
1932
  }
1790
1933
  }
1791
1934
  }
1792
- action = await this.commitKernelAction(runtime, this.pendingObservations, providerEvent);
1935
+ // P4-S1: land the attempt evidence BEFORE the kernel resolution commits — the record
1936
+ // describes the transport fact, which exists regardless of what the kernel decides next.
1937
+ const attemptTelemetry = this.opts.provider.peekTransportTelemetry?.();
1793
1938
  const providerReplay = peekProviderReplay(this.opts.provider, finalText, finalToolCalls);
1939
+ const wireEvidence = {
1940
+ protocol: this.providerRoute.protocol,
1941
+ request_fingerprint: providerPlan.fingerprint,
1942
+ ...(attemptTelemetry?.responseId !== undefined ? { response_id: attemptTelemetry.responseId } : {}),
1943
+ ...(providerReplay !== undefined ? { replay_state: providerReplay } : {}),
1944
+ };
1945
+ await this.appendProviderAttempt(sessionId, {
1946
+ effectId: providerEffectId,
1947
+ attemptSeq: 1,
1948
+ route: this.providerRoute,
1949
+ requestFingerprint: providerPlan.fingerprint,
1950
+ status: "success",
1951
+ transportRungs: attemptTelemetry?.rungs ?? 1,
1952
+ startedAtMs: attemptStartedAtMs,
1953
+ finishedAtMs: Date.now(),
1954
+ ...(attemptUsage !== undefined ? { usage: attemptUsage } : {}),
1955
+ wireEvidence,
1956
+ });
1957
+ action = await this.commitKernelAction(runtime, this.pendingObservations, providerEvent);
1794
1958
  await this.opts.sessionLog.append(sessionId, buildLlmCompletedEvent({
1795
1959
  turn: runtime.turn(),
1796
1960
  content: finalText,
1797
1961
  tokenCount: turnOutputTokens || turnTokens || undefined,
1798
1962
  toolCalls: finalToolCalls,
1799
1963
  providerReplay,
1964
+ effectId: providerEffectId,
1965
+ invocationId,
1966
+ wireEvidence,
1800
1967
  }));
1801
1968
  // P0-C: emit per-turn tool-gating telemetry. `activeSkill` reflects the skill in effect
1802
1969
  // GOING INTO this turn; a `skill` call here only takes effect next turn, so emit first, then
@@ -1831,7 +1998,7 @@ export class RuntimeRunner {
1831
1998
  }
1832
1999
  }
1833
2000
  else if (action.kind === "request_approval") {
1834
- const resolved = await this.resolveApprovalRequests(action.requests, runtime, sessionId);
2001
+ const resolved = await this.resolveApprovalRequests(action.requests, runtime, sessionId, action.effectId);
1835
2002
  for (const event of resolved.events)
1836
2003
  yield event;
1837
2004
  action = await this.commitKernelAction(runtime, this.pendingObservations, {
@@ -1914,6 +2081,7 @@ export class RuntimeRunner {
1914
2081
  await this.logMemoryRetrievalResult(sessionId, hits);
1915
2082
  }
1916
2083
  else if (action.kind === "archive_page_out") {
2084
+ const archiveEffectId = action.effectId;
1917
2085
  const archiveMeta = this.activePageOutArchive
1918
2086
  ?? this.pendingPageOutArchives.shift()
1919
2087
  ?? {
@@ -1941,8 +2109,8 @@ export class RuntimeRunner {
1941
2109
  error = formatToolError(cause);
1942
2110
  }
1943
2111
  const archived = action.archived ?? [];
1944
- const archiveAction = compressionAction(action.action) ?? "auto_compact";
1945
- const archiveTier = action.tier;
2112
+ const archiveAction = archiveMeta.action ?? "auto_compact";
2113
+ const archiveTier = archiveMeta.tier;
1946
2114
  const compressedSeq = archiveMeta.compressedSeq;
1947
2115
  if (!error)
1948
2116
  this.activePageOutArchive = undefined;
@@ -1959,7 +2127,7 @@ export class RuntimeRunner {
1959
2127
  taskScope.spawn("compressed-summary-upgrade", upgrade);
1960
2128
  }
1961
2129
  if (archiveTier === "semantic" && archived.length > 0) {
1962
- taskScope.spawn("semantic-page-out", () => this.archiveSemanticPageOut(archived, archiveAction, sessionId));
2130
+ taskScope.spawn("semantic-page-out", () => this.archiveSemanticPageOut(archived, archiveAction, sessionId, archiveEffectId));
1963
2131
  }
1964
2132
  }
1965
2133
  }
@@ -2160,6 +2328,7 @@ export class RuntimeRunner {
2160
2328
  token_count: r.tokenCount,
2161
2329
  content: { blocks: toolOutputBlocksToDurable(r.contentParts?.length ? r.contentParts : [{ type: "text", text: r.output }]) },
2162
2330
  })),
2331
+ effect_id: toolEffectId,
2163
2332
  });
2164
2333
  // The canonical provider resolution already activates a successfully resolved `skill` call.
2165
2334
  // The host's remaining responsibility is to pin the resolved METHOD content — how to do
@@ -2516,7 +2685,12 @@ export class RuntimeRunner {
2516
2685
  const compressedSeq = await this.opts.sessionLog.append(sessionId, event);
2517
2686
  if (event.kind === "compressed") {
2518
2687
  if ((obs.archived_count ?? 0) > 0) {
2519
- this.pendingPageOutArchives.push({ archiveStart: nextArchiveStart, compressedSeq });
2688
+ const archivePresentation = archivePresentationFromObservations([obs]);
2689
+ this.pendingPageOutArchives.push({
2690
+ archiveStart: nextArchiveStart,
2691
+ compressedSeq,
2692
+ ...archivePresentation,
2693
+ });
2520
2694
  }
2521
2695
  nextArchiveStart = compressedSeq + 1;
2522
2696
  }
@@ -2529,37 +2703,58 @@ export class RuntimeRunner {
2529
2703
  }
2530
2704
  return nextArchiveStart;
2531
2705
  }
2532
- async archiveSemanticPageOut(archived, action, sessionId) {
2706
+ async archiveSemanticPageOut(archived, action, sessionId, effectId = "unknown") {
2533
2707
  if (!this.opts.memoryStore || !this.opts.agentId || !this.opts.memoryScope)
2534
2708
  return;
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);
2538
- // P2 write-funnel: route through the ONE gated WriteMemory syscall so validation,
2539
- // the rolling write quota, dedup, and the memory_written audit all apply. Score is
2540
- // advisory (0.6) — an automatic summary must never outrank curated content.
2541
- const now = Date.now();
2542
- const name = `page-out-${now}`;
2543
- await this.writeMemory({
2544
- record_id: `${this.opts.memoryScope.tenant_id}:${this.opts.memoryScope.namespace}:project:${name}`,
2545
- scope: this.opts.memoryScope,
2546
- name,
2547
- kind: "project",
2548
- content: summary,
2549
- description: `auto summary of ${action ?? "compaction"} archive`,
2550
- provenance: {
2551
- session_id: sessionId,
2552
- author: "extraction",
2553
- trust: "untrusted",
2554
- evidence_refs: [],
2555
- },
2556
- created_at: now,
2557
- updated_at: now,
2558
- recall_count: 0,
2559
- confidence: 0.6,
2560
- links: [],
2561
- pinned: false,
2562
- }, { sessionId, agentId: this.opts.agentId });
2709
+ await this.opts.sessionLog.append(sessionId, {
2710
+ kind: "semantic_archive_pending",
2711
+ effect_id: effectId,
2712
+ ...(action ? { action } : {}),
2713
+ });
2714
+ try {
2715
+ const summary = this.opts.memorySummarizer
2716
+ ? await this.opts.memorySummarizer.summarize(archived, { action })
2717
+ : await summarizeForLongTermMemory(this.opts.memoryProvider ?? this.opts.provider, archived, this.opts.memorySystemPrompt);
2718
+ // P2 write-funnel: route through the ONE gated WriteMemory syscall so validation,
2719
+ // the rolling write quota, dedup, and the memory_written audit all apply. Score is
2720
+ // advisory (0.6) — an automatic summary must never outrank curated content.
2721
+ const now = Date.now();
2722
+ const name = stableSemanticArchiveName(effectId);
2723
+ const recordId = `${this.opts.memoryScope.tenant_id}:${this.opts.memoryScope.namespace}:project:${name}`;
2724
+ await this.writeMemory({
2725
+ record_id: recordId,
2726
+ scope: this.opts.memoryScope,
2727
+ name,
2728
+ kind: "project",
2729
+ content: summary,
2730
+ description: `auto summary of ${action ?? "compaction"} archive`,
2731
+ provenance: {
2732
+ session_id: sessionId,
2733
+ author: "extraction",
2734
+ trust: "untrusted",
2735
+ evidence_refs: [],
2736
+ },
2737
+ created_at: now,
2738
+ updated_at: now,
2739
+ recall_count: 0,
2740
+ confidence: 0.6,
2741
+ links: [],
2742
+ pinned: false,
2743
+ }, { sessionId, agentId: this.opts.agentId });
2744
+ await this.opts.sessionLog.append(sessionId, {
2745
+ kind: "semantic_archive_completed",
2746
+ effect_id: effectId,
2747
+ record_id: recordId,
2748
+ });
2749
+ }
2750
+ catch (error) {
2751
+ await this.opts.sessionLog.append(sessionId, {
2752
+ kind: "semantic_archive_failed",
2753
+ effect_id: effectId,
2754
+ error: formatToolError(error),
2755
+ });
2756
+ throw error;
2757
+ }
2563
2758
  }
2564
2759
  async upgradeCompressedSummary(sessionId, compressedSeq, archived, action, runtime) {
2565
2760
  const summary = await this.opts.asyncSummarizer.summarize(archived, action);
@@ -2634,15 +2829,6 @@ function attachmentsToKernelMessage(parts) {
2634
2829
  });
2635
2830
  return { role: "user", content };
2636
2831
  }
2637
- function compressionAction(action) {
2638
- if (action === "snip_compact" ||
2639
- action === "micro_compact" ||
2640
- action === "context_collapse" ||
2641
- action === "auto_compact") {
2642
- return action;
2643
- }
2644
- return undefined;
2645
- }
2646
2832
  async function summarizeForLongTermMemory(provider, archived, systemPrompt) {
2647
2833
  const transcript = archived
2648
2834
  .map(m => `${m.role}: ${m.content}`)
@@ -1,6 +1,7 @@
1
1
  import type { KernelPrimitive } from "./kernel-event-log.js";
2
- import type { ContentPart, ProviderReplay, ToolCall, ToolErrorKind } from "../types.js";
3
- import type { RecordedPromptMeasurement } from "../providers/request-plan.js";
2
+ import type { ContentPart, ProviderReplay, ProviderWireEvidence, ToolCall, ToolErrorKind } from "../types.js";
3
+ import type { RecordedPromptMeasurement, ResolvedProviderRoute } from "../providers/request-plan.js";
4
+ import type { ProviderAttemptRecord } from "./execution-evidence.js";
4
5
  import type { MemoryRecall, MemoryScope } from "../memory/protocols.js";
5
6
  import type { KernelJournal } from "./kernel-journal.js";
6
7
  export type RollbackReason = {
@@ -30,6 +31,7 @@ export type SessionEvent = {
30
31
  agent_id?: string;
31
32
  system_prompt?: string;
32
33
  attachments?: ContentPart[];
34
+ route?: ResolvedProviderRoute;
33
35
  } | {
34
36
  kind: "llm_completed";
35
37
  turn: number;
@@ -37,11 +39,17 @@ export type SessionEvent = {
37
39
  token_count?: number;
38
40
  tool_calls: ToolCall[];
39
41
  provider_replay?: ProviderReplay;
42
+ effect_id?: string;
43
+ invocation_id?: string;
44
+ wire_evidence?: ProviderWireEvidence;
40
45
  } | {
41
46
  kind: "prompt_measured";
42
47
  turn: number;
43
48
  measurement: RecordedPromptMeasurement;
44
- } | {
49
+ effect_id?: string;
50
+ } | ({
51
+ kind: "provider_attempt";
52
+ } & ProviderAttemptRecord) | {
45
53
  kind: "tool_requested";
46
54
  turn: number;
47
55
  calls: ToolCall[];
@@ -59,6 +67,7 @@ export type SessionEvent = {
59
67
  blocks: Record<string, unknown>[];
60
68
  };
61
69
  }>;
70
+ effect_id?: string;
62
71
  } | {
63
72
  kind: "tool_argument_repaired";
64
73
  turn: number;
@@ -98,6 +107,18 @@ export type SessionEvent = {
98
107
  tier_hint?: string;
99
108
  message_count?: number;
100
109
  archive_ref?: string;
110
+ } | {
111
+ kind: "semantic_archive_pending";
112
+ effect_id: string;
113
+ action?: string;
114
+ } | {
115
+ kind: "semantic_archive_completed";
116
+ effect_id: string;
117
+ record_id: string;
118
+ } | {
119
+ kind: "semantic_archive_failed";
120
+ effect_id: string;
121
+ error: string;
101
122
  } | {
102
123
  kind: "page_in";
103
124
  turn: number;
@@ -314,6 +335,14 @@ export type SessionEvent = {
314
335
  reason: string;
315
336
  coerced_from?: string;
316
337
  };
338
+ export type SessionEventKind = SessionEvent["kind"];
339
+ /**
340
+ * The registered session-event vocabulary (F9 / S3, P7-S4). This list is the single authority
341
+ * the cross-SDK manifest fixture pins: a kind added here without the same-commit update to
342
+ * `tests/fixtures/sdk-conformance/canonical/session-event-vocabulary.json` and the python/wasm
343
+ * vocabularies turns cross-SDK conformance red. Declared in `SessionEvent` union order.
344
+ */
345
+ export declare const SESSION_EVENT_KINDS: readonly ["run_started", "llm_completed", "prompt_measured", "provider_attempt", "tool_requested", "tool_completed", "tool_argument_repaired", "tool_denied", "permission_requested", "permission_resolved", "compressed", "page_out", "semantic_archive_pending", "semantic_archive_completed", "semantic_archive_failed", "page_in", "rollbacked", "capability_changed", "context_renewed", "suspended", "resumed", "tool_gated", "signal_delivery_disposed", "budget_exceeded", "budget_usage_reported", "operation_cancelled", "milestone_advanced", "milestone_blocked", "checkpoint_taken", "entropy_sample", "entropy_alert", "agent_process_changed", "memory_written", "memory_queried", "memory_validation_failed", "memory_write_failed", "memory_query_failed", "memory_retrieval_result", "workflow_node_completed", "workflow_nodes_submitted", "workflow_batch_spawned", "workflow_completed", "kernel_observation", "run_terminal", "summary_upgraded", "group_member_joined", "group_budget_charged", "round_started", "round_paced"];
317
346
  /**
318
347
  * The business-projection log (spec §9.2): run started/terminal, stream events, observations,
319
348
  * provider/tool presentation, audit metadata.