@ouro.bot/cli 0.1.0-alpha.767 → 0.1.0-alpha.769

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.
@@ -45,6 +45,8 @@ exports.claimExternalEvent = claimExternalEvent;
45
45
  exports.renewExternalEventClaim = renewExternalEventClaim;
46
46
  exports.commitExternalEventDisposition = commitExternalEventDisposition;
47
47
  exports.failExternalEventAttempt = failExternalEventAttempt;
48
+ exports.externalEventRecoveryFailure = externalEventRecoveryFailure;
49
+ exports.reviveExternalEventAfterRecovery = reviveExternalEventAfterRecovery;
48
50
  exports.reconcileExternalEvent = reconcileExternalEvent;
49
51
  exports.advanceExternalEventFromAwait = advanceExternalEventFromAwait;
50
52
  exports.advanceExternalEventsFromAwait = advanceExternalEventsFromAwait;
@@ -305,11 +307,19 @@ function isRecord(value) {
305
307
  if (!value || typeof value !== "object" || Array.isArray(value))
306
308
  return false;
307
309
  const candidate = value;
310
+ const failure = candidate.failureProvenance;
311
+ const grant = candidate.recoveryGrant;
312
+ const validFailure = failure === undefined || (failure !== null && typeof failure === "object"
313
+ && (failure.class === "provider_lane_unavailable" || failure.class === "managed_runtime_unavailable") && canonicalIso(failure.failedAt));
314
+ const validGrant = grant === undefined || (grant !== null && typeof grant === "object"
315
+ && Number.isSafeInteger(grant.generation) && grant.generation === candidate.generation && canonicalIso(grant.consumedAt));
308
316
  return candidate.schemaVersion === 2
309
317
  && typeof candidate.recordPath === "string"
310
318
  && Number.isSafeInteger(candidate.version)
311
319
  && Number.isSafeInteger(candidate.generation)
312
- && typeof candidate.observationRevision === "string";
320
+ && typeof candidate.observationRevision === "string"
321
+ && validFailure
322
+ && validGrant;
313
323
  }
314
324
  function readExternalEventRecord(recordPath) {
315
325
  const parsed = JSON.parse(fs.readFileSync(recordPath, "utf8"));
@@ -359,6 +369,8 @@ function listExternalEventStatus(root) {
359
369
  careId: record.disposition?.careId ?? null,
360
370
  awaitId: record.disposition?.awaitId ?? null,
361
371
  lastError: record.lastError,
372
+ failureProvenance: record.failureProvenance ?? null,
373
+ recoveryGrant: record.recoveryGrant ?? null,
362
374
  nextAttemptAt: record.nextAttemptAt,
363
375
  claimOwner: record.claimOwner,
364
376
  claimExpiresAt: record.claimExpiresAt,
@@ -390,6 +402,8 @@ function listExternalEventStatus(root) {
390
402
  careId: null,
391
403
  awaitId: null,
392
404
  lastError: `invalid receipt: ${error instanceof Error ? error.message : /* v8 ignore next -- filesystem and JSON parsers throw Error objects @preserve */ String(error)}`,
405
+ failureProvenance: null,
406
+ recoveryGrant: null,
393
407
  nextAttemptAt: null,
394
408
  claimOwner: null,
395
409
  claimExpiresAt: null,
@@ -533,6 +547,8 @@ function recordExternalEventInternal(input, options = {}) {
533
547
  claimExpiresAt: shouldWake || quietInitialReceipt ? null : existing.claimExpiresAt,
534
548
  nextAttemptAt: shouldWake || quietInitialReceipt ? null : existing.nextAttemptAt,
535
549
  lastError: shouldWake || quietInitialReceipt ? null : existing.lastError,
550
+ ...(!shouldWake && !quietInitialReceipt && existing?.failureProvenance ? { failureProvenance: existing.failureProvenance } : {}),
551
+ ...(!shouldWake && !quietInitialReceipt && existing?.recoveryGrant ? { recoveryGrant: existing.recoveryGrant } : {}),
536
552
  disposition: shouldWake || quietInitialReceipt ? null : existing.disposition,
537
553
  pendingObservation: shouldWake || quietInitialReceipt ? null : existing.pendingObservation,
538
554
  dispatchEnabled: options.dispatchEnabled ?? existing?.dispatchEnabled ?? true,
@@ -1032,12 +1048,13 @@ function commitExternalEventDisposition(recordPath, input) {
1032
1048
  disposition: wakePending ? null : input.disposition,
1033
1049
  pendingObservation: null,
1034
1050
  pendingPrivilegedProtectiveAction: undefined,
1051
+ ...(wakePending ? { failureProvenance: undefined, recoveryGrant: undefined } : {}),
1035
1052
  shouldWake: wakePending,
1036
1053
  }, now);
1037
1054
  });
1038
1055
  }
1039
- function retryState(record, now, maxAttempts, baseDelayMs, error) {
1040
- const dead = record.attemptCount >= maxAttempts;
1056
+ function retryState(record, now, maxAttempts, baseDelayMs, error, failureClass) {
1057
+ const dead = record.recoveryGrant?.generation === record.generation || record.attemptCount >= maxAttempts;
1041
1058
  return {
1042
1059
  ...record,
1043
1060
  executionState: dead ? "dead_letter" : "retry_wait",
@@ -1045,6 +1062,7 @@ function retryState(record, now, maxAttempts, baseDelayMs, error) {
1045
1062
  claimExpiresAt: null,
1046
1063
  nextAttemptAt: dead ? null : new Date(Date.parse(now) + baseDelayMs * 2 ** Math.max(0, record.attemptCount - 1)).toISOString(),
1047
1064
  lastError: error.slice(0, 1_000),
1065
+ ...(dead && failureClass && !record.failureProvenance ? { failureProvenance: { class: failureClass, failedAt: now } } : {}),
1048
1066
  shouldWake: false,
1049
1067
  };
1050
1068
  }
@@ -1055,9 +1073,73 @@ function failExternalEventAttempt(recordPath, input) {
1055
1073
  const now = input.now?.() ?? new Date().toISOString();
1056
1074
  const maxAttempts = input.maxAttempts ?? 5;
1057
1075
  const baseDelayMs = input.baseDelayMs ?? 1_000;
1076
+ if (input.failureClass !== undefined && input.failureClass !== "provider_lane_unavailable" && input.failureClass !== "managed_runtime_unavailable")
1077
+ throw new Error("External event failure class is invalid");
1058
1078
  if (!Number.isSafeInteger(maxAttempts) || maxAttempts < 1 || !Number.isSafeInteger(baseDelayMs) || baseDelayMs < 1)
1059
1079
  throw new Error("External event retry policy is invalid");
1060
- return commitMutation(recordPath, retryState(record, now, maxAttempts, baseDelayMs, input.error), now);
1080
+ return commitMutation(recordPath, retryState(record, now, maxAttempts, baseDelayMs, input.error, input.failureClass), now);
1081
+ });
1082
+ }
1083
+ function exactLegacyProviderFailure(record) {
1084
+ const escapedAgent = record.agent.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
1085
+ const canonical = new RegExp(`^private-runtime wake denied for ${escapedAgent}: provider lane resolution failed$`, "u");
1086
+ return record.lastError && canonical.test(record.lastError)
1087
+ ? { class: "provider_lane_unavailable", failedAt: record.updatedAt }
1088
+ : null;
1089
+ }
1090
+ function externalEventRecoveryFailure(record) {
1091
+ return record.failureProvenance ?? exactLegacyProviderFailure(record);
1092
+ }
1093
+ function recoveryIneligible(record, reason) {
1094
+ (0, runtime_1.emitNervesEvent)({
1095
+ component: "daemon",
1096
+ event: "daemon.external_event_recovery_ineligible",
1097
+ message: "external event dead-letter recovery was ineligible or exhausted",
1098
+ meta: { agent: record.agent, source: record.source, eventId: record.eventId, generation: record.generation, reason },
1099
+ });
1100
+ return { revived: false, reason, record };
1101
+ }
1102
+ function reviveExternalEventAfterRecovery(recordPath, input) {
1103
+ return withRecordLock(recordPath, () => {
1104
+ const record = readExternalEventRecord(recordPath);
1105
+ assertCas(record, input);
1106
+ if (input.evidence.class !== "provider_lane_unavailable" && input.evidence.class !== "managed_runtime_unavailable")
1107
+ throw new Error("External event recovery evidence class is invalid");
1108
+ if (!canonicalIso(input.evidence.observedAt))
1109
+ throw new Error("External event recovery evidence time is invalid");
1110
+ if (record.executionState !== "dead_letter")
1111
+ return recoveryIneligible(record, "not_dead_letter");
1112
+ if (record.dispatchEnabled === false)
1113
+ return recoveryIneligible(record, "dispatch_disabled");
1114
+ if (record.recoveryGrant?.generation === record.generation)
1115
+ return recoveryIneligible(record, "grant_consumed");
1116
+ const failure = externalEventRecoveryFailure(record);
1117
+ if (!failure)
1118
+ return recoveryIneligible(record, "ineligible_failure");
1119
+ if (failure.class !== input.evidence.class)
1120
+ return recoveryIneligible(record, "evidence_mismatch");
1121
+ if (Date.parse(input.evidence.observedAt) <= Date.parse(failure.failedAt))
1122
+ return recoveryIneligible(record, "stale_evidence");
1123
+ const now = input.now?.() ?? new Date().toISOString();
1124
+ if (!canonicalIso(now))
1125
+ throw new Error("External event recovery grant consumed time is invalid");
1126
+ const revived = commitMutation(recordPath, {
1127
+ ...record,
1128
+ executionState: "queued",
1129
+ claimOwner: null,
1130
+ claimExpiresAt: null,
1131
+ nextAttemptAt: null,
1132
+ shouldWake: true,
1133
+ failureProvenance: failure,
1134
+ recoveryGrant: { generation: record.generation, consumedAt: now },
1135
+ }, now);
1136
+ (0, runtime_1.emitNervesEvent)({
1137
+ component: "daemon",
1138
+ event: "daemon.external_event_requeued",
1139
+ message: "requeued external event after infrastructure recovery",
1140
+ meta: { agent: revived.agent, source: revived.source, eventId: revived.eventId, generation: revived.generation, failureClass: failure.class },
1141
+ });
1142
+ return { revived: true, record: revived };
1061
1143
  });
1062
1144
  }
1063
1145
  function reconcileExternalEvent(recordPath, options = {}) {
@@ -1084,6 +1166,8 @@ function advanceExternalEventFromAwait(recordPath, input) {
1084
1166
  generation: record.generation + 1,
1085
1167
  executionState: "queued",
1086
1168
  attemptCount: 0,
1169
+ failureProvenance: undefined,
1170
+ recoveryGrant: undefined,
1087
1171
  disposition: null,
1088
1172
  shouldWake: true,
1089
1173
  }, now);
@@ -57,6 +57,7 @@ function sanitizePrivateDecision(row, ledgerPath) {
57
57
  ? row
58
58
  : {};
59
59
  const deniedReason = stringField(record.deniedReason);
60
+ const denialCode = record.denialCode === "provider_lane_unavailable" ? record.denialCode : undefined;
60
61
  const duplicateOf = stringField(record.duplicateOf);
61
62
  const error = stringField(record.error);
62
63
  return {
@@ -76,6 +77,7 @@ function sanitizePrivateDecision(row, ledgerPath) {
76
77
  decidedAt: stringField(record.decidedAt),
77
78
  ledgerLocator: ledgerLocatorField(record.ledgerLocator, ledgerPath),
78
79
  ...(deniedReason ? { deniedReason } : {}),
80
+ ...(denialCode ? { denialCode } : {}),
79
81
  ...(duplicateOf ? { duplicateOf } : {}),
80
82
  ...(error ? { error } : {}),
81
83
  };
@@ -137,6 +137,7 @@ function mismatchDecision(candidate, existing) {
137
137
  result: "deny",
138
138
  executable: false,
139
139
  deniedReason: "idempotency-key fingerprint mismatch",
140
+ denialCode: undefined,
140
141
  duplicateOf: existing.receiptId,
141
142
  };
142
143
  }
@@ -159,6 +160,7 @@ function ledgerWriteFailedDecision(candidate, ledgerPath, error) {
159
160
  result: "deny",
160
161
  executable: false,
161
162
  deniedReason: "ledger write failed",
163
+ denialCode: undefined,
162
164
  ledgerLocator: { path: ledgerPath },
163
165
  error: String(error),
164
166
  };
@@ -194,7 +196,8 @@ function recordPrivateTurnDecision(decision, deps = {}) {
194
196
  if (latestSameFingerprint
195
197
  && latestSameFingerprint.result === candidate.result
196
198
  && latestSameFingerprint.executable === candidate.executable
197
- && latestSameFingerprint.deniedReason === candidate.deniedReason) {
199
+ && latestSameFingerprint.deniedReason === candidate.deniedReason
200
+ && latestSameFingerprint.denialCode === candidate.denialCode) {
198
201
  return latestSameFingerprint;
199
202
  }
200
203
  if (priorExecutable && !candidate.executable) {
@@ -167,12 +167,6 @@ async function requestPrivateTurnDecision(request, deps = {}) {
167
167
  let evaluation;
168
168
  try {
169
169
  providerLane = await resolveProviderLaneMetadata(normalizedRequest, deps);
170
- requestFingerprint = createPrivateTurnRequestFingerprint(normalizedRequest, providerLane);
171
- evaluation = await evaluatePolicy(normalizedRequest, {
172
- requestFingerprint,
173
- idempotencyKey,
174
- providerLane,
175
- }, deps);
176
170
  }
177
171
  catch (error) {
178
172
  providerLane = {
@@ -181,13 +175,30 @@ async function requestPrivateTurnDecision(request, deps = {}) {
181
175
  model: "-",
182
176
  source: "agent.json",
183
177
  };
184
- requestFingerprint = createPrivateTurnRequestFingerprint(normalizedRequest, providerLane);
185
178
  evaluation = {
186
179
  result: "deny",
187
180
  reason: error instanceof Error ? error.message : String(error),
188
181
  deniedReason: "provider lane resolution failed",
182
+ denialCode: "provider_lane_unavailable",
189
183
  };
190
184
  }
185
+ requestFingerprint = createPrivateTurnRequestFingerprint(normalizedRequest, providerLane);
186
+ if (evaluation === undefined) {
187
+ try {
188
+ evaluation = await evaluatePolicy(normalizedRequest, {
189
+ requestFingerprint,
190
+ idempotencyKey,
191
+ providerLane,
192
+ }, deps);
193
+ }
194
+ catch (error) {
195
+ evaluation = {
196
+ result: "deny",
197
+ reason: error instanceof Error ? error.message : String(error),
198
+ deniedReason: "private runtime policy evaluation failed",
199
+ };
200
+ }
201
+ }
191
202
  const result = evaluation.result;
192
203
  emitPolicyEvaluated(deps, { request: normalizedRequest, result, requestFingerprint, idempotencyKey });
193
204
  const reason = evaluation.reason ?? normalizedRequest.reason;
@@ -208,7 +219,10 @@ async function requestPrivateTurnDecision(request, deps = {}) {
208
219
  executable: result === "allow",
209
220
  decidedAt: nowIso(deps),
210
221
  ledgerLocator: { path: deps.ledgerPath ?? "" },
211
- ...(result === "deny" ? { deniedReason: evaluation.deniedReason ?? reason } : {}),
222
+ ...(result === "deny" ? {
223
+ deniedReason: evaluation.deniedReason ?? reason,
224
+ ...(evaluation.denialCode ? { denialCode: evaluation.denialCode } : {}),
225
+ } : {}),
212
226
  };
213
227
  return (0, ledger_1.recordPrivateTurnDecision)(decision, deps);
214
228
  }
@@ -42,6 +42,7 @@ const path = __importStar(require("path"));
42
42
  const runtime_1 = require("../nerves/runtime");
43
43
  const bundle_state_1 = require("./bundle-state");
44
44
  const tempo_1 = require("./tempo");
45
+ const cares_1 = require("../arc/cares");
45
46
  const flight_recorder_1 = require("../arc/flight-recorder");
46
47
  const context_loss_sentinel_1 = require("./context-loss-sentinel");
47
48
  const orientation_frame_1 = require("./orientation-frame");
@@ -82,11 +83,14 @@ function buildObligationsSection(obligations) {
82
83
  })
83
84
  .join("\n");
84
85
  }
85
- function buildCaresSection(cares) {
86
+ function buildCaresSection(cares, now) {
86
87
  if (cares.length === 0)
87
88
  return "";
88
89
  return cares
89
- .map((c) => {
90
+ .map((care) => {
91
+ const c = (0, cares_1.projectCareEvidence)(care, now);
92
+ if ("recheckRequired" in c)
93
+ return `- system care [${c.salience}] evidence stale; recheck required`;
90
94
  const parts = [`- ${c.label}`];
91
95
  if (c.salience !== "low") {
92
96
  parts.push(` [${c.salience}]`);
@@ -192,7 +196,7 @@ function buildStartOfTurnPacket(view, opts) {
192
196
  const packet = {
193
197
  plotLine: buildPlotLine(view.recentEpisodes, tempo),
194
198
  obligations: buildObligationsSection(effectiveObligations),
195
- cares: buildCaresSection(view.activeCares),
199
+ cares: buildCaresSection(view.activeCares, opts?.careEvidenceNow ?? Date.now()),
196
200
  presence: buildPresenceSection(view.peerPresence),
197
201
  arcResume: opts?.flightRecorderResume ? (0, flight_recorder_1.formatFlightRecorderResume)(opts.flightRecorderResume) : undefined,
198
202
  recoverySentinel: opts?.recoverySentinel,
@@ -45,24 +45,6 @@ const presence_1 = require("../arc/presence");
45
45
  const intentions_1 = require("../arc/intentions");
46
46
  const steward_policy_1 = require("../heart/steward-policy");
47
47
  const await_parser_1 = require("../heart/awaiting/await-parser");
48
- function presentCare(care) {
49
- const staleAt = care.nextCheckAt ? Date.parse(care.nextCheckAt) : Number.NaN;
50
- if (care.kind !== "system" || !["active", "watching"].includes(care.status) || care.nextCheckAt === null)
51
- return care;
52
- if (Number.isFinite(staleAt) && staleAt >= Date.now())
53
- return care;
54
- return {
55
- id: care.id,
56
- kind: care.kind,
57
- status: care.status,
58
- salience: care.salience,
59
- steward: care.steward,
60
- evidenceStatus: "stale",
61
- recheckRequired: true,
62
- staleAt: care.nextCheckAt,
63
- lastAssessedAt: care.updatedAt,
64
- };
65
- }
66
48
  exports.continuityToolDefinitions = [
67
49
  // ── Continuity tools ──────────────────────────────────────────────
68
50
  {
@@ -326,7 +308,8 @@ exports.continuityToolDefinitions = [
326
308
  },
327
309
  handler: (a) => {
328
310
  const agentRoot = (0, identity_1.getAgentRoot)();
329
- const cares = (a.status === "all" ? (0, cares_1.readCares)(agentRoot) : (0, cares_1.readActiveCares)(agentRoot)).map(presentCare);
311
+ const now = Date.now();
312
+ const cares = (a.status === "all" ? (0, cares_1.readCares)(agentRoot) : (0, cares_1.readActiveCares)(agentRoot)).map((care) => (0, cares_1.projectCareEvidence)(care, now));
330
313
  (0, runtime_1.emitNervesEvent)({ component: "repertoire", event: "repertoire.query_cares", message: `queried ${cares.length} cares`, meta: { count: cares.length } });
331
314
  return JSON.stringify(cares, null, 2);
332
315
  },
@@ -346,6 +329,7 @@ exports.continuityToolDefinitions = [
346
329
  why: { type: "string", description: "Why this matters" },
347
330
  salience: { type: "string", description: "low, medium, high, or critical" },
348
331
  kind: { type: "string", description: "person, agent, project, mission, or system" },
332
+ status: { type: "string", enum: ["active", "watching", "resolved", "dormant"], description: "active, watching, resolved, or dormant" },
349
333
  stewardship: { type: "string", description: "mine, shared, or delegated" },
350
334
  source: { type: "string", description: "Machine evidence source for an incident binding" },
351
335
  incidentKey: { type: "string", description: "Stable incident key within the source" },
@@ -367,7 +351,7 @@ exports.continuityToolDefinitions = [
367
351
  label: a.label ?? "untitled",
368
352
  why: a.why ?? "",
369
353
  kind: a.kind ?? "project",
370
- status: "active",
354
+ status: a.status ?? "active",
371
355
  salience: a.salience ?? "medium",
372
356
  steward: a.stewardship ?? "mine",
373
357
  relatedFriendIds: [],
@@ -412,22 +396,33 @@ exports.continuityToolDefinitions = [
412
396
  source: a.source,
413
397
  incidentKey: a.incidentKey,
414
398
  expectedUpdatedAt: a.expectedUpdatedAt,
399
+ ...((a.label !== undefined || a.why !== undefined || a.currentRisk !== undefined || a.nextCheckAt !== undefined) ? {
400
+ display: {
401
+ ...(a.label !== undefined ? { label: String(a.label) } : {}),
402
+ ...(a.why !== undefined ? { why: String(a.why) } : {}),
403
+ ...(a.currentRisk !== undefined ? { currentRisk: a.currentRisk ? String(a.currentRisk) : null } : {}),
404
+ ...(a.nextCheckAt !== undefined ? { nextCheckAt: a.nextCheckAt ? String(a.nextCheckAt) : null } : {}),
405
+ },
406
+ } : {}),
415
407
  });
416
408
  }
417
409
  else if (a.action === "upsert_incident") {
418
410
  result = (0, cares_1.upsertCareForIncident)(agentRoot, {
419
- label: a.label ?? "untitled",
420
- why: a.why ?? "",
421
- kind: a.kind ?? "system",
422
- status: "active",
423
- salience: a.salience ?? "medium",
424
- steward: a.stewardship ?? "mine",
411
+ ...(a.id ? { id: String(a.id) } : {}),
412
+ ...(!a.id ? {
413
+ label: a.label ?? "untitled", why: a.why ?? "", kind: a.kind ?? "system", status: a.status ?? "active",
414
+ salience: a.salience ?? "medium", steward: a.stewardship ?? "mine",
415
+ } : {
416
+ ...(a.label !== undefined ? { label: String(a.label) } : {}), ...(a.why !== undefined ? { why: String(a.why) } : {}),
417
+ ...(a.kind !== undefined ? { kind: a.kind } : {}), ...(a.status !== undefined ? { status: a.status } : {}),
418
+ ...(a.salience !== undefined ? { salience: a.salience } : {}), ...(a.stewardship !== undefined ? { steward: a.stewardship } : {}),
419
+ }),
425
420
  relatedFriendIds: [],
426
421
  relatedAgentIds: [],
427
422
  relatedObligationIds: [],
428
423
  relatedEpisodeIds: [],
429
- currentRisk: a.currentRisk ? String(a.currentRisk) : null,
430
- nextCheckAt: a.nextCheckAt ? String(a.nextCheckAt) : null,
424
+ ...(!a.id || a.currentRisk !== undefined ? { currentRisk: a.currentRisk ? String(a.currentRisk) : null } : {}),
425
+ ...(!a.id || a.nextCheckAt !== undefined ? { nextCheckAt: a.nextCheckAt ? String(a.nextCheckAt) : null } : {}),
431
426
  ...(a.expectedUpdatedAt ? { expectedUpdatedAt: String(a.expectedUpdatedAt) } : {}),
432
427
  incident: {
433
428
  source: a.source,
@@ -72,6 +72,7 @@ const provider_visibility_1 = require("../heart/provider-visibility");
72
72
  const orientation_frame_1 = require("../heart/orientation-frame");
73
73
  const flight_recorder_1 = require("../arc/flight-recorder");
74
74
  const context_loss_sentinel_1 = require("../heart/context-loss-sentinel");
75
+ const cares_1 = require("../arc/cares");
75
76
  const VOICE_PENDING_MAX_AGE_MS = 15 * 60 * 1_000;
76
77
  function pendingExpirationReason(channel, message, now) {
77
78
  /* v8 ignore start -- pending expiry edge permutations are covered by the stale voice queue tests; this helper keeps defensive non-voice fallbacks @preserve */
@@ -809,11 +810,14 @@ async function handleInboundTurn(input) {
809
810
  : undefined);
810
811
  // Step 4b: Continuity pipeline — derive tempo, build start-of-turn packet, snapshot obligations
811
812
  let renderedStartOfTurnPacket;
813
+ const careEvidenceNow = Date.now();
814
+ const activeCaresSnapshot = ctx.activeCares;
812
815
  const preTurnObligationIds = new Set(pendingObligations.map((ob) => `${ob.id}:${ob.status}`));
813
816
  try {
814
817
  const agentRoot = (0, identity_1.getAgentRoot)();
815
818
  const agentName = (0, identity_1.getAgentName)();
816
- const { recentEpisodes, activeCares } = ctx;
819
+ const { recentEpisodes } = ctx;
820
+ const projectedCares = activeCaresSnapshot.map((care) => (0, cares_1.projectCareEvidence)(care, careEvidenceNow));
817
821
  const tempoState = (0, tempo_1.deriveTempo)({
818
822
  activeSessions: sessionActivity.length + 1,
819
823
  openObligations: pendingObligations.length,
@@ -823,15 +827,15 @@ async function handleInboundTurn(input) {
823
827
  : 0,
824
828
  hasBlockers: false, // obligations use specific statuses, not "blocked"
825
829
  highSalienceEpisodes: recentEpisodes.filter((ep) => ep.salience === "high" || ep.salience === "critical").length,
826
- activeCareCount: activeCares.length,
827
- atRiskCareCount: activeCares.filter((c) => c.currentRisk != null).length,
830
+ activeCareCount: activeCaresSnapshot.length,
831
+ atRiskCareCount: projectedCares.filter((care) => !("recheckRequired" in care) && care.currentRisk != null).length,
828
832
  });
829
833
  const temporalView = (0, temporal_view_1.buildTemporalView)(agentRoot, {
830
834
  tempo: tempoState.mode,
831
835
  preloaded: {
832
836
  recentEpisodes,
833
837
  activeObligations: pendingObligations,
834
- activeCares,
838
+ activeCares: activeCaresSnapshot,
835
839
  },
836
840
  });
837
841
  const startOfTurnPacket = (0, start_of_turn_packet_1.buildStartOfTurnPacket)(temporalView, {
@@ -843,6 +847,7 @@ async function handleInboundTurn(input) {
843
847
  friendContactTiming,
844
848
  flightRecorderResume: ctx.flightRecorderResume,
845
849
  recoverySentinel: ctx.recoverySentinel,
850
+ careEvidenceNow,
846
851
  });
847
852
  /* v8 ignore next 3 -- syncFailure propagation tested in sync.test.ts @preserve */
848
853
  if (syncFailure) {
@@ -928,6 +933,8 @@ async function handleInboundTurn(input) {
928
933
  currentUserMessages,
929
934
  resolvedContext,
930
935
  runAgentOptions,
936
+ activeCares: activeCaresSnapshot,
937
+ careEvidenceNow,
931
938
  }));
932
939
  const checkpointCurrentAsk = selectCheckpointCurrentAsk({
933
940
  currentUserMessage: runAgentOptions.toolContext?.currentUserMessage ?? currentUserMessage,