@ouro.bot/cli 0.1.0-alpha.732 → 0.1.0-alpha.734

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 (96) hide show
  1. package/README.md +1 -1
  2. package/changelog.json +34 -0
  3. package/deploy/unraid/Dockerfile +27 -0
  4. package/deploy/unraid/README.txt +1368 -0
  5. package/deploy/unraid/audit-container-spec.sh +4 -0
  6. package/deploy/unraid/container-runtime.json +1 -0
  7. package/deploy/unraid/sanctuary-acceptance-adapter.sh +37 -0
  8. package/deploy/unraid/sanctuary-acceptance-contract.json +200 -0
  9. package/deploy/unraid/sanctuary-acceptance-harness.sh +23 -0
  10. package/deploy/unraid/sanctuary-deployment-target.mjs +705 -0
  11. package/deploy/unraid/sanctuary-safe-rename.py +24 -0
  12. package/deploy/unraid/sanctuary-unit16-host-broker.mjs +1777 -0
  13. package/deploy/unraid/sanctuary-unit16-run.sh +502 -0
  14. package/deploy/unraid/sanctuary-unit18-target-audit.sh +27 -0
  15. package/deploy/unraid/sanctuary.ouro/agent.json +24 -0
  16. package/deploy/unraid/sanctuary.ouro/arc/README.md +3 -0
  17. package/deploy/unraid/sanctuary.ouro/bundle-meta.json +5 -0
  18. package/deploy/unraid/sanctuary.ouro/habits/sanctuary-health.md +8 -0
  19. package/deploy/unraid/sanctuary.ouro/provider-readiness.json +18 -0
  20. package/deploy/unraid/sanctuary.ouro/psyche/ASPIRATIONS.md +3 -0
  21. package/deploy/unraid/sanctuary.ouro/psyche/IDENTITY.md +3 -0
  22. package/deploy/unraid/sanctuary.ouro/psyche/LORE.md +3 -0
  23. package/deploy/unraid/sanctuary.ouro/psyche/SOUL.md +5 -0
  24. package/deploy/unraid/sanctuary.ouro/psyche/TACIT.md +7 -0
  25. package/deploy/unraid/sanctuary.ouro/tool-profiles.json +21 -0
  26. package/deploy/unraid/sanctuary.xml +25 -0
  27. package/dist/heart/approval-files.js +113 -0
  28. package/dist/heart/approval-store.js +168 -9
  29. package/dist/heart/auth/auth-flow.js +17 -1
  30. package/dist/heart/autonomy-budget.js +44 -1
  31. package/dist/heart/awaiting/await-scheduler.js +6 -2
  32. package/dist/heart/config.js +8 -0
  33. package/dist/heart/core.js +117 -132
  34. package/dist/heart/daemon/cli-exec.js +74 -2
  35. package/dist/heart/daemon/cli-help.js +6 -1
  36. package/dist/heart/daemon/cli-parse.js +6 -4
  37. package/dist/heart/daemon/container-credential-bootstrap.js +185 -0
  38. package/dist/heart/daemon/container-healthcheck.js +94 -0
  39. package/dist/heart/daemon/container-runtime.js +84 -0
  40. package/dist/heart/daemon/container-spec-auditor-main.js +156 -0
  41. package/dist/heart/daemon/container-spec-auditor.js +156 -0
  42. package/dist/heart/daemon/daemon-bootstrap-startup.js +41 -0
  43. package/dist/heart/daemon/daemon-entry.js +116 -28
  44. package/dist/heart/daemon/daemon-health.js +10 -0
  45. package/dist/heart/daemon/daemon.js +92 -5
  46. package/dist/heart/daemon/doctor.js +56 -7
  47. package/dist/heart/daemon/runtime-logging.js +6 -0
  48. package/dist/heart/daemon/runtime-mode.js +13 -2
  49. package/dist/heart/daemon/sanctuary-acceptance-adapter.js +2365 -0
  50. package/dist/heart/daemon/sanctuary-acceptance-harness.js +1624 -0
  51. package/dist/heart/daemon/sanctuary-acceptance-marker.js +267 -0
  52. package/dist/heart/daemon/sanctuary-acceptance-scenarios.js +985 -0
  53. package/dist/heart/daemon/sanctuary-scheduler-liveness.js +227 -0
  54. package/dist/heart/daemon/sanctuary-scheduler-origin.js +126 -0
  55. package/dist/heart/daemon/sense-manager.js +25 -6
  56. package/dist/heart/daemon/supercronic-supervisor.js +267 -0
  57. package/dist/heart/habits/habit-scheduler.js +7 -3
  58. package/dist/heart/identity.js +13 -3
  59. package/dist/heart/provider-credentials.js +9 -1
  60. package/dist/heart/provider-models.js +8 -0
  61. package/dist/heart/provider-ping.js +5 -2
  62. package/dist/heart/providers/openai-compatible.js +177 -0
  63. package/dist/heart/runtime-credentials.js +217 -8
  64. package/dist/heart/sense-truth.js +2 -0
  65. package/dist/heart/tool-approval.js +1 -0
  66. package/dist/heart/turn-context.js +8 -0
  67. package/dist/mailroom/blob-store.js +97 -19
  68. package/dist/mailroom/cache-sync-cli.js +5 -1
  69. package/dist/mailroom/core.js +23 -8
  70. package/dist/mailroom/hosted-cache-sync.js +179 -44
  71. package/dist/mailroom/search-cache.js +109 -0
  72. package/dist/mind/prompt.js +9 -0
  73. package/dist/nerves/coverage/source-scanner.js +4 -4
  74. package/dist/nerves/index.js +75 -9
  75. package/dist/nerves/runtime.js +12 -0
  76. package/dist/repertoire/tool-arguments.js +39 -0
  77. package/dist/repertoire/tools-unraid.js +353 -0
  78. package/dist/repertoire/tools.js +19 -1
  79. package/dist/repertoire/unraid-client.js +155 -0
  80. package/dist/repertoire/unraid-restart.js +130 -0
  81. package/dist/senses/private-runtime-worker.js +3 -2
  82. package/dist/senses/sanctuary-grounding.js +118 -0
  83. package/dist/senses/sanctuary-health-acceptance-probe-entry.js +12 -0
  84. package/dist/senses/sanctuary-health-acceptance-probe.js +772 -0
  85. package/dist/senses/sanctuary-health-runner.js +146 -0
  86. package/dist/senses/sanctuary-health.js +447 -0
  87. package/dist/senses/sanctuary-interactive-control.js +306 -0
  88. package/dist/senses/sanctuary-runtime.js +206 -0
  89. package/dist/senses/shared-turn.js +11 -3
  90. package/dist/senses/telegram-approval-runtime.js +526 -0
  91. package/dist/senses/telegram-audit-ledger.js +220 -0
  92. package/dist/senses/telegram-client.js +1471 -0
  93. package/dist/senses/telegram-entry.js +80 -0
  94. package/dist/senses/telegram.js +1232 -0
  95. package/npm-shrinkwrap.json +6558 -0
  96. package/package.json +4 -2
@@ -0,0 +1,985 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.SANCTUARY_APPROVAL_TERMINAL_EDIT_TIMEOUT_MS = exports.SANCTUARY_APPROVAL_RECONCILIATION_JITTER_MS = exports.SANCTUARY_APPROVAL_TTL_MS = void 0;
37
+ exports.verifySanctuaryPostbootIntegrity = verifySanctuaryPostbootIntegrity;
38
+ exports.deriveSanctuaryScenarioAssertions = deriveSanctuaryScenarioAssertions;
39
+ exports.createSanctuaryScenarioCapture = createSanctuaryScenarioCapture;
40
+ exports.finalizeSanctuaryScenarioCapture = finalizeSanctuaryScenarioCapture;
41
+ const node_crypto_1 = require("node:crypto");
42
+ const fs = __importStar(require("node:fs"));
43
+ const path = __importStar(require("node:path"));
44
+ const runtime_1 = require("../../nerves/runtime");
45
+ const identity_1 = require("../identity");
46
+ const sanctuary_acceptance_marker_1 = require("./sanctuary-acceptance-marker");
47
+ const sanctuary_acceptance_harness_1 = require("./sanctuary-acceptance-harness");
48
+ const sanctuary_grounding_1 = require("../../senses/sanctuary-grounding");
49
+ const telegram_client_1 = require("../../senses/telegram-client");
50
+ const SHA256 = /^[0-9a-f]{64}$/u;
51
+ exports.SANCTUARY_APPROVAL_TTL_MS = telegram_client_1.TELEGRAM_APPROVAL_TTL_MS;
52
+ exports.SANCTUARY_APPROVAL_RECONCILIATION_JITTER_MS = 1_000;
53
+ exports.SANCTUARY_APPROVAL_TERMINAL_EDIT_TIMEOUT_MS = telegram_client_1.TELEGRAM_APPROVAL_TERMINAL_EDIT_TIMEOUT_MS;
54
+ const CANONICAL_RESTART_TARGET = "calibre-web";
55
+ function uniqueDigests(rows) {
56
+ return rows.every((row) => SHA256.test(row.idDigest)) && new Set(rows.map((row) => row.idDigest)).size === rows.length;
57
+ }
58
+ function preservedPrefix(before, after) {
59
+ return before.length <= after.length && before.every((row, index) => row.recordDigest === after[index]?.recordDigest);
60
+ }
61
+ function verifySanctuaryPostbootIntegrity(before, after, scenarioHandleDigest, options = {}) {
62
+ if (before.schemaVersion !== "sanctuary-postboot-integrity-v2" || after.schemaVersion !== before.schemaVersion || !SHA256.test(scenarioHandleDigest)
63
+ || before.activeScenarioHandleDigest !== null || after.activeScenarioHandleDigest !== scenarioHandleDigest)
64
+ return null;
65
+ if (!Number.isSafeInteger(before.telegramNextUpdateId) || !Number.isSafeInteger(after.telegramNextUpdateId)
66
+ || before.telegramNextUpdateId < 0 || after.telegramNextUpdateId < before.telegramNextUpdateId)
67
+ return null;
68
+ if (options.preserveCursors !== false && (JSON.stringify(before.approvalCheckpoints) !== JSON.stringify(after.approvalCheckpoints)
69
+ || before.approvalExecutionCount !== after.approvalExecutionCount || before.fingerprintDigest !== after.fingerprintDigest))
70
+ return null;
71
+ if (![before.approvalCheckpoints, after.approvalCheckpoints, before.sweeps, after.sweeps, before.deliveries, after.deliveries, before.audits, after.audits].every(uniqueDigests))
72
+ return null;
73
+ const rows = [...before.approvalCheckpoints, ...after.approvalCheckpoints, ...before.restartAttempts, ...after.restartAttempts,
74
+ ...before.sweeps, ...after.sweeps, ...before.deliveries, ...after.deliveries, ...before.audits, ...after.audits];
75
+ if (!rows.every((row) => SHA256.test(row.recordDigest)))
76
+ return null;
77
+ if (JSON.stringify(before.restartAttempts) !== JSON.stringify(after.restartAttempts) || !preservedPrefix(before.sweeps, after.sweeps)
78
+ || !preservedPrefix(before.deliveries, after.deliveries) || !preservedPrefix(before.audits, after.audits))
79
+ return null;
80
+ const executionCount = (snapshot) => new Set(snapshot.restartAttempts
81
+ .filter((row) => row.state !== "attempt_not_started").map((row) => row.idDigest)).size;
82
+ const validAttemptLifecycles = (snapshot) => {
83
+ const states = new Map();
84
+ for (const row of snapshot.restartAttempts)
85
+ states.set(row.idDigest, [...(states.get(row.idDigest) ?? []), row.state]);
86
+ return [...states.values()].every((sequence) => sequence.length === 3 && sequence[0] === "attempt_not_started"
87
+ && sequence[1] === "attempting" && (sequence[2] === "succeeded" || sequence[2] === "attempted_or_indeterminate"));
88
+ };
89
+ if (!validAttemptLifecycles(before) || !validAttemptLifecycles(after))
90
+ return null;
91
+ if (before.approvalExecutionCount !== executionCount(before) || after.approvalExecutionCount !== executionCount(after)
92
+ || after.approvalExecutionCount !== before.approvalExecutionCount)
93
+ return null;
94
+ const newSweeps = after.sweeps.slice(before.sweeps.length);
95
+ const newDeliveries = after.deliveries.slice(before.deliveries.length);
96
+ const newAudits = after.audits.slice(before.audits.length);
97
+ if (newSweeps.some((row) => row.scenarioHandleDigest !== scenarioHandleDigest))
98
+ return null;
99
+ const boundDeliveryIds = new Set(newSweeps.flatMap((row) => row.deliveryIdDigest ? [row.deliveryIdDigest] : []));
100
+ if (newDeliveries.some((row) => !boundDeliveryIds.has(row.idDigest)))
101
+ return null;
102
+ if (newAudits.some((row) => row.scenarioRelevant && row.scenarioHandleDigest !== scenarioHandleDigest))
103
+ return null;
104
+ return { preserved: true, sweepDeltaCount: newSweeps.length, deliveryDeltaCount: newDeliveries.length, auditDeltaCount: newAudits.length };
105
+ }
106
+ const HEALTH_SCENARIO_LABELS = new Set([
107
+ "unit-16f-cron-fingerprint",
108
+ "unit-16g-health-transition",
109
+ "unit-16h-daily-digest",
110
+ ]);
111
+ const INTERACTIVE_DRIVER_LABELS = new Set([
112
+ "unit-16l-duplicate-callback",
113
+ "unit-16m-restart-continuation",
114
+ ]);
115
+ const READ_ONLY_DENIAL_LABELS = new Set([
116
+ "unit-16e-1-stop-denial",
117
+ "unit-16e-2-restart-denial",
118
+ ]);
119
+ function hash(value) {
120
+ return (0, node_crypto_1.createHash)("sha256").update(JSON.stringify(value), "utf8").digest("hex");
121
+ }
122
+ const CONTAINMENT_READ_SCOPE = ["ARRAY", "DASHBOARD", "DISK", "DOCKER", "INFO", "LOGS", "NOTIFICATIONS", "SHARE", "VARS"]
123
+ .map((resource) => `${resource}:READ_ANY`).sort();
124
+ const CONTAINMENT_TELEGRAM_PROFILE = ["unraid_list_containers", "unraid_get_container_logs", "unraid_get_storage", "unraid_get_disks", "unraid_get_notifications", "unraid_get_system", "unraid_restart_container", "ponder", "settle", "speak"];
125
+ const CONTAINMENT_PRIVATE_PROFILE = ["send_message", "rest"];
126
+ const CONTAINMENT_EXCLUDED_TOOLS = ["shell", "read_file", "edit_file", "vault_get", "mcp_call", "exec", "credential_get"];
127
+ const CONTAINMENT_AUDIT_PATH = "/home/ouro/AgentBundles/sanctuary.ouro/state/acceptance/telegram-audit-chain.ndjson";
128
+ const CONTAINMENT_WRITE_POLICY = { kind: "required", policyId: "sanctuary.unraid.restart.v1", actionClass: "unraid.container.restart", requiresSoleCall: true };
129
+ const CONTAINMENT_TELEGRAM_SCHEMA_DIGEST = "3c66299a5f70ec82f8795cae47659284e6dbc691ef49002c2fb22edba76c59b6";
130
+ const CONTAINMENT_PRIVATE_SCHEMA_DIGEST = "61b137b2467acbcf22ca7443ee01e71ed970a62728c42aabffbdcb562f4a6a70";
131
+ function exactContainmentAudit(evidence) {
132
+ const digestFields = [evidence.keyInventoryDigest, evidence.telegramSchemaDigest, evidence.privateSchemaDigest, evidence.auditLedgerDigest];
133
+ return evidence.schemaVersion === "sanctuary-containment-audit-v1"
134
+ && evidence.keyCount === 2
135
+ && evidence.readScopeDigest === hash(CONTAINMENT_READ_SCOPE)
136
+ && evidence.writeScopeDigest === hash([...CONTAINMENT_READ_SCOPE, "DOCKER:UPDATE_ANY"].sort())
137
+ && evidence.keyRoleAssignmentCount === 0
138
+ && evidence.telegramToolCount === CONTAINMENT_TELEGRAM_PROFILE.length
139
+ && evidence.telegramProfileDigest === hash(CONTAINMENT_TELEGRAM_PROFILE)
140
+ && evidence.telegramSchemaDigest === CONTAINMENT_TELEGRAM_SCHEMA_DIGEST
141
+ && evidence.privateToolCount === CONTAINMENT_PRIVATE_PROFILE.length
142
+ && evidence.privateProfileDigest === hash(CONTAINMENT_PRIVATE_PROFILE)
143
+ && evidence.privateSchemaDigest === CONTAINMENT_PRIVATE_SCHEMA_DIGEST
144
+ && evidence.resolvedHandlerCount === CONTAINMENT_TELEGRAM_PROFILE.length + CONTAINMENT_PRIVATE_PROFILE.length
145
+ && evidence.excludedToolCount === CONTAINMENT_EXCLUDED_TOOLS.length
146
+ && evidence.excludedSchemaIntersectionCount === 0
147
+ && evidence.fabricatedHandlerInvocationCount === 0
148
+ && evidence.excludedToolAttemptCount === CONTAINMENT_EXCLUDED_TOOLS.length
149
+ && evidence.excludedToolRejectedCount === CONTAINMENT_EXCLUDED_TOOLS.length
150
+ && evidence.excludedToolInvokedCount === 0 && evidence.excludedToolSideEffectCount === 0
151
+ && evidence.globallyResolvableExcludedToolCount >= 1
152
+ && evidence.auditPathDigest === (0, node_crypto_1.createHash)("sha256").update(CONTAINMENT_AUDIT_PATH).digest("hex")
153
+ && evidence.auditRecordCount >= 2 && evidence.auditLifecyclePairCount >= 1
154
+ && evidence.containerUser === "10001:10001" && evidence.liveProcessUser === "10001:10001" && evidence.mountCount === 2 && evidence.publishedPortCount === 0
155
+ && evidence.networkMode === "host" && evidence.readOnlyRoot && evidence.mountsExact && evidence.securityExact && evidence.updaterDisabled
156
+ && !evidence.writableKeyExposure && evidence.rawWriteMaterialFieldCount === 0 && evidence.typedWriteExecutorCount === 1
157
+ && evidence.writeApprovalPolicyDigest === hash(CONTAINMENT_WRITE_POLICY) && !evidence.sensitiveMaterialObserved
158
+ && digestFields.every((value) => SHA256.test(value));
159
+ }
160
+ function atomicPrivateJson(filePath, value) {
161
+ fs.mkdirSync(path.dirname(filePath), { recursive: true, mode: 0o700 });
162
+ const temporary = `${filePath}.${process.pid}.tmp`;
163
+ fs.writeFileSync(temporary, `${JSON.stringify(value)}\n`, { encoding: "utf8", mode: 0o600, flag: "wx" });
164
+ fs.renameSync(temporary, filePath);
165
+ fs.chmodSync(filePath, 0o600);
166
+ }
167
+ function eventCount(facts, event) {
168
+ return facts.events.filter((entry) => entry.event === event).length;
169
+ }
170
+ function delta(after, before, event) {
171
+ return Math.max(0, eventCount(after, event) - eventCount(before, event));
172
+ }
173
+ function turnHasGroundedRead(after, turn, toolName) {
174
+ const resultDigests = new Set(after.events.flatMap((entry) => entry.event === "senses.sanctuary_read_receipt" && entry.meta.toolName === toolName && entry.meta.success === true && typeof entry.meta.resultDigest === "string" ? [entry.meta.resultDigest] : []));
175
+ return turn.toolResultDigests.some((digest) => resultDigests.has(digest));
176
+ }
177
+ function exactGroundedResponse(after, turn, toolName) {
178
+ const grounding = turn.toolGroundings?.length === 1 ? turn.toolGroundings[0] : undefined;
179
+ const live = after.liveGrounding;
180
+ const audit = after.events.filter((entry) => entry.event === "senses.sanctuary_read_receipt" && entry.meta.toolName === toolName && entry.meta.success === true
181
+ && entry.meta.resultDigest === grounding?.resultDigest && entry.meta.groundingDigest === grounding?.groundingDigest
182
+ && entry.meta.sourceIdentityDigest === grounding?.sourceIdentityDigest && entry.meta.observedAt === grounding?.observedAt);
183
+ const turnObservedAt = Date.parse(grounding?.observedAt ?? "");
184
+ const liveObservedAt = Date.parse(live?.observedAt ?? "");
185
+ const timeBound = Number.isFinite(turnObservedAt) && Number.isFinite(liveObservedAt)
186
+ && turnObservedAt <= turn.completedAt && turn.completedAt - turnObservedAt <= 300_000
187
+ && liveObservedAt >= turnObservedAt && liveObservedAt - turnObservedAt <= 300_000;
188
+ const liveFactsBound = toolName === "unraid_get_storage"
189
+ ? live?.groundingDigest === (0, sanctuary_grounding_1.sanctuaryGroundingDigest)(live?.facts ?? {})
190
+ : grounding?.groundingDigest === live?.groundingDigest && JSON.stringify(grounding?.facts) === JSON.stringify(live?.facts);
191
+ return Boolean(grounding && live && grounding.toolName === toolName && live.toolName === toolName
192
+ && grounding.groundingDigest === (0, sanctuary_grounding_1.sanctuaryGroundingDigest)(grounding.facts) && live.groundingDigest === (0, sanctuary_grounding_1.sanctuaryGroundingDigest)(live.facts)
193
+ && grounding.sourceIdentityDigest === live.sourceIdentityDigest && SHA256.test(grounding.sourceIdentityDigest) && timeBound && liveFactsBound
194
+ && audit.length === 1 && typeof turn.responseText === "string" && turn.responseText.length > 0
195
+ && turn.responseUtf16Units === turn.responseText.length && turn.responseUtf16Units <= 1_200
196
+ && (0, sanctuary_grounding_1.sanctuaryGroundedResponseAccurate)(toolName, grounding.facts, turn.responseText));
197
+ }
198
+ function recordsAdded(before, after, key) {
199
+ const remaining = new Map();
200
+ for (const value of before)
201
+ remaining.set(key(value), (remaining.get(key(value)) ?? 0) + 1);
202
+ return after.filter((value) => {
203
+ const digest = key(value);
204
+ const count = remaining.get(digest) ?? 0;
205
+ if (count === 0)
206
+ return true;
207
+ remaining.set(digest, count - 1);
208
+ return false;
209
+ });
210
+ }
211
+ function exactFreshTelegramLifecycle(before, after, expectedUpdateDigest) {
212
+ if (before.events.length > after.events.length || before.events.some((entry, index) => hash(entry) !== hash(after.events[index])))
213
+ return false;
214
+ const fresh = recordsAdded(before.events, after.events, (entry) => hash(entry));
215
+ const starts = fresh.filter((entry) => entry.event === "senses.telegram_turn_start");
216
+ const terminals = fresh.filter((entry) => entry.event === "senses.telegram_turn_end" || entry.event === "senses.telegram_turn_error");
217
+ if (starts.length !== 1 || terminals.length !== 1)
218
+ return false;
219
+ const start = starts[0];
220
+ const terminal = terminals[0];
221
+ const coordinateKeys = ["scenarioHandleDigest", "turnDigest", "updateDigest", "subject", "identityDigest", "sessionDigest", "argumentDigest"];
222
+ if (coordinateKeys.some((key) => start.meta[key] !== terminal.meta[key]))
223
+ return false;
224
+ if (!SHA256.test(String(start.meta.scenarioHandleDigest)) || !SHA256.test(String(start.meta.turnDigest))
225
+ || !SHA256.test(String(start.meta.updateDigest)) || (expectedUpdateDigest !== undefined && start.meta.updateDigest !== expectedUpdateDigest)
226
+ || typeof start.meta.subject !== "string" || !/^tg_[A-Za-z0-9_-]{43}$/u.test(start.meta.subject)
227
+ || ![start.meta.identityDigest, start.meta.sessionDigest, start.meta.argumentDigest, start.meta.lifecycleMac, terminal.meta.lifecycleMac].every((value) => typeof value === "string" && SHA256.test(value)))
228
+ return false;
229
+ if (!Number.isSafeInteger(terminal.meta.deliveryCount) || Number(terminal.meta.deliveryCount) < 0)
230
+ return false;
231
+ if (!Number.isSafeInteger(start.meta.lifecycleAt) || !Number.isSafeInteger(terminal.meta.lifecycleAt)
232
+ || Number(start.meta.lifecycleAt) < 0 || Number(start.meta.lifecycleAt) >= Number(terminal.meta.lifecycleAt))
233
+ return false;
234
+ return terminal.event === "senses.telegram_turn_end" && terminal.meta.outcome === "success" && terminal.meta.errorDigest === null;
235
+ }
236
+ function intendedApproval(before, after) {
237
+ const previous = new Set(before.approvals.map((record) => record.approvalId));
238
+ const candidates = after.approvals.filter((record) => !previous.has(record.approvalId) && record.createdAt >= before.capturedAt);
239
+ return candidates.length === 1 ? candidates[0] : null;
240
+ }
241
+ function intendedRestartApproval(approval) {
242
+ const argumentDigest = (0, node_crypto_1.createHash)("sha256").update(JSON.stringify({ container: CANONICAL_RESTART_TARGET })).digest("hex");
243
+ return approval?.toolName === "unraid_restart_container"
244
+ && approval.target === CANONICAL_RESTART_TARGET
245
+ && approval.argumentDigest === argumentDigest;
246
+ }
247
+ function approvalEvidenceCoordinates(approval) {
248
+ return {
249
+ actionDigest: (0, node_crypto_1.createHash)("sha256").update(JSON.stringify({ toolName: approval.toolName, argumentDigest: approval.argumentDigest })).digest("hex"),
250
+ targetDigest: (0, node_crypto_1.createHash)("sha256").update(JSON.stringify({ container: CANONICAL_RESTART_TARGET })).digest("hex"),
251
+ };
252
+ }
253
+ function exactApprovalEvidence(before, after, approval) {
254
+ const fresh = recordsAdded(before.events, after.events, (entry) => hash(entry));
255
+ const prompt = fresh.filter((entry) => entry.event === "senses.telegram_approval_prompt_bound" && entry.meta.approvalId === approval.approvalId);
256
+ const terminals = fresh.filter((entry) => entry.event === "telegram.approval_prompt_terminalized" && entry.meta.approvalId === approval.approvalId);
257
+ const callbackCandidates = fresh.filter((entry) => ["telegram.callback_settled", "telegram.callback_recovery_settled"].includes(entry.event) && entry.meta.approvalId === approval.approvalId);
258
+ const callbacks = [...new Map(callbackCandidates.map((entry) => [hash({ event: entry.event, meta: entry.meta }), entry])).values()];
259
+ const continuations = fresh.filter((entry) => entry.event === "senses.telegram_approval_continuation_delivered" && entry.meta.approvalId === approval.approvalId);
260
+ const staleCallbacks = fresh.filter((entry) => entry.event === "telegram.approval_stale_callback_settled" && entry.meta.approvalId === approval.approvalId);
261
+ const expiryObservations = fresh.filter((entry) => entry.event === "telegram.approval_expiry_observed" && entry.meta.approvalId === approval.approvalId);
262
+ if (prompt.length !== 1 || terminals.length !== 1 || callbacks.length > 1 || continuations.length > 1 || staleCallbacks.length > 1)
263
+ return null;
264
+ if ((approval.state === "expired" && (expiryObservations.length < 1
265
+ || new Set(expiryObservations.map((entry) => hash({ event: entry.event, meta: entry.meta }))).size !== 1))
266
+ || (approval.state !== "expired" && expiryObservations.length !== 0))
267
+ return null;
268
+ const expected = approvalEvidenceCoordinates(approval);
269
+ const coordinatesMatch = (entry) => entry.meta.scenarioHandleDigest !== undefined
270
+ && typeof entry.meta.scenarioHandleDigest === "string" && SHA256.test(entry.meta.scenarioHandleDigest)
271
+ && entry.meta.actionDigest === expected.actionDigest && entry.meta.targetDigest === expected.targetDigest
272
+ && entry.meta.checkpointDigest === approval.checkpointDigest
273
+ && typeof approval.suspendedSessionRevision === "string"
274
+ && entry.meta.suspendedSessionRevisionDigest === (0, node_crypto_1.createHash)("sha256").update(approval.suspendedSessionRevision, "utf8").digest("hex")
275
+ && typeof entry.meta.messageIdDigest === "string" && SHA256.test(entry.meta.messageIdDigest)
276
+ && typeof entry.meta.evidenceMac === "string" && SHA256.test(entry.meta.evidenceMac);
277
+ if (![...prompt, ...terminals, ...callbacks, ...continuations, ...staleCallbacks, ...expiryObservations].every(coordinatesMatch))
278
+ return null;
279
+ const promptScenarioHandleDigest = prompt[0].meta.scenarioHandleDigest;
280
+ const promptMessageIdDigest = prompt[0].meta.messageIdDigest;
281
+ if (![...terminals, ...callbacks, ...continuations, ...staleCallbacks, ...expiryObservations].every((entry) => entry.meta.scenarioHandleDigest === promptScenarioHandleDigest
282
+ && entry.meta.messageIdDigest === promptMessageIdDigest))
283
+ return null;
284
+ const boundAt = Number(prompt[0].meta.boundAt);
285
+ const terminalEditStartedAt = Number(terminals[0].meta.terminalEditStartedAt);
286
+ const terminalizedAt = Number(terminals[0].meta.terminalizedAt);
287
+ const expiryObservation = expiryObservations[0] ?? null;
288
+ const expiryObservedAt = expiryObservation === null || expiryObservation.meta.expiryObservedAt === null ? null : Number(expiryObservation.meta.expiryObservedAt);
289
+ const expiryDeadlineAt = expiryObservation === null ? null : Number(expiryObservation.meta.expiryDeadlineAt);
290
+ if (!Number.isSafeInteger(boundAt) || boundAt < 0
291
+ || !Number.isSafeInteger(terminalEditStartedAt) || terminalEditStartedAt < boundAt
292
+ || !Number.isSafeInteger(terminalizedAt) || terminalizedAt < terminalEditStartedAt
293
+ || terminalizedAt - terminalEditStartedAt > exports.SANCTUARY_APPROVAL_TERMINAL_EDIT_TIMEOUT_MS
294
+ || terminals[0].meta.boundAt !== boundAt || terminals[0].meta.buttonsRemoved !== true
295
+ || approval.expiresAt !== boundAt + exports.SANCTUARY_APPROVAL_TTL_MS)
296
+ return null;
297
+ if (approval.state === "expired") {
298
+ if (expiryObservedAt === null || !Number.isSafeInteger(expiryObservedAt) || expiryObservedAt < boundAt
299
+ || expiryObservation?.meta.expiryObservationSchemaVersion !== "telegram-approval-expiry-observation-v1"
300
+ || expiryObservation.meta.boundAt !== boundAt
301
+ || terminals[0].meta.expiryObservedAt !== expiryObservedAt || terminals[0].meta.expiryDeadlineAt !== expiryDeadlineAt
302
+ || terminalEditStartedAt < expiryObservedAt || terminalizedAt - expiryObservedAt > exports.SANCTUARY_APPROVAL_TERMINAL_EDIT_TIMEOUT_MS)
303
+ return null;
304
+ }
305
+ else if (expiryObservedAt !== null || terminals[0].meta.expiryObservedAt !== undefined || terminals[0].meta.expiryDeadlineAt !== undefined)
306
+ return null;
307
+ const callback = callbacks[0] ?? null;
308
+ if (callback) {
309
+ const callbackAt = Number(callback.meta.callbackAt);
310
+ if (!Number.isSafeInteger(callbackAt) || callbackAt < boundAt || callbackAt > terminalEditStartedAt
311
+ || callback.meta.boundAt !== boundAt
312
+ || (callback.event === "telegram.callback_settled" && (callback.meta.acknowledged !== true || callback.meta.acknowledgementState !== "acknowledged"))
313
+ || (callback.event === "telegram.callback_recovery_settled" && (callback.meta.acknowledgementState !== "indeterminate_after_restart"
314
+ || !Number.isSafeInteger(Number(callback.meta.recoveredAt)) || Number(callback.meta.recoveredAt) < terminalizedAt
315
+ || !Number.isSafeInteger(callback.at) || callback.at < Number(callback.meta.recoveredAt)
316
+ || typeof callback.meta.decisionAttemptDigest !== "string" || !SHA256.test(callback.meta.decisionAttemptDigest))))
317
+ return null;
318
+ }
319
+ const continuation = continuations[0] ?? null;
320
+ if (continuation) {
321
+ const deliveredAt = Number(continuation.meta.deliveredAt);
322
+ const callbackAt = callback === null ? null : Number(callback.meta.callbackAt);
323
+ if (!Number.isSafeInteger(deliveredAt) || deliveredAt < boundAt || deliveredAt > terminalEditStartedAt
324
+ || callbackAt !== null && deliveredAt < callbackAt
325
+ || continuation.meta.boundAt !== boundAt || typeof continuation.meta.resultDigest !== "string" || !SHA256.test(continuation.meta.resultDigest)
326
+ || continuation.meta.resultDigest !== approval.resultDigest
327
+ || typeof continuation.meta.deliveryDigest !== "string" || !SHA256.test(continuation.meta.deliveryDigest)
328
+ || typeof continuation.meta.deliveryMessageIdDigest !== "string" || !SHA256.test(continuation.meta.deliveryMessageIdDigest))
329
+ return null;
330
+ }
331
+ const staleCallback = staleCallbacks[0] ?? null;
332
+ if (staleCallback) {
333
+ const staleAt = Number(staleCallback.meta.staleAt);
334
+ if (!Number.isSafeInteger(staleAt) || staleAt < terminalizedAt || staleCallback.meta.acknowledged !== true
335
+ || staleCallback.meta.accepted !== false || staleCallback.meta.reason !== "stale_callback")
336
+ return null;
337
+ }
338
+ return { boundAt, callback, continuation, expiryObservedAt, expiryDeadlineAt, terminalizedAt, staleCallback };
339
+ }
340
+ function terminalizedWithinTtlJitter(evidence) {
341
+ const expiryObservedAt = evidence?.expiryObservedAt ?? null;
342
+ const expiryDeadlineAt = evidence?.expiryDeadlineAt ?? null;
343
+ if ([expiryObservedAt, expiryDeadlineAt].some((value) => value === null))
344
+ return false;
345
+ return expiryDeadlineAt === evidence.boundAt + exports.SANCTUARY_APPROVAL_TTL_MS
346
+ && expiryObservedAt >= expiryDeadlineAt
347
+ && expiryObservedAt <= expiryDeadlineAt + exports.SANCTUARY_APPROVAL_RECONCILIATION_JITTER_MS;
348
+ }
349
+ function interactiveReceiptBindsApproval(receipt, approval) {
350
+ const digestText = (value) => (0, node_crypto_1.createHash)("sha256").update(value, "utf8").digest("hex");
351
+ return receipt.approvalIdDigest === digestText(approval.approvalId)
352
+ && receipt.checkpointDigest === approval.checkpointDigest
353
+ && receipt.approvalEpochBefore === approval.approvalEpoch
354
+ && typeof approval.suspendedSessionRevision === "string"
355
+ && receipt.suspendedSessionRevisionDigest === digestText(approval.suspendedSessionRevision);
356
+ }
357
+ function healthProbeRestored(probe, currentCron) {
358
+ return probe.ownerImageDigestBefore === probe.ownerImageDigestAfter
359
+ && probe.ownerContainerDigestBefore === probe.ownerContainerDigestAfter
360
+ && (probe.schedulerReceipt !== null || probe.beforeStateDigest === probe.restoredStateDigest)
361
+ && probe.cronFingerprintBefore === probe.cronFingerprintAfter
362
+ && probe.cronRegisteredBefore && probe.cronRegisteredAfter && !probe.cronDegradedBefore && !probe.cronDegradedAfter
363
+ && currentCron?.fingerprint === probe.cronFingerprintAfter && currentCron.registered === probe.cronRegisteredAfter
364
+ && probe.workspaceAbsent && probe.socketAbsent && probe.snapshotAbsent && probe.realCheckEquivalent && probe.productionRestored;
365
+ }
366
+ function deriveSanctuaryScenarioAssertions(label, before, after, _now, scenarioHandleDigest) {
367
+ const approval = intendedApproval(before, after);
368
+ const newTurns = recordsAdded(before.telegramTurns, after.telegramTurns, (turn) => hash(turn));
369
+ const newAttempts = recordsAdded(before.restartAttempts, after.restartAttempts, (attempt) => hash(attempt));
370
+ const newApprovals = recordsAdded(before.approvals, after.approvals, (record) => record.approvalId);
371
+ const linkedAttempts = approval ? after.restartAttempts
372
+ .filter((attempt) => approval.toolName === "unraid_restart_container" && attempt.approvalId === approval.approvalId && attempt.argumentDigest === approval.argumentDigest && attempt.target === approval.target)
373
+ .sort((left, right) => left.observedAt - right.observedAt) : [];
374
+ const attemptIsLinked = (attempt) => {
375
+ if (!approval || !intendedRestartApproval(approval) || attempt.approvalId !== approval.approvalId
376
+ || attempt.argumentDigest !== approval.argumentDigest || attempt.target !== CANONICAL_RESTART_TARGET
377
+ || typeof attempt.targetId !== "string" || attempt.targetId.length < 1 || attempt.targetId.length > 128
378
+ || approval.resultTargetId === null || attempt.targetId !== approval.resultTargetId)
379
+ return false;
380
+ const expectedActionDigest = (0, node_crypto_1.createHash)("sha256").update(JSON.stringify({ operation: "restart", container: { id: attempt.targetId, name: CANONICAL_RESTART_TARGET } })).digest("hex");
381
+ return attempt.actionDigest === expectedActionDigest;
382
+ };
383
+ const completeAttemptLedgerLinked = newAttempts.every(attemptIsLinked) && linkedAttempts.length === newAttempts.length
384
+ && new Set(linkedAttempts.map((attempt) => attempt.targetId)).size <= 1;
385
+ const mutationAttemptIds = new Set(linkedAttempts.filter((attempt) => attempt.state === "attempting").map((attempt) => attempt.attemptId));
386
+ const mutationCount = mutationAttemptIds.size;
387
+ const unsafeAttemptStates = new Set(["attempting", "succeeded", "attempted_or_indeterminate"]);
388
+ const scenarioMutationCount = new Set(after.restartAttempts.filter((attempt) => unsafeAttemptStates.has(attempt.state)).map((attempt) => attempt.attemptId)).size;
389
+ const restartSucceeded = linkedAttempts.some((attempt) => attempt.state === "succeeded" && mutationAttemptIds.has(attempt.attemptId)
390
+ && (attempt.mutationAcknowledged || attempt.afterState === "running"));
391
+ const firstIndeterminate = linkedAttempts.find((attempt) => attempt.state === "attempted_or_indeterminate");
392
+ const attemptedIndeterminateRetryCount = firstIndeterminate
393
+ ? new Set(linkedAttempts.filter((attempt) => attempt.state === "attempting" && attempt.observedAt > firstIndeterminate.observedAt && attempt.attemptId !== firstIndeterminate.attemptId).map((attempt) => attempt.attemptId)).size
394
+ : 0;
395
+ const deliveredTurns = newTurns.filter((turn) => turn.status === "success" && turn.deliveryCount > 0);
396
+ const telegramResponses = deliveredTurns.length;
397
+ const approvalTransitions = delta(after, before, "approval.acceptance_transition");
398
+ const postbootIntegrity = after.prebootIntegrity && after.postbootIntegrity
399
+ && typeof scenarioHandleDigest === "string"
400
+ ? verifySanctuaryPostbootIntegrity(after.prebootIntegrity, after.postbootIntegrity, scenarioHandleDigest) : null;
401
+ switch (label) {
402
+ case "unit-12c-1-opaque-identity":
403
+ case "unit-14b-3-opaque-identity-live":
404
+ if (!after.identity || (after.identity.inspectedRecordCount ?? 0) < 1 || (after.identity.opaqueSubjectCount ?? 0) < 1 || after.identity.mismatchCount !== 0 || after.identity.rawLeakCount !== 0
405
+ || (label.includes("live") && (telegramResponses !== 1 || newTurns.length !== 1 || !after.identity.liveSubjectObserved
406
+ || after.identity.canonicalSessionCount !== 1 || after.identity.canonicalFriendCount !== 1
407
+ || !exactFreshTelegramLifecycle(before, after, newTurns[0]?.updateDigest))))
408
+ return null;
409
+ return { identityBound: after.identity.keyPresent, opaqueSubject: after.identity.subjectOpaque, rawIdentityAbsent: after.identity.rawIdentityAbsent };
410
+ case "unit-15c-1-no-callback-terminalization": {
411
+ if (!intendedRestartApproval(approval) || approval.state !== "expired" || approval.createdAt < before.capturedAt)
412
+ return null;
413
+ const evidence = exactApprovalEvidence(before, after, approval);
414
+ if (!terminalizedWithinTtlJitter(evidence) || !evidence || evidence.callback !== null || evidence.continuation !== null)
415
+ return null;
416
+ const elapsedMs = evidence.terminalizedAt - evidence.boundAt;
417
+ if (!approval.buttonsRemoved || !approval.terminalPrompt)
418
+ return null;
419
+ const baseline = before.sourceValues["no-callback-baseline"];
420
+ if (!baseline || typeof baseline !== "object" || Array.isArray(baseline))
421
+ return null;
422
+ const baselineRecord = baseline;
423
+ const inboundEventCount = after.events.filter((entry) => entry.event === "telegram.callback_settled" || entry.event === "telegram.callback_recovery_settled" || entry.event === "telegram.update_dropped").length;
424
+ const noInboundUpdate = baselineRecord.approvalId === approval.approvalId
425
+ && baselineRecord.offsetDigest === hash(after.sourceValues["telegram-offset"])
426
+ && baselineRecord.inboundEventCount === inboundEventCount
427
+ && approval.callbackCount === 0;
428
+ if (!noInboundUpdate || scenarioMutationCount !== 0 || !completeAttemptLedgerLinked)
429
+ return null;
430
+ return { buttonsRemoved: approval.buttonsRemoved, elapsedMs, mutationCount: 0, noInboundUpdate, replayMutationCount: approval.replayMutationCount, terminalExpired: approval.state === "expired", ttlMs: exports.SANCTUARY_APPROVAL_TTL_MS };
431
+ }
432
+ case "unit-16a-pre-reboot-checkpoint":
433
+ if (!after.reboot || after.reboot.phase !== "preflight" || !SHA256.test(after.reboot.processBindingDigest) || after.reboot.requestCount !== 0 || !after.container?.running || !after.container.healthy || after.reboot.unrelatedHostOperations !== 0)
434
+ return null;
435
+ return { approvalDigest: hash(after.approvals), auditDigest: hash(after.events), containerDigest: hash(after.container), fingerprintDigest: hash(after.cron), offsetDigest: hash(after.sourceValues["telegram-offset"]), processBindingDigest: after.reboot.processBindingDigest, ready: Boolean(after.container?.running && after.container.healthy), unrelatedHostOperations: after.reboot.unrelatedHostOperations };
436
+ case "unit-16a-reboot-request":
437
+ if (!after.reboot || after.reboot.phase !== "requested" || !SHA256.test(after.reboot.processBindingDigest) || after.reboot.requestCount !== 1 || !after.reboot.checkpointPersisted)
438
+ return null;
439
+ return { exactlyOnce: true, processBindingDigest: after.reboot.processBindingDigest, requestCheckpointPersisted: true, requestDigest: after.reboot.requestDigest };
440
+ case "unit-16a-boot-recovery-milestones":
441
+ if (!after.reboot || after.reboot.phase !== "complete" || !SHA256.test(after.reboot.processBindingDigest) || !after.reboot.bootIdentityChanged || !after.reboot.arrayReady || !after.reboot.butlerReady || !after.reboot.dockerReady || !after.reboot.hostReady || !after.reboot.sshReady || !after.reboot.tailscaleReady || !postbootIntegrity)
442
+ return null;
443
+ return { arrayReady: after.reboot.arrayReady, bootIdentityChanged: true, butlerReady: after.reboot.butlerReady, dockerReady: after.reboot.dockerReady, hostReady: after.reboot.hostReady, postbootIntegrityPreserved: true, processBindingDigest: after.reboot.processBindingDigest, sshReady: after.reboot.sshReady, tailscaleReady: after.reboot.tailscaleReady };
444
+ case "unit-16b-runtime-vault-containment":
445
+ if (!after.container)
446
+ return null;
447
+ if (!after.container.autostartExact || !after.container.exactImage || after.container.manualAuthRequired
448
+ || after.container.mountCount !== 2 || Number(after.container.user.split(":")[0]) !== 10001
449
+ || after.container.publishedPortCount !== 0 || !after.container.readOnlyRoot
450
+ || !after.container.updaterDisabled || !after.container.vaultUnlocked)
451
+ return null;
452
+ return { autostartExact: after.container.autostartExact, exactImage: after.container.exactImage, manualAuthRequired: after.container.manualAuthRequired, mountCount: after.container.mountCount, nonRootUid: Number(after.container.user.split(":")[0]), publishedPortCount: after.container.publishedPortCount, readOnlyRoot: after.container.readOnlyRoot, updaterDisabled: after.container.updaterDisabled, vaultUnlocked: after.container.vaultUnlocked };
453
+ case "unit-16c-provider-readiness":
454
+ return after.provider && after.provider.outwardReady && after.provider.innerReady && after.provider.geminiCandidateReady && after.provider.providersDistinct && !after.provider.silentFallback
455
+ && after.provider.credentialRevisionsPresent === true && after.provider.requestSemanticsExact === true && after.provider.fallbackAttemptCount === 0
456
+ && after.provider.modelsExact === true && after.provider.baseUrlsExact === true && after.provider.vaultCoordinatesExact === true && after.provider.credentialIdentitiesDistinct === true
457
+ ? { outwardReady: true, innerReady: true, geminiCandidateReady: true, providersDistinct: true, silentFallback: false, modelsExact: true, baseUrlsExact: true, vaultCoordinatesExact: true, credentialIdentitiesDistinct: true } : null;
458
+ case "unit-16d-whats-up":
459
+ if (telegramResponses !== 1 || newTurns.length !== 1 || !exactFreshTelegramLifecycle(before, after, newTurns[0].updateDigest) || deliveredTurns[0].toolInvocationCount !== 1 || deliveredTurns[0].toolResultDigests.length !== 1 || !turnHasGroundedRead(after, deliveredTurns[0], "unraid_get_system") || !exactGroundedResponse(after, deliveredTurns[0], "unraid_get_system"))
460
+ return null;
461
+ return { accurate: true, authorized: true, grounded: true, liveFactsMatched: true, responseCount: telegramResponses, responseWithinLimit: true, telegramDelivered: true };
462
+ case "unit-16d-1-space":
463
+ if (telegramResponses !== 1 || newTurns.length !== 1 || !exactFreshTelegramLifecycle(before, after, newTurns[0].updateDigest) || deliveredTurns[0].toolInvocationCount !== 1 || deliveredTurns[0].toolResultDigests.length !== 1 || !turnHasGroundedRead(after, deliveredTurns[0], "unraid_get_storage") || !exactGroundedResponse(after, deliveredTurns[0], "unraid_get_storage"))
464
+ return null;
465
+ return { accurate: true, authorized: true, grounded: true, liveFactsMatched: true, mutationCount: scenarioMutationCount, responseCount: telegramResponses, responseWithinLimit: true, telegramDelivered: true };
466
+ case "unit-16d-2-unauthorized": {
467
+ if (before.events.length > after.events.length || before.events.some((entry, index) => hash(entry) !== hash(after.events[index])))
468
+ return null;
469
+ const freshEvents = recordsAdded(before.events, after.events, (entry) => hash(entry));
470
+ const freshDrops = freshEvents.filter((entry) => entry.event === "telegram.update_dropped");
471
+ if (freshDrops.length !== 1)
472
+ return null;
473
+ const drop = freshDrops[0];
474
+ const distinctAccount = drop.meta.distinctAccount === true
475
+ && typeof drop.meta.scenarioHandleDigest === "string" && SHA256.test(drop.meta.scenarioHandleDigest)
476
+ && typeof drop.meta.updateDigest === "string" && SHA256.test(drop.meta.updateDigest)
477
+ && typeof drop.meta.senderIdentityDigest === "string" && SHA256.test(drop.meta.senderIdentityDigest)
478
+ && typeof drop.meta.authorizedIdentityDigest === "string" && SHA256.test(drop.meta.authorizedIdentityDigest)
479
+ && drop.meta.senderIdentityDigest !== drop.meta.authorizedIdentityDigest
480
+ && drop.meta.senderDistinct === true
481
+ && typeof drop.meta.nextOffsetDigest === "string" && SHA256.test(drop.meta.nextOffsetDigest)
482
+ && typeof drop.meta.dropMac === "string" && SHA256.test(drop.meta.dropMac);
483
+ const providerInvocationCount = newTurns.reduce((sum, turn) => sum + turn.providerTurnCount, 0);
484
+ const toolInvocationCount = newTurns.reduce((sum, turn) => sum + turn.toolInvocationCount, 0);
485
+ const durableToolRecordCount = delta(after, before, "senses.sanctuary_read_receipt");
486
+ const workItemCount = newApprovals.length;
487
+ const sessionStateUnchanged = Boolean(before.identity && after.identity
488
+ && typeof before.identity.sessionSurfaceDigest === "string" && SHA256.test(before.identity.sessionSurfaceDigest)
489
+ && before.identity.sessionSurfaceDigest === after.identity.sessionSurfaceDigest
490
+ && typeof before.identity.friendSurfaceDigest === "string" && SHA256.test(before.identity.friendSurfaceDigest)
491
+ && before.identity.friendSurfaceDigest === after.identity.friendSurfaceDigest);
492
+ const forbiddenWorkEvent = freshEvents.some((entry) => entry.event === "senses.telegram_turn_start" || entry.event === "senses.telegram_turn_end"
493
+ || entry.event === "senses.telegram_turn_error" || entry.event === "senses.sanctuary_read_receipt"
494
+ || entry.event.startsWith("senses.telegram_approved_restart_") || entry.event === "senses.sanctuary_health_delivered");
495
+ const offsetAdvanced = Number.isSafeInteger(before.telegramNextUpdateId) && Number.isSafeInteger(after.telegramNextUpdateId)
496
+ && Number(after.telegramNextUpdateId) === Number(before.telegramNextUpdateId) + 1;
497
+ const zeroWorkUnchanged = Boolean(before.zeroWork && after.zeroWork
498
+ && before.zeroWork.providerToolDigest === after.zeroWork.providerToolDigest
499
+ && before.zeroWork.outwardDigest === after.zeroWork.outwardDigest
500
+ && before.zeroWork.approvalMutationDigest === after.zeroWork.approvalMutationDigest
501
+ && before.zeroWork.sessionFriendDigest === after.zeroWork.sessionFriendDigest);
502
+ if (!after.containment || !exactContainmentAudit(after.containment) || !distinctAccount || !offsetAdvanced || !zeroWorkUnchanged || !sessionStateUnchanged || newTurns.length !== 0 || providerInvocationCount !== 0 || toolInvocationCount !== 0 || telegramResponses !== 0 || workItemCount !== 0 || approvalTransitions !== 0 || newAttempts.length !== 0 || scenarioMutationCount !== 0 || durableToolRecordCount !== 0 || forbiddenWorkEvent)
503
+ return null;
504
+ return { auditRejected: true, distinctAccount, mutationCount: 0, providerInvocationCount: 0, responseCount: 0, workItemCount: 0 };
505
+ }
506
+ case "unit-16e-containment-audit":
507
+ if (!after.containment || !exactContainmentAudit(after.containment) || scenarioMutationCount !== 0)
508
+ return null;
509
+ return {
510
+ schemaVersion: after.containment.schemaVersion,
511
+ keyCount: after.containment.keyCount, keyInventoryDigest: after.containment.keyInventoryDigest,
512
+ readScopeDigest: after.containment.readScopeDigest, writeScopeDigest: after.containment.writeScopeDigest,
513
+ keyRoleAssignmentCount: after.containment.keyRoleAssignmentCount,
514
+ telegramToolCount: after.containment.telegramToolCount, telegramProfileDigest: after.containment.telegramProfileDigest,
515
+ telegramSchemaDigest: after.containment.telegramSchemaDigest,
516
+ privateToolCount: after.containment.privateToolCount, privateProfileDigest: after.containment.privateProfileDigest,
517
+ privateSchemaDigest: after.containment.privateSchemaDigest, resolvedHandlerCount: after.containment.resolvedHandlerCount,
518
+ excludedToolCount: after.containment.excludedToolCount, excludedSchemaIntersectionCount: after.containment.excludedSchemaIntersectionCount,
519
+ fabricatedHandlerInvocationCount: after.containment.fabricatedHandlerInvocationCount,
520
+ excludedToolAttemptCount: after.containment.excludedToolAttemptCount, excludedToolRejectedCount: after.containment.excludedToolRejectedCount,
521
+ excludedToolInvokedCount: after.containment.excludedToolInvokedCount, excludedToolSideEffectCount: after.containment.excludedToolSideEffectCount,
522
+ globallyResolvableExcludedToolCount: after.containment.globallyResolvableExcludedToolCount,
523
+ auditPathDigest: after.containment.auditPathDigest, auditLedgerDigest: after.containment.auditLedgerDigest,
524
+ auditRecordCount: after.containment.auditRecordCount, auditLifecyclePairCount: after.containment.auditLifecyclePairCount,
525
+ containerUser: after.containment.containerUser, liveProcessUser: after.containment.liveProcessUser, mountCount: after.containment.mountCount,
526
+ publishedPortCount: after.containment.publishedPortCount, networkMode: after.containment.networkMode,
527
+ readOnlyRoot: after.containment.readOnlyRoot, mountsExact: after.containment.mountsExact,
528
+ securityExact: after.containment.securityExact, updaterDisabled: after.containment.updaterDisabled,
529
+ writableKeyExposure: after.containment.writableKeyExposure,
530
+ rawWriteMaterialFieldCount: after.containment.rawWriteMaterialFieldCount,
531
+ typedWriteExecutorCount: after.containment.typedWriteExecutorCount,
532
+ writeApprovalPolicyDigest: after.containment.writeApprovalPolicyDigest,
533
+ sensitiveMaterialObserved: after.containment.sensitiveMaterialObserved,
534
+ mutationCount: scenarioMutationCount,
535
+ };
536
+ case "unit-16e-1-stop-denial":
537
+ case "unit-16e-2-restart-denial": {
538
+ const receipt = after.denial;
539
+ const expectedOperation = label === "unit-16e-1-stop-denial" ? "stop" : "restart";
540
+ if (!receipt || receipt.label !== label || receipt.operation !== expectedOperation || receipt.scenarioHandleDigest.length !== 64
541
+ || receipt.attemptCount !== 1 || ![200, 401, 403].includes(receipt.httpStatus)
542
+ || (receipt.errorCode !== "FORBIDDEN" && receipt.errorCode !== "PERMISSION_DENIED")
543
+ || receipt.targetDigest !== receipt.before.targetContainerIdDigest || receipt.targetDigest !== receipt.after.targetContainerIdDigest
544
+ || JSON.stringify(receipt.before) !== JSON.stringify(receipt.after) || scenarioMutationCount !== 0)
545
+ return null;
546
+ return { attemptCount: receipt.attemptCount, cursorBoundaryCount: 7, denied: true, mutationCount: 0, restartCountUnchanged: true, resumed: true };
547
+ }
548
+ case "unit-16f-cron-fingerprint":
549
+ if (!after.container?.running || !after.container.healthy || !after.healthProbe || !healthProbeRestored(after.healthProbe, after.cron) || after.healthProbe.clockMode !== "ambient" || after.healthProbe.phases.length !== 1
550
+ || !after.healthProbe.schedulerReceipt || after.healthProbe.schedulerReceipt.trigger !== "cron" || after.healthProbe.schedulerReceipt.sweepDelta !== 1
551
+ || after.healthProbe.schedulerReceipt.deliveryDelta !== 0 || after.healthProbe.schedulerReceipt.nonReplay !== true
552
+ || after.healthProbe.schedulerReceipt.sweep.recordDigest !== after.healthProbe.phases[0]?.sweepReceiptDigest
553
+ || after.healthProbe.phases[0]?.name !== "cron-unchanged" || after.healthProbe.phases[0].trigger !== "cron" || after.healthProbe.phases[0].fixtureStatus !== null
554
+ || after.healthProbe.phases[0].opened !== 0 || after.healthProbe.phases[0].recovered !== 0 || after.healthProbe.phases[0].digestDue
555
+ || after.healthProbe.phases[0].deliveryKind !== null || after.healthProbe.phases[0].deliveryReceiptDigest !== null
556
+ || after.healthProbe.privateTurnCount !== 0 || after.healthProbe.providerInvocationCount !== 0 || after.healthProbe.deliveryCount !== 0 || telegramResponses !== 0 || delta(after, before, "senses.telegram_turn_start") !== 0)
557
+ return null;
558
+ return { fingerprintUnchanged: true, messageCount: 0, providerInvocationCount: 0, receiptUnchanged: true, scheduleRegistered: true, sweepObserved: true };
559
+ case "unit-16g-health-transition": {
560
+ const probe = after.healthProbe;
561
+ const exactPhases = [
562
+ ["live-baseline", null, 0, 0, null], ["live-repeat", null, 0, 0, null], ["fixture-fail", 503, 1, 0, "transition"],
563
+ ["fixture-repeat", 503, 0, 0, null], ["fixture-recover", 200, 0, 1, "transition"], ["fixture-refail", 503, 1, 0, "transition"],
564
+ ];
565
+ if (!after.container?.running || !after.container.healthy || !probe || !healthProbeRestored(probe, after.cron) || probe.clockMode !== "ambient" || probe.privateTurnCount !== 3
566
+ || probe.providerInvocationCount < probe.privateTurnCount || probe.providerInvocationCount > 1_000 || probe.deliveryCount !== 3 || probe.phases.length !== exactPhases.length
567
+ || !probe.phases.every((phase, index) => phase.ordinal === index + 1 && phase.name === exactPhases[index][0] && phase.trigger === "acceptance"
568
+ && phase.fixtureStatus === exactPhases[index][1] && phase.opened === exactPhases[index][2] && phase.recovered === exactPhases[index][3]
569
+ && phase.deliveryKind === exactPhases[index][4] && phase.digestDue === false && (phase.deliveryKind === null) === (phase.deliveryReceiptDigest === null)))
570
+ return null;
571
+ return { alertCount: 3, productionRestored: true, transitionObserved: true };
572
+ }
573
+ case "unit-16h-daily-digest": {
574
+ const probe = after.healthProbe;
575
+ if (!after.container?.running || !after.container.healthy || !probe || !healthProbeRestored(probe, after.cron) || probe.clockMode !== "local-daily-boundary" || probe.privateTurnCount !== 1
576
+ || probe.providerInvocationCount < probe.privateTurnCount || probe.providerInvocationCount > 1_000 || probe.deliveryCount !== 1 || probe.phases.length !== 2
577
+ || probe.phases[0]?.ordinal !== 1 || probe.phases[0].name !== "digest-first" || probe.phases[0].trigger !== "acceptance" || probe.phases[0].fixtureStatus !== 503
578
+ || !probe.phases[0].digestDue || probe.phases[0].deliveryKind !== "digest" || probe.phases[0].deliveryReceiptDigest === null
579
+ || probe.phases[1]?.ordinal !== 2 || probe.phases[1].name !== "digest-repeat" || probe.phases[1].trigger !== "acceptance" || probe.phases[1].fixtureStatus !== 503
580
+ || probe.phases[1].digestDue || probe.phases[1].deliveryKind !== null || probe.phases[1].deliveryReceiptDigest !== null)
581
+ return null;
582
+ return { firedWithinMs: 0, messageCount: 1, productionRestored: true, scheduleObserved: true };
583
+ }
584
+ case "unit-16i-delayed-approval":
585
+ if (!exactFreshTelegramLifecycle(before, after) || !intendedRestartApproval(approval) || !completeAttemptLedgerLinked || approval.state !== "succeeded" || mutationCount !== 1 || scenarioMutationCount !== 1 || !restartSucceeded || approval.replayMutationCount !== 0 || !approval.continuationCompleted)
586
+ return null;
587
+ if (!approval.terminalPrompt)
588
+ return null;
589
+ {
590
+ const evidence = exactApprovalEvidence(before, after, approval);
591
+ const callbackAt = Number(evidence?.callback?.meta.callbackAt);
592
+ if (!evidence || !evidence.callback || !evidence.continuation || callbackAt - evidence.boundAt < 120_000
593
+ || evidence.callback.event !== "telegram.callback_settled" || evidence.callback.meta.accepted !== true || evidence.callback.meta.reason !== "accepted")
594
+ return null;
595
+ return { elapsedMs: callbackAt - evidence.boundAt, mutationCount, promptTerminal: approval.terminalPrompt, replayMutationCount: approval.replayMutationCount, resumed: approval.continuationCompleted, state: approval.state };
596
+ }
597
+ case "unit-16j-denial": {
598
+ if (!exactFreshTelegramLifecycle(before, after) || !intendedRestartApproval(approval) || !completeAttemptLedgerLinked || approval.state !== "denied" || scenarioMutationCount !== 0 || approval.replayMutationCount !== 0 || !approval.continuationCompleted)
599
+ return null;
600
+ if (!approval.terminalPrompt)
601
+ return null;
602
+ const evidence = exactApprovalEvidence(before, after, approval);
603
+ if (!evidence?.callback || evidence.callback.event !== "telegram.callback_settled" || !evidence.continuation || evidence.callback.meta.accepted !== false || evidence.callback.meta.reason !== "decision_refused")
604
+ return null;
605
+ return { mutationCount, promptTerminal: approval.terminalPrompt, replayMutationCount: approval.replayMutationCount, resumed: approval.continuationCompleted, state: approval.state };
606
+ }
607
+ case "unit-16k-timeout-stale": {
608
+ if (!intendedRestartApproval(approval) || !completeAttemptLedgerLinked || approval.state !== "expired" || scenarioMutationCount !== 0 || approval.replayMutationCount !== 0)
609
+ return null;
610
+ if (!approval.buttonsRemoved || !approval.terminalPrompt || !approval.staleAcknowledged)
611
+ return null;
612
+ const evidence = exactApprovalEvidence(before, after, approval);
613
+ if (!terminalizedWithinTtlJitter(evidence) || !evidence || evidence.callback !== null || evidence.continuation !== null || evidence.staleCallback === null)
614
+ return null;
615
+ const driver = after.interactiveDriver;
616
+ if (!driver || driver.label !== "unit-16k-timeout-stale" || !interactiveReceiptBindsApproval(driver, approval)
617
+ || driver.callbackAttempts !== 1 || driver.distinctQueryCount !== 1 || driver.settledCount !== 1
618
+ || driver.claimCount !== 0 || driver.mutationCount !== 0 || !driver.staleAcknowledged || !driver.promptTerminal)
619
+ return null;
620
+ return { buttonsRemoved: approval.buttonsRemoved, mutationCount, promptTerminal: approval.terminalPrompt, staleAcknowledged: approval.staleAcknowledged, staleReplayMutationCount: approval.replayMutationCount, state: approval.state };
621
+ }
622
+ case "unit-16l-duplicate-callback": {
623
+ if (!intendedRestartApproval(approval) || !completeAttemptLedgerLinked || approval.callbackCount < 1 || approval.settledCount < 1 || approval.claimCount !== 1 || !approval.terminalPrompt || approval.replayMutationCount !== 0 || mutationCount !== 1 || scenarioMutationCount !== 1 || !restartSucceeded)
624
+ return null;
625
+ const evidence = exactApprovalEvidence(before, after, approval);
626
+ if (!evidence?.callback || evidence.callback.event !== "telegram.callback_settled" || !evidence.continuation || evidence.callback.meta.accepted !== true || evidence.callback.meta.reason !== "accepted")
627
+ return null;
628
+ const driver = after.interactiveDriver;
629
+ if (!driver || driver.label !== "unit-16l-duplicate-callback"
630
+ || !interactiveReceiptBindsApproval(driver, approval) || driver.callbackAttempts !== 2 || driver.distinctQueryCount !== 2
631
+ || !driver.barrierObserved || driver.settledCount !== 2 || driver.claimCount !== 1 || driver.mutationCount !== 1
632
+ || driver.staleReplayAttempts !== 1 || !driver.staleReplaySettled || driver.staleReplayMutationCount !== 0
633
+ || !driver.promptTerminal || driver.writeCredentialObserved)
634
+ return null;
635
+ return { callbackCount: driver.callbackAttempts, claimCount: driver.claimCount, mutationCount, promptTerminal: approval.terminalPrompt, replayMutationCount: approval.replayMutationCount, settledCount: driver.settledCount, staleReplaySettled: true, writeCredentialAbsent: true };
636
+ }
637
+ case "unit-16m-restart-continuation":
638
+ if (!intendedRestartApproval(approval) || !completeAttemptLedgerLinked || approval.state !== "succeeded" || mutationCount !== 1 || scenarioMutationCount !== 1 || !restartSucceeded || attemptedIndeterminateRetryCount !== 0)
639
+ return null;
640
+ {
641
+ const evidence = exactApprovalEvidence(before, after, approval);
642
+ if (!evidence?.continuation || !evidence.callback || evidence.callback.event !== "telegram.callback_recovery_settled"
643
+ || evidence.callback.meta.acknowledgementState !== "indeterminate_after_restart"
644
+ || evidence.callback.meta.accepted !== true || evidence.callback.meta.reason !== "accepted"
645
+ || typeof evidence.callback.meta.decisionAttemptDigest !== "string" || !SHA256.test(evidence.callback.meta.decisionAttemptDigest))
646
+ return null;
647
+ }
648
+ if (!after.interactiveDriver || after.interactiveDriver.label !== "unit-16m-restart-continuation"
649
+ || !interactiveReceiptBindsApproval(after.interactiveDriver, approval)
650
+ || !after.interactiveDriver.pendingRestored || after.interactiveDriver.pendingDigestBefore !== after.interactiveDriver.pendingDigestAfter || after.interactiveDriver.callbackAttempts !== 1 || after.interactiveDriver.mutationCount !== 1
651
+ || after.interactiveDriver.approvalEpochBefore !== after.interactiveDriver.approvalEpochAfterRestart
652
+ || after.interactiveDriver.continuationEpochAfter <= after.interactiveDriver.approvalEpochAfterRestart
653
+ || approval.continuationEpoch !== after.interactiveDriver.continuationEpochAfter || approval.continuationState !== "completed"
654
+ || after.interactiveDriver.restartCountAfter !== after.interactiveDriver.restartCountBefore + 1
655
+ || !after.interactiveDriver.indeterminateRecoveryObserved || !after.interactiveDriver.attemptedRecoveryReopened
656
+ || !SHA256.test(after.interactiveDriver.attemptedRecordDigest) || !SHA256.test(after.interactiveDriver.recoveredRecordDigest)
657
+ || after.interactiveDriver.attemptedRecordDigest === after.interactiveDriver.recoveredRecordDigest
658
+ || after.interactiveDriver.indeterminateRetryCount !== 0)
659
+ return null;
660
+ return {
661
+ attemptedIndeterminateRetryCount, mutationCount, preAttemptResumed: after.interactiveDriver.pendingRestored,
662
+ butlerRestartObserved: true, checkpointEpochPreserved: true, continuationEpochAdvanced: true,
663
+ restartObserved: restartSucceeded && after.events.filter((entry) => entry.event === "senses.telegram_approved_restart_end" && entry.meta.approvalId === approval.approvalId).length - before.events.filter((entry) => entry.event === "senses.telegram_approved_restart_end" && entry.meta.approvalId === approval.approvalId).length === 1,
664
+ state: approval.state,
665
+ };
666
+ }
667
+ }
668
+ function createSanctuaryScenarioCapture(deps) {
669
+ const receiptRoot = deps.receiptRoot ?? path.join(deps.agentRoot, "state", "acceptance", "receipts");
670
+ const receiptPath = (checkpointDigest) => path.join(receiptRoot, `${checkpointDigest}.json`);
671
+ return async (input) => {
672
+ if (input.phase === "begin") {
673
+ const startedAt = new Date(deps.now()).toISOString();
674
+ const scenarioHandleDigest = (0, node_crypto_1.createHash)("sha256").update((0, node_crypto_1.randomBytes)(32)).digest("hex");
675
+ const checkpointDigest = hash({ label: input.label, gate: input.externalGate, sources: input.sources, startedAt, scenarioHandleDigest });
676
+ const healthScenario = HEALTH_SCENARIO_LABELS.has(input.label);
677
+ const capturedBefore = await deps.readFacts(input.label, scenarioHandleDigest, healthScenario ? { skipContainerSnapshot: true } : undefined);
678
+ const before = { ...capturedBefore, sourceValues: Object.fromEntries(Object.entries(capturedBefore.sourceValues).map(([source, value]) => [source, hash(value)])) };
679
+ const receipt = { schemaVersion: "sanctuary-acceptance-receipt-v1", label: input.label, gate: input.externalGate, sources: [...input.sources], checkpointDigest, scenarioHandleDigest, startedAt, before };
680
+ (0, sanctuary_acceptance_marker_1.writeSanctuaryAcceptanceMarker)("sanctuary", { schemaVersion: "sanctuary-acceptance-marker-v1", label: input.label, scenarioHandleDigest, startedAt }, deps.agentRoot);
681
+ atomicPrivateJson(receiptPath(checkpointDigest), receipt);
682
+ (0, sanctuary_acceptance_marker_1.publishSanctuaryAcceptanceGateStatus)({ label: input.label, gate: input.externalGate, phase: "waiting", startedAt }, deps.gateStatusPath);
683
+ if (healthScenario) {
684
+ if (!deps.healthDriver)
685
+ throw new Error("health scenario driver is unavailable");
686
+ await deps.healthDriver.begin(input.label, scenarioHandleDigest);
687
+ }
688
+ (0, runtime_1.emitNervesEvent)({ component: "daemon", event: "daemon.sanctuary_acceptance_scenario_begin", message: "Sanctuary live acceptance scenario began", meta: { label: input.label, gate: input.externalGate, scenarioHandleDigest } });
689
+ return { state: "waiting", checkpointDigest };
690
+ }
691
+ if (!input.checkpointDigest || !SHA256.test(input.checkpointDigest))
692
+ throw new Error("scenario checkpoint digest is invalid");
693
+ const filePath = receiptPath(input.checkpointDigest);
694
+ const receipt = JSON.parse(fs.readFileSync(filePath, "utf8"));
695
+ if (receipt.checkpointDigest !== input.checkpointDigest || receipt.label !== input.label || receipt.gate !== input.externalGate || JSON.stringify(receipt.sources) !== JSON.stringify(input.sources))
696
+ throw new Error("scenario checkpoint binding mismatch");
697
+ let factOptions;
698
+ if (HEALTH_SCENARIO_LABELS.has(input.label)) {
699
+ if (!deps.healthDriver)
700
+ throw new Error("health scenario driver is unavailable");
701
+ const probe = await deps.healthDriver.poll(input.label, receipt.scenarioHandleDigest);
702
+ if (probe.state === "waiting")
703
+ return { state: "waiting", checkpointDigest: receipt.checkpointDigest };
704
+ factOptions = { containerSnapshot: probe.containerSnapshot };
705
+ }
706
+ if (INTERACTIVE_DRIVER_LABELS.has(input.label)) {
707
+ if (!deps.interactiveDriver)
708
+ throw new Error("interactive scenario driver is unavailable");
709
+ const driven = await deps.interactiveDriver.poll(input.label, receipt.scenarioHandleDigest);
710
+ if (driven.state === "waiting")
711
+ return { state: "waiting", checkpointDigest: receipt.checkpointDigest };
712
+ }
713
+ if (READ_ONLY_DENIAL_LABELS.has(input.label)) {
714
+ if (!deps.denialDriver)
715
+ throw new Error("read-only denial scenario driver is unavailable");
716
+ const driven = await deps.denialDriver.poll(input.label, receipt.scenarioHandleDigest);
717
+ if (driven.state === "waiting")
718
+ return { state: "waiting", checkpointDigest: receipt.checkpointDigest };
719
+ }
720
+ const after = await deps.readFacts(input.label, receipt.scenarioHandleDigest, factOptions);
721
+ if (input.label === "unit-15c-1-no-callback-terminalization" && !receipt.noCallbackBaseline) {
722
+ const activeApproval = after.approvals.find((approval) => approval.state === "proposed" || approval.state === "claimed");
723
+ if (activeApproval) {
724
+ const baseline = {
725
+ approvalId: activeApproval.approvalId,
726
+ offsetDigest: hash(after.sourceValues["telegram-offset"]),
727
+ inboundEventCount: after.events.filter((entry) => entry.event === "telegram.callback_settled" || entry.event === "telegram.callback_recovery_settled" || entry.event === "telegram.update_dropped").length,
728
+ };
729
+ receipt.noCallbackBaseline = baseline;
730
+ receipt.before.sourceValues["no-callback-baseline"] = baseline;
731
+ atomicPrivateJson(filePath, receipt);
732
+ return { state: "waiting", checkpointDigest: receipt.checkpointDigest };
733
+ }
734
+ }
735
+ const candidate = deriveSanctuaryScenarioAssertions(input.label, receipt.before, after, deps.now(), receipt.scenarioHandleDigest);
736
+ if (!candidate)
737
+ return { state: "waiting", checkpointDigest: receipt.checkpointDigest };
738
+ const assertions = (0, sanctuary_acceptance_harness_1.validateSanctuaryUnit16EvidenceAssertions)(input.label, candidate);
739
+ const sourceDigests = Object.fromEntries(input.sources.map((source) => [source, hash(after.sourceValues[source])]));
740
+ if (HEALTH_SCENARIO_LABELS.has(input.label))
741
+ await deps.healthDriver.recover(input.label, receipt.scenarioHandleDigest);
742
+ if (INTERACTIVE_DRIVER_LABELS.has(input.label) && await deps.interactiveDriver.complete(input.label, receipt.scenarioHandleDigest) !== "complete") {
743
+ throw new Error("interactive scenario requires inspect-before-retry");
744
+ }
745
+ if (INTERACTIVE_DRIVER_LABELS.has(input.label))
746
+ await deps.interactiveDriver.cleanup(input.label, receipt.scenarioHandleDigest);
747
+ if (READ_ONLY_DENIAL_LABELS.has(input.label))
748
+ await deps.denialDriver.complete(input.label, receipt.scenarioHandleDigest);
749
+ (0, sanctuary_acceptance_marker_1.publishSanctuaryAcceptanceGateStatus)({ label: input.label, gate: input.externalGate, phase: "complete", startedAt: receipt.startedAt }, deps.gateStatusPath);
750
+ (0, sanctuary_acceptance_marker_1.clearSanctuaryAcceptanceMarker)("sanctuary", receipt.scenarioHandleDigest, deps.agentRoot);
751
+ fs.unlinkSync(filePath);
752
+ (0, runtime_1.emitNervesEvent)({ component: "daemon", event: "daemon.sanctuary_acceptance_scenario_end", message: "Sanctuary live acceptance scenario completed", meta: { label: input.label, scenarioHandleDigest: receipt.scenarioHandleDigest } });
753
+ return { state: "complete", checkpointDigest: receipt.checkpointDigest, sourceDigests, assertions };
754
+ };
755
+ }
756
+ function boundedReceiptEntries(receiptRoot) {
757
+ const rootHandle = fs.openSync(receiptRoot, fs.constants.O_RDONLY | fs.constants.O_DIRECTORY | fs.constants.O_NOFOLLOW);
758
+ try {
759
+ const rootMetadata = fs.fstatSync(rootHandle);
760
+ const pathMetadata = fs.lstatSync(receiptRoot);
761
+ if (!rootMetadata.isDirectory() || !pathMetadata.isDirectory() || rootMetadata.dev !== pathMetadata.dev || rootMetadata.ino !== pathMetadata.ino) {
762
+ throw new Error("acceptance receipt root changed during cleanup");
763
+ }
764
+ const directory = fs.opendirSync(receiptRoot);
765
+ const entries = [];
766
+ try {
767
+ for (;;) {
768
+ const entry = directory.readSync();
769
+ if (!entry)
770
+ break;
771
+ entries.push(entry);
772
+ if (entries.length === 33)
773
+ throw new Error("acceptance receipt cleanup exceeds its bound");
774
+ }
775
+ }
776
+ finally {
777
+ directory.closeSync();
778
+ }
779
+ const finalMetadata = fs.lstatSync(receiptRoot);
780
+ if (!finalMetadata.isDirectory() || rootMetadata.dev !== finalMetadata.dev || rootMetadata.ino !== finalMetadata.ino) {
781
+ throw new Error("acceptance receipt root changed during cleanup");
782
+ }
783
+ return entries;
784
+ }
785
+ finally {
786
+ fs.closeSync(rootHandle);
787
+ }
788
+ }
789
+ function prepareSafeQuarantineRoot(sourceParent, sourceParentHandle) {
790
+ const quarantineRoot = path.join(sourceParent, "quarantine");
791
+ const rejectedPath = path.join(sourceParent, `.quarantine-rejected-${(0, node_crypto_1.randomUUID)()}`);
792
+ const boundQuarantineRoot = (0, sanctuary_acceptance_marker_1.boundDirectoryEntryPath)(sourceParentHandle, sourceParent, path.basename(quarantineRoot));
793
+ const boundRejectedPath = (0, sanctuary_acceptance_marker_1.boundDirectoryEntryPath)(sourceParentHandle, sourceParent, path.basename(rejectedPath));
794
+ let quarantineExists = false;
795
+ let rejectedMetadata = null;
796
+ try {
797
+ const existing = fs.lstatSync(boundQuarantineRoot);
798
+ if (existing.isDirectory())
799
+ quarantineExists = true;
800
+ else {
801
+ (0, sanctuary_acceptance_marker_1.secureRenameBoundInodeSync)(sourceParentHandle, path.basename(quarantineRoot), sourceParentHandle, path.basename(rejectedPath), existing);
802
+ const rejected = fs.lstatSync(boundRejectedPath);
803
+ if (rejected.dev !== existing.dev || rejected.ino !== existing.ino)
804
+ throw new Error("acceptance quarantine rejection changed during move");
805
+ rejectedMetadata = rejected;
806
+ }
807
+ }
808
+ catch (error) {
809
+ if (error.code !== "ENOENT")
810
+ throw error;
811
+ }
812
+ if (!quarantineExists)
813
+ fs.mkdirSync(boundQuarantineRoot, { mode: 0o700 });
814
+ const quarantineHandle = fs.openSync(boundQuarantineRoot, fs.constants.O_RDONLY | fs.constants.O_DIRECTORY | fs.constants.O_NOFOLLOW);
815
+ try {
816
+ const metadata = fs.fstatSync(quarantineHandle);
817
+ const pathMetadata = fs.lstatSync(boundQuarantineRoot);
818
+ if (!metadata.isDirectory() || !pathMetadata.isDirectory() || metadata.dev !== pathMetadata.dev || metadata.ino !== pathMetadata.ino) {
819
+ throw new Error("acceptance quarantine root changed");
820
+ }
821
+ fs.fchmodSync(quarantineHandle, 0o700);
822
+ if (rejectedMetadata) {
823
+ (0, sanctuary_acceptance_marker_1.secureRenameBoundInodeSync)(sourceParentHandle, path.basename(rejectedPath), quarantineHandle, path.basename(rejectedPath).slice(1), rejectedMetadata);
824
+ }
825
+ fs.fsyncSync(quarantineHandle);
826
+ fs.fsyncSync(sourceParentHandle);
827
+ return { quarantineRoot, quarantineHandle };
828
+ }
829
+ catch (error) {
830
+ fs.closeSync(quarantineHandle);
831
+ throw error;
832
+ }
833
+ }
834
+ function durableQuarantineReceiptRoot(receiptRoot) {
835
+ const sourceParent = path.dirname(receiptRoot);
836
+ const sourceParentHandle = fs.openSync(sourceParent, fs.constants.O_RDONLY | fs.constants.O_DIRECTORY | fs.constants.O_NOFOLLOW);
837
+ let rootHandle = null;
838
+ let quarantineHandle = null;
839
+ let quarantineRoot = "";
840
+ try {
841
+ const sourceParentMetadata = fs.fstatSync(sourceParentHandle);
842
+ const sourceParentPathMetadata = fs.lstatSync(sourceParent);
843
+ if (!sourceParentMetadata.isDirectory() || !sourceParentPathMetadata.isDirectory()
844
+ || sourceParentMetadata.dev !== sourceParentPathMetadata.dev || sourceParentMetadata.ino !== sourceParentPathMetadata.ino)
845
+ throw new Error("acceptance receipt parent changed during quarantine");
846
+ const prepared = prepareSafeQuarantineRoot(sourceParent, sourceParentHandle);
847
+ quarantineRoot = prepared.quarantineRoot;
848
+ quarantineHandle = prepared.quarantineHandle;
849
+ const boundReceiptRoot = (0, sanctuary_acceptance_marker_1.boundDirectoryEntryPath)(sourceParentHandle, sourceParent, path.basename(receiptRoot));
850
+ const rootPathMetadata = fs.lstatSync(boundReceiptRoot);
851
+ if (rootPathMetadata.isDirectory() || rootPathMetadata.isFile()) {
852
+ rootHandle = fs.openSync(boundReceiptRoot, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK);
853
+ }
854
+ if (rootHandle !== null) {
855
+ const rootMetadata = fs.fstatSync(rootHandle);
856
+ const reboundMetadata = fs.lstatSync(boundReceiptRoot);
857
+ if (rootMetadata.dev !== reboundMetadata.dev || rootMetadata.ino !== reboundMetadata.ino)
858
+ throw new Error("acceptance receipt root changed during cleanup");
859
+ }
860
+ const finalRootPathMetadata = fs.lstatSync(boundReceiptRoot);
861
+ if (finalRootPathMetadata.dev !== rootPathMetadata.dev || finalRootPathMetadata.ino !== rootPathMetadata.ino)
862
+ throw new Error("acceptance receipt root changed before quarantine move");
863
+ const quarantinePath = path.join(quarantineRoot, `receipts-${(0, node_crypto_1.randomUUID)()}`);
864
+ (0, sanctuary_acceptance_marker_1.secureRenameBoundInodeSync)(sourceParentHandle, path.basename(receiptRoot), quarantineHandle, path.basename(quarantinePath), rootPathMetadata);
865
+ const movedMetadata = fs.lstatSync((0, sanctuary_acceptance_marker_1.boundDirectoryEntryPath)(quarantineHandle, quarantineRoot, path.basename(quarantinePath)));
866
+ if (movedMetadata.dev !== rootPathMetadata.dev || movedMetadata.ino !== rootPathMetadata.ino) {
867
+ throw new Error("acceptance receipt quarantine changed during move");
868
+ }
869
+ if (rootHandle !== null)
870
+ fs.fsyncSync(rootHandle);
871
+ fs.fsyncSync(sourceParentHandle);
872
+ fs.fsyncSync(quarantineHandle);
873
+ fs.mkdirSync(boundReceiptRoot, { mode: 0o700 });
874
+ const replacementHandle = fs.openSync(boundReceiptRoot, fs.constants.O_RDONLY | fs.constants.O_DIRECTORY | fs.constants.O_NOFOLLOW);
875
+ try {
876
+ fs.fsyncSync(replacementHandle);
877
+ }
878
+ finally {
879
+ fs.closeSync(replacementHandle);
880
+ }
881
+ fs.fsyncSync(sourceParentHandle);
882
+ }
883
+ finally {
884
+ fs.closeSync(sourceParentHandle);
885
+ if (quarantineHandle !== null)
886
+ fs.closeSync(quarantineHandle);
887
+ if (rootHandle !== null)
888
+ fs.closeSync(rootHandle);
889
+ }
890
+ }
891
+ function finalizeSanctuaryScenarioCapture(gateStatusPath, configuredReceiptRoot, configuredAgentRoot) {
892
+ const receiptRoot = configuredReceiptRoot ?? path.join(configuredAgentRoot ?? (0, identity_1.getAgentRoot)("sanctuary"), "state", "acceptance", "receipts");
893
+ const errors = [];
894
+ let marker = null;
895
+ let markerReadable = true;
896
+ let normalCleanupFailed = false;
897
+ try {
898
+ marker = (0, sanctuary_acceptance_marker_1.readSanctuaryAcceptanceMarker)("sanctuary", configuredAgentRoot);
899
+ }
900
+ catch (error) {
901
+ markerReadable = false;
902
+ errors.push(error);
903
+ }
904
+ let activeReceipt = null;
905
+ let receiptSetCanonical = true;
906
+ try {
907
+ const entries = boundedReceiptEntries(receiptRoot);
908
+ if (entries.length !== (marker ? 1 : 0))
909
+ throw new Error(marker ? "active acceptance receipt is absent or ambiguous" : "acceptance receipt exists without an active marker");
910
+ if (marker) {
911
+ const entry = entries[0];
912
+ if (!entry.isFile() || !entry.name.endsWith(".json") || !SHA256.test(entry.name.slice(0, -5)))
913
+ throw new Error("acceptance receipt cleanup found a noncanonical entry");
914
+ const filePath = path.join(receiptRoot, entry.name);
915
+ const handle = fs.openSync(filePath, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
916
+ try {
917
+ const metadata = fs.fstatSync(handle);
918
+ if (!metadata.isFile() || metadata.size > 4 * 1024 * 1024 || (metadata.mode & 0o777) !== 0o600)
919
+ throw new Error("acceptance receipt cleanup found an invalid file");
920
+ const receipt = JSON.parse(fs.readFileSync(handle, "utf8"));
921
+ const pathMetadata = fs.lstatSync(filePath);
922
+ if (!pathMetadata.isFile() || pathMetadata.dev !== metadata.dev || pathMetadata.ino !== metadata.ino)
923
+ throw new Error("acceptance receipt changed during cleanup");
924
+ if (receipt.checkpointDigest !== entry.name.slice(0, -5) || receipt.scenarioHandleDigest !== marker.scenarioHandleDigest
925
+ || receipt.label !== marker.label || receipt.startedAt !== marker.startedAt)
926
+ throw new Error("active acceptance receipt is absent or ambiguous");
927
+ activeReceipt = filePath;
928
+ }
929
+ finally {
930
+ fs.closeSync(handle);
931
+ }
932
+ }
933
+ }
934
+ catch (error) {
935
+ if (error.code !== "ENOENT") {
936
+ receiptSetCanonical = false;
937
+ errors.push(error);
938
+ }
939
+ else if (marker) {
940
+ receiptSetCanonical = false;
941
+ errors.push(new Error("active acceptance receipt is absent or ambiguous"));
942
+ }
943
+ }
944
+ if (markerReadable && marker && receiptSetCanonical && activeReceipt) {
945
+ try {
946
+ fs.unlinkSync(activeReceipt);
947
+ }
948
+ catch (error) {
949
+ normalCleanupFailed = true;
950
+ errors.push(error);
951
+ }
952
+ if (!normalCleanupFailed) {
953
+ try {
954
+ (0, sanctuary_acceptance_marker_1.clearSanctuaryAcceptanceMarker)("sanctuary", marker.scenarioHandleDigest, configuredAgentRoot);
955
+ }
956
+ catch (error) {
957
+ normalCleanupFailed = true;
958
+ errors.push(error);
959
+ }
960
+ }
961
+ }
962
+ if (!markerReadable || !receiptSetCanonical || (marker && activeReceipt === null) || normalCleanupFailed) {
963
+ try {
964
+ durableQuarantineReceiptRoot(receiptRoot);
965
+ }
966
+ catch (error) {
967
+ if (error.code !== "ENOENT")
968
+ errors.push(error);
969
+ }
970
+ try {
971
+ (0, sanctuary_acceptance_marker_1.quarantineSanctuaryAcceptanceMarker)("sanctuary", configuredAgentRoot);
972
+ }
973
+ catch (error) {
974
+ errors.push(error);
975
+ }
976
+ }
977
+ try {
978
+ (0, sanctuary_acceptance_marker_1.clearSanctuaryAcceptanceGateStatus)(gateStatusPath);
979
+ }
980
+ catch (error) {
981
+ errors.push(error);
982
+ }
983
+ if (errors.length > 0)
984
+ throw new AggregateError(errors, "Sanctuary scenario finalization failed");
985
+ }