@deepstrike/sdk 0.2.52 → 0.2.61

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