@caupulican/pi-adaptative 0.80.97 → 0.80.98

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 (60) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/core/agent-session.d.ts +19 -2
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +185 -6
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/autonomy/envelope-enforcement.d.ts +17 -0
  7. package/dist/core/autonomy/envelope-enforcement.d.ts.map +1 -0
  8. package/dist/core/autonomy/envelope-enforcement.js +80 -0
  9. package/dist/core/autonomy/envelope-enforcement.js.map +1 -0
  10. package/dist/core/context/brain-curator.d.ts +7 -0
  11. package/dist/core/context/brain-curator.d.ts.map +1 -1
  12. package/dist/core/context/brain-curator.js +6 -0
  13. package/dist/core/context/brain-curator.js.map +1 -1
  14. package/dist/core/context/context-composition.d.ts.map +1 -1
  15. package/dist/core/context/context-composition.js +1 -1
  16. package/dist/core/context/context-composition.js.map +1 -1
  17. package/dist/core/delegation/session-worker-result.d.ts +8 -2
  18. package/dist/core/delegation/session-worker-result.d.ts.map +1 -1
  19. package/dist/core/delegation/session-worker-result.js +18 -1
  20. package/dist/core/delegation/session-worker-result.js.map +1 -1
  21. package/dist/core/learning/observation-store.d.ts +20 -0
  22. package/dist/core/learning/observation-store.d.ts.map +1 -0
  23. package/dist/core/learning/observation-store.js +101 -0
  24. package/dist/core/learning/observation-store.js.map +1 -0
  25. package/dist/core/model-router/executor-route.d.ts +8 -0
  26. package/dist/core/model-router/executor-route.d.ts.map +1 -0
  27. package/dist/core/model-router/executor-route.js +33 -0
  28. package/dist/core/model-router/executor-route.js.map +1 -0
  29. package/dist/core/model-router/tool-escalation.d.ts +2 -0
  30. package/dist/core/model-router/tool-escalation.d.ts.map +1 -1
  31. package/dist/core/model-router/tool-escalation.js +6 -0
  32. package/dist/core/model-router/tool-escalation.js.map +1 -1
  33. package/dist/core/research/research-runner.d.ts +8 -1
  34. package/dist/core/research/research-runner.d.ts.map +1 -1
  35. package/dist/core/research/research-runner.js +13 -1
  36. package/dist/core/research/research-runner.js.map +1 -1
  37. package/dist/core/research/workspace-collector.d.ts +25 -0
  38. package/dist/core/research/workspace-collector.d.ts.map +1 -0
  39. package/dist/core/research/workspace-collector.js +286 -0
  40. package/dist/core/research/workspace-collector.js.map +1 -0
  41. package/dist/core/settings-manager.d.ts +2 -0
  42. package/dist/core/settings-manager.d.ts.map +1 -1
  43. package/dist/core/settings-manager.js +3 -0
  44. package/dist/core/settings-manager.js.map +1 -1
  45. package/dist/modes/interactive/components/fitness-role-selector.d.ts +1 -1
  46. package/dist/modes/interactive/components/fitness-role-selector.d.ts.map +1 -1
  47. package/dist/modes/interactive/components/fitness-role-selector.js +5 -0
  48. package/dist/modes/interactive/components/fitness-role-selector.js.map +1 -1
  49. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  50. package/dist/modes/interactive/interactive-mode.js +9 -0
  51. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  52. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  53. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  54. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  55. package/examples/extensions/sandbox/package-lock.json +2 -2
  56. package/examples/extensions/sandbox/package.json +1 -1
  57. package/examples/extensions/with-deps/package-lock.json +2 -2
  58. package/examples/extensions/with-deps/package.json +1 -1
  59. package/npm-shrinkwrap.json +12 -12
  60. package/package.json +4 -4
@@ -25,6 +25,7 @@ import { evaluateToolGate } from "./autonomy/gates.js";
25
25
  import { LaneTracker } from "./autonomy/lane-tracker.js";
26
26
  import { appendLaneRecordSnapshot, getLaneRecordSnapshots } from "./autonomy/session-lane-record.js";
27
27
  import { composeSubagentSystemPrompt } from "./autonomy/subagent-prompt.js";
28
+ import { AUTONOMY_TELEMETRY_EVENT_TYPES, redactTelemetryValue, } from "./autonomy/telemetry-events.js";
28
29
  import { executeBashWithOperations } from "./bash-executor.js";
29
30
  import { calculateContextTokens, collectEntriesForBranchSummary, compact, estimateContextTokens, generateBranchSummary, prepareCompaction, shouldCompact, } from "./compaction/index.js";
30
31
  // (module-scope helper for curation goal extraction defined below the imports)
@@ -57,6 +58,7 @@ import { buildGoalRuntimeSnapshot, } from "./goals/goal-runtime-snapshot.js";
57
58
  import { appendGoalStateSnapshot, getLatestGoalStateSnapshot } from "./goals/session-goal-state.js";
58
59
  import { appendLearningAuditSnapshot, getLearningAuditSnapshots, proposalFromReflectionWrite, rollbackPlanForReflectionWrite, } from "./learning/learning-audit.js";
59
60
  import { evaluateLearningDecision } from "./learning/learning-gate.js";
61
+ import { ObservationStore, observationKey } from "./learning/observation-store.js";
60
62
  import { decideDemand, ReflectionEngine, } from "./learning/reflection-engine.js";
61
63
  import { appendLearningDecisionSnapshot, getLearningDecisionSnapshots } from "./learning/session-learning-decision.js";
62
64
  import { isPromotedFrontmatter, SkillCurator } from "./learning/skill-curator.js";
@@ -69,6 +71,7 @@ import { createCustomMessage } from "./messages.js";
69
71
  import { deriveModelCapabilityProfile, filterToolNamesForCapability, } from "./model-capability.js";
70
72
  import { resolveCliModel, resolveProfileModelSettings } from "./model-resolver.js";
71
73
  import { collectModelRouterConfigDiagnostics } from "./model-router/config-diagnostics.js";
74
+ import { classifyExecutorTurn } from "./model-router/executor-route.js";
72
75
  import { classifyModelRouterRoute } from "./model-router/intent-classifier.js";
73
76
  import { ROUTE_JUDGE_MAX_OUTPUT_TOKENS, runRouteJudge } from "./model-router/route-judge.js";
74
77
  import { bufferModelRouterSessionCustomMessage, bufferModelRouterSessionMessage, createModelRouterSessionBuffer, flushModelRouterSessionBuffer, } from "./model-router/session-buffer.js";
@@ -79,6 +82,7 @@ import { expandPromptTemplate } from "./prompt-templates.js";
79
82
  import { runModelFitnessProbe } from "./research/model-fitness.js";
80
83
  import { runResearch } from "./research/research-runner.js";
81
84
  import { appendEvidenceBundleSnapshot, getEvidenceBundleSnapshots, getLatestEvidenceBundleSnapshot, } from "./research/session-evidence-bundle.js";
85
+ import { collectWorkspaceSources } from "./research/workspace-collector.js";
82
86
  import { stripResourceProfileBlocks } from "./resource-profile-blocks.js";
83
87
  import { classifyToolTrust, UNTRUSTED_BOUNDARY_SYSTEM_RULE, wrapUntrustedText } from "./security/untrusted-boundary.js";
84
88
  import { CURRENT_SESSION_VERSION, getLatestCompactionEntry } from "./session-manager.js";
@@ -142,6 +146,9 @@ function formatModelRouterModel(model) {
142
146
  function persistModelRouterDecision(sessionManager, decision) {
143
147
  sessionManager.appendCustomEntry(MODEL_ROUTER_DECISION_CUSTOM_TYPE, decision);
144
148
  }
149
+ /** Custom-entry type for G3 autonomy telemetry. Distinct from the router/lane record types so a
150
+ * telemetry consumer can filter on it without decoding operational snapshots. */
151
+ const AUTONOMY_TELEMETRY_CUSTOM_TYPE = "autonomy-telemetry";
145
152
  /** Read a packed grep/find tool result's `details.artifactId`, if present, without `any`. */
146
153
  function extractArtifactId(message) {
147
154
  if (!message || message.role !== "toolResult")
@@ -256,6 +263,7 @@ export class AgentSession {
256
263
  _baseToolDefinitions = new Map();
257
264
  _cwd;
258
265
  _agentDir;
266
+ _collectWorkspaceSources;
259
267
  _extensionRunnerRef;
260
268
  _initialActiveToolNames;
261
269
  _allowedToolNames;
@@ -323,6 +331,7 @@ export class AgentSession {
323
331
  this._customTools = config.customTools ?? [];
324
332
  this._cwd = config.cwd;
325
333
  this._agentDir = config.agentDir ?? getAgentDir();
334
+ this._collectWorkspaceSources = config.collectWorkspaceSources ?? collectWorkspaceSources;
326
335
  this._modelRegistry = config.modelRegistry;
327
336
  this._extensionRunnerRef = config.extensionRunnerRef;
328
337
  this._initialActiveToolNames = config.initialActiveToolNames;
@@ -1213,7 +1222,13 @@ export class AgentSession {
1213
1222
  writePayloads,
1214
1223
  curation: curationSettings.enabled
1215
1224
  ? {
1216
- resolveDigest: (digestKey) => this._brainCurator.getDigest(digestKey),
1225
+ resolveDigest: (digestKey) => {
1226
+ const digest = this._brainCurator.getDigest(digestKey);
1227
+ // Count serves on the REAL per-turn pass only, never the report path.
1228
+ if (digest !== undefined && writePayloads)
1229
+ this._brainCurator.noteDigestServed();
1230
+ return digest;
1231
+ },
1217
1232
  // Only the real per-turn pass enqueues work; the read-only report path
1218
1233
  // (writePayloads=false) stays side-effect free.
1219
1234
  onPacked: writePayloads
@@ -1315,7 +1330,12 @@ export class AgentSession {
1315
1330
  _installAgentToolHooks() {
1316
1331
  this.agent.beforeToolCall = async ({ toolCall, args }) => {
1317
1332
  if (this._activeModelRouterRoute &&
1318
- shouldEscalateModelRouterTool({ tier: this._activeModelRouterRoute.tier, toolName: toolCall.name, args })) {
1333
+ shouldEscalateModelRouterTool({
1334
+ tier: this._activeModelRouterRoute.tier,
1335
+ toolName: toolCall.name,
1336
+ args,
1337
+ reasonCode: this._activeModelRouterRoute.reasonCode,
1338
+ })) {
1319
1339
  this._modelRouterEscalationRequested = true;
1320
1340
  this.agent.abort();
1321
1341
  return {
@@ -2037,12 +2057,54 @@ export class AgentSession {
2037
2057
  return false;
2038
2058
  return this._modelRegistry.hasConfiguredAuth(resolved.model);
2039
2059
  }
2060
+ _resolveExecutorRoute(prompt, executorPattern) {
2061
+ if (!executorPattern)
2062
+ return undefined;
2063
+ try {
2064
+ const verdict = classifyExecutorTurn(prompt, this.settingsManager.getToolkitScripts());
2065
+ if (!verdict.execute)
2066
+ return undefined;
2067
+ const resolved = resolveCliModel({ cliModel: executorPattern, modelRegistry: this._modelRegistry });
2068
+ if (!resolved.model || !this._modelRegistry.hasConfiguredAuth(resolved.model))
2069
+ return undefined;
2070
+ // Fitness gate: the executor must have PROVEN tool-calling on this host (same
2071
+ // canonical-ref discipline as the curation gate).
2072
+ const canonicalRef = `${resolved.model.provider}/${resolved.model.id}`;
2073
+ const fitness = FitnessStore.forAgentDir(this._agentDir)
2074
+ .getForHost()
2075
+ .find((entry) => entry.model === canonicalRef);
2076
+ const toolCall = fitness?.report.toolCall;
2077
+ if (!toolCall || toolCall.succeeded < Math.ceil(toolCall.total * (2 / 3)))
2078
+ return undefined;
2079
+ this._lastModelRouterIntent = "research";
2080
+ return {
2081
+ decision: {
2082
+ tier: "cheap",
2083
+ risk: "scoped-write",
2084
+ confidence: 1,
2085
+ reasonCode: "executor_direct",
2086
+ reasons: [`Executor lane: Level-0 direct hit on toolkit script "${verdict.scriptName}"`],
2087
+ },
2088
+ model: resolved.model,
2089
+ };
2090
+ }
2091
+ catch {
2092
+ return undefined;
2093
+ }
2094
+ }
2040
2095
  _resolveModelRouterTurnRoute(prompt) {
2041
2096
  const settings = this.settingsManager.getModelRouterSettings();
2042
2097
  if (!settings.enabled) {
2043
2098
  this._lastModelRouterSkipReason = "disabled";
2044
2099
  return undefined;
2045
2100
  }
2101
+ // G16 executor lane: a Level-0 DIRECT toolkit hit on a command-shaped prompt routes the
2102
+ // whole turn to the configured local executor (tool-call-fitness-gated) instead of
2103
+ // spending the frontier model on a one-tool reflex. Ambiguity never routes here — it
2104
+ // stays with the big model and the reflex brain. Deterministic, so the judge is skipped.
2105
+ const executorRoute = this._resolveExecutorRoute(prompt, settings.executorModel);
2106
+ if (executorRoute)
2107
+ return executorRoute;
2046
2108
  const decision = classifyModelRouterRoute(prompt);
2047
2109
  this._lastModelRouterIntent = decision.tier === "cheap" ? "research" : "modify";
2048
2110
  // Learning tier must not be selected for normal user prompts
@@ -2128,6 +2190,9 @@ export class AgentSession {
2128
2190
  return undefined;
2129
2191
  if (options?.skipJudge)
2130
2192
  return baseline;
2193
+ // Deterministic executor routes need no judge (Level-0 already decided).
2194
+ if (baseline.decision.reasonCode === "executor_direct")
2195
+ return baseline;
2131
2196
  const settings = this.settingsManager.getModelRouterSettings();
2132
2197
  if (!settings.judgeEnabled)
2133
2198
  return baseline;
@@ -2336,6 +2401,19 @@ export class AgentSession {
2336
2401
  }
2337
2402
  if (persistDecision && completedDecision) {
2338
2403
  persistModelRouterDecision(this.sessionManager, completedDecision);
2404
+ // G3: one route event per user-facing routed turn (the escalation retry runs with
2405
+ // persistDecision=false, so it does not double-emit). Codes/numbers only — no prompt text.
2406
+ this._emitAutonomyTelemetry({
2407
+ type: AUTONOMY_TELEMETRY_EVENT_TYPES.routeDecision,
2408
+ timestamp: new Date().toISOString(),
2409
+ payload: {
2410
+ tier: completedDecision.route.tier,
2411
+ risk: completedDecision.route.risk,
2412
+ reasonCode: completedDecision.route.reasonCode,
2413
+ confidence: completedDecision.route.confidence,
2414
+ outcome: completedDecision.outcome,
2415
+ },
2416
+ });
2339
2417
  }
2340
2418
  if (thrownError) {
2341
2419
  throw thrownError;
@@ -5007,8 +5085,24 @@ export class AgentSession {
5007
5085
  getLaneRecords() {
5008
5086
  return this._laneTracker.getRecords();
5009
5087
  }
5010
- saveWorkerResultSnapshot(result) {
5011
- return appendWorkerResultSnapshot(this.sessionManager, result);
5088
+ /**
5089
+ * G3: bounded autonomy-telemetry sink. Passes the whole event through {@link redactTelemetryValue}
5090
+ * (the taxonomy's redaction contract) before storing it, so a secret that leaked into a payload
5091
+ * field never lands in the session log. Observe-only: a failure here can never surface into the
5092
+ * turn it is measuring, so the whole body is swallowed. Payloads MUST stay small (ids, codes,
5093
+ * numbers) — never prompt/summary text; callers own that discipline.
5094
+ */
5095
+ _emitAutonomyTelemetry(event) {
5096
+ try {
5097
+ const redacted = redactTelemetryValue(event);
5098
+ this.sessionManager.appendCustomEntry(AUTONOMY_TELEMETRY_CUSTOM_TYPE, { version: 1, ...redacted });
5099
+ }
5100
+ catch {
5101
+ // Telemetry is best-effort: swallow so a sink failure cannot break the observed turn.
5102
+ }
5103
+ }
5104
+ saveWorkerResultSnapshot(result, request) {
5105
+ return appendWorkerResultSnapshot(this.sessionManager, result, request);
5012
5106
  }
5013
5107
  getWorkerResultSnapshots() {
5014
5108
  return getWorkerResultSnapshots(this.sessionManager.getEntries());
@@ -5285,9 +5379,17 @@ export class AgentSession {
5285
5379
  const startedRecord = this._laneTracker.start({ type: "research", goalId: demand.goalId });
5286
5380
  try {
5287
5381
  let spentUsage;
5382
+ // Best-effort, pointer-first workspace evidence. Derives search terms from the goal/requirement
5383
+ // text (not the identity-key query) and is bounded + silent-on-failure: [] == today's behavior.
5384
+ const workspaceSources = await this._collectWorkspaceSources({
5385
+ query: `${demand.context}\n${demand.query}`,
5386
+ cwd: this._cwd,
5387
+ maxSources: settings.maxSources,
5388
+ });
5288
5389
  const result = await runResearch({
5289
5390
  query: demand.query,
5290
5391
  context: demand.context,
5392
+ sources: workspaceSources,
5291
5393
  envelope: this._buildResearchLaneEnvelope(settings.maxUsd, laneProfile),
5292
5394
  maxUsd: settings.maxUsd,
5293
5395
  maxSources: settings.maxSources,
@@ -5346,6 +5448,20 @@ export class AgentSession {
5346
5448
  });
5347
5449
  if (record) {
5348
5450
  appendLaneRecordSnapshot(this.sessionManager, record);
5451
+ // G3: a research lane's product is an evidence bundle, so its terminal record maps to
5452
+ // the evidence_bundle event. Lane outcome only (status/reasonCode/cost) — no findings text.
5453
+ this._emitAutonomyTelemetry({
5454
+ type: AUTONOMY_TELEMETRY_EVENT_TYPES.evidenceBundle,
5455
+ timestamp: new Date().toISOString(),
5456
+ payload: {
5457
+ laneId: record.laneId,
5458
+ laneType: record.type,
5459
+ status: record.status,
5460
+ reasonCode: record.reasonCode ?? null,
5461
+ costUsd: record.costUsd ?? null,
5462
+ hasEvidence: record.evidenceEntryId !== undefined,
5463
+ },
5464
+ });
5349
5465
  }
5350
5466
  return { started: true, record, result };
5351
5467
  }
@@ -5356,6 +5472,18 @@ export class AgentSession {
5356
5472
  });
5357
5473
  if (record && !this._disposed) {
5358
5474
  appendLaneRecordSnapshot(this.sessionManager, record);
5475
+ this._emitAutonomyTelemetry({
5476
+ type: AUTONOMY_TELEMETRY_EVENT_TYPES.evidenceBundle,
5477
+ timestamp: new Date().toISOString(),
5478
+ payload: {
5479
+ laneId: record.laneId,
5480
+ laneType: record.type,
5481
+ status: record.status,
5482
+ reasonCode: record.reasonCode ?? null,
5483
+ costUsd: record.costUsd ?? null,
5484
+ hasEvidence: record.evidenceEntryId !== undefined,
5485
+ },
5486
+ });
5359
5487
  }
5360
5488
  const message = error instanceof Error ? error.message : String(error);
5361
5489
  this._emit({ type: "warning", message: `Research lane failed: ${message}` });
@@ -5458,7 +5586,7 @@ export class AgentSession {
5458
5586
  });
5459
5587
  return { started: true, record, outcome };
5460
5588
  }
5461
- this.saveWorkerResultSnapshot(outcome.result);
5589
+ this.saveWorkerResultSnapshot(outcome.result, workerRequest);
5462
5590
  if (spentUsage && (spentUsage.cost.total > 0 || spentUsage.totalTokens > 0)) {
5463
5591
  this.addSpawnedUsage(spentUsage, { label: "worker-delegation", reportId: usageReportId });
5464
5592
  }
@@ -5469,6 +5597,19 @@ export class AgentSession {
5469
5597
  });
5470
5598
  if (record) {
5471
5599
  appendLaneRecordSnapshot(this.sessionManager, record);
5600
+ // G3: worker lane terminal record -> worker_result event. Lane outcome only
5601
+ // (status/reasonCode/cost) — never the worker's summary/changed-file text.
5602
+ this._emitAutonomyTelemetry({
5603
+ type: AUTONOMY_TELEMETRY_EVENT_TYPES.workerResult,
5604
+ timestamp: new Date().toISOString(),
5605
+ payload: {
5606
+ laneId: record.laneId,
5607
+ laneType: record.type,
5608
+ status: record.status,
5609
+ reasonCode: record.reasonCode ?? null,
5610
+ costUsd: record.costUsd ?? null,
5611
+ },
5612
+ });
5472
5613
  }
5473
5614
  return { started: true, record, outcome };
5474
5615
  }
@@ -5479,6 +5620,17 @@ export class AgentSession {
5479
5620
  });
5480
5621
  if (record && !this._disposed) {
5481
5622
  appendLaneRecordSnapshot(this.sessionManager, record);
5623
+ this._emitAutonomyTelemetry({
5624
+ type: AUTONOMY_TELEMETRY_EVENT_TYPES.workerResult,
5625
+ timestamp: new Date().toISOString(),
5626
+ payload: {
5627
+ laneId: record.laneId,
5628
+ laneType: record.type,
5629
+ status: record.status,
5630
+ reasonCode: record.reasonCode ?? null,
5631
+ costUsd: record.costUsd ?? null,
5632
+ },
5633
+ });
5482
5634
  }
5483
5635
  const message = error instanceof Error ? error.message : String(error);
5484
5636
  this._emit({ type: "warning", message: `Worker delegation failed: ${message}` });
@@ -5750,17 +5902,32 @@ export class AgentSession {
5750
5902
  // every pass, so advancing it for a no-op (which stores nothing) would make later passes
5751
5903
  // reuse ids — and rollback keys on the id, so a collision blocks or misdirects rollback.
5752
5904
  let auditSequence = getLearningAuditSnapshots(this.sessionManager.getEntries()).length;
5905
+ // G6 evidence strength: durable proposals accumulate observation counts across passes/sessions
5906
+ // so the gate can distinguish a one-off cue from a repeatedly-confirmed lesson. Built once per
5907
+ // pass; every increment is best-effort (store IO must never break reflection).
5908
+ const observationStore = ObservationStore.forAgentDir(this._agentDir);
5753
5909
  let writeIndex = 0;
5754
5910
  for (const write of result.writes) {
5755
5911
  writeIndex += 1;
5756
5912
  const proposalId = `${input.reportId ?? "reflection"}-w${writeIndex}`;
5757
5913
  const proposal = proposalFromReflectionWrite(write, proposalId);
5758
5914
  const rollback = rollbackPlanForReflectionWrite(write);
5915
+ let observations = 1;
5916
+ if (policy.enabled) {
5917
+ try {
5918
+ observations = observationStore.increment(observationKey(proposal.layer, proposal.summary));
5919
+ }
5920
+ catch {
5921
+ // A store read/write failure falls back to a fresh count of 1, which keeps the gate
5922
+ // proposal-first (never spuriously auto-applies) rather than crashing the pass.
5923
+ observations = 1;
5924
+ }
5925
+ }
5759
5926
  const decision = policy.enabled
5760
5927
  ? evaluateLearningDecision({
5761
5928
  proposal,
5762
5929
  confidence: policy.reflectionSourceConfidence,
5763
- observations: 1,
5930
+ observations,
5764
5931
  contradictions: 0,
5765
5932
  settings: {
5766
5933
  enabled: true,
@@ -5779,6 +5946,18 @@ export class AgentSession {
5779
5946
  requiresApproval: false,
5780
5947
  };
5781
5948
  this.saveLearningDecisionSnapshot(decision);
5949
+ // G3: learning-gate outcome. Codes/numbers only — never the proposal summary/memory text.
5950
+ this._emitAutonomyTelemetry({
5951
+ type: AUTONOMY_TELEMETRY_EVENT_TYPES.learningDecision,
5952
+ timestamp: new Date().toISOString(),
5953
+ payload: {
5954
+ kind: decision.kind,
5955
+ reasonCode: decision.reasonCode,
5956
+ layer: proposal.layer,
5957
+ confidence: decision.confidence,
5958
+ requiresApproval: decision.requiresApproval,
5959
+ },
5960
+ });
5782
5961
  if (decision.kind === "apply") {
5783
5962
  await this._applyReflectionWrite(write, signal);
5784
5963
  }