@ouro.bot/cli 0.1.0-alpha.83 → 0.1.0-alpha.831

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 (647) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +154 -23
  3. package/RepairGuide.ouro/agent.json +5 -0
  4. package/RepairGuide.ouro/psyche/IDENTITY.md +19 -0
  5. package/RepairGuide.ouro/psyche/SOUL.md +55 -0
  6. package/RepairGuide.ouro/skills/diagnose-broken-remote.md +63 -0
  7. package/RepairGuide.ouro/skills/diagnose-stacked-typed-issues.md +35 -0
  8. package/RepairGuide.ouro/skills/diagnose-sync-blocked.md +54 -0
  9. package/RepairGuide.ouro/skills/diagnose-vault-expired.md +60 -0
  10. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +4 -2
  11. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +2 -2
  12. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +1 -1
  13. package/assets/bluebubbles-host +264 -0
  14. package/assets/sanctuary-host-launcher.sh +16 -0
  15. package/changelog.json +5308 -16
  16. package/deploy/unraid/Dockerfile +54 -0
  17. package/deploy/unraid/README.txt +2983 -0
  18. package/deploy/unraid/audit-container-spec.sh +4 -0
  19. package/deploy/unraid/container-runtime.json +1 -0
  20. package/deploy/unraid/docker-man-template-transaction.mjs +711 -0
  21. package/deploy/unraid/docker-man-template-xml.cjs +105 -0
  22. package/deploy/unraid/migrate-sanctuary-bundle.mjs +60 -0
  23. package/deploy/unraid/ouro-events/bootstrap-spool.sh +92 -0
  24. package/deploy/unraid/ouro-events/emit-event.mjs +310 -0
  25. package/deploy/unraid/ouro-events/emit-usenet-event.sh +54 -0
  26. package/deploy/unraid/ouro-events/install-usenet-guard.sh +435 -0
  27. package/deploy/unraid/ouro-events/usenet-health.sh +176 -0
  28. package/deploy/unraid/sanctuary-acceptance-adapter.sh +43 -0
  29. package/deploy/unraid/sanctuary-acceptance-contract.json +198 -0
  30. package/deploy/unraid/sanctuary-acceptance-harness.sh +23 -0
  31. package/deploy/unraid/sanctuary-authority-installation.json +36 -0
  32. package/deploy/unraid/sanctuary-authority-service.sh +30 -0
  33. package/deploy/unraid/sanctuary-deployment-target.mjs +710 -0
  34. package/deploy/unraid/sanctuary-safe-rename.py +24 -0
  35. package/deploy/unraid/sanctuary-unit16-host-broker.mjs +2084 -0
  36. package/deploy/unraid/sanctuary-unit16-run.sh +560 -0
  37. package/deploy/unraid/sanctuary-unit18-target-audit.sh +27 -0
  38. package/deploy/unraid/sanctuary.ouro/agent.json +66 -0
  39. package/deploy/unraid/sanctuary.ouro/arc/README.md +3 -0
  40. package/deploy/unraid/sanctuary.ouro/bundle-meta.json +5 -0
  41. package/deploy/unraid/sanctuary.ouro/habits/sanctuary-health.md +8 -0
  42. package/deploy/unraid/sanctuary.ouro/mcp/media-mcp.mjs +795 -0
  43. package/deploy/unraid/sanctuary.ouro/provider-readiness.json +11 -0
  44. package/deploy/unraid/sanctuary.ouro/psyche/ASPIRATIONS.md +5 -0
  45. package/deploy/unraid/sanctuary.ouro/psyche/IDENTITY.md +5 -0
  46. package/deploy/unraid/sanctuary.ouro/psyche/LORE.md +15 -0
  47. package/deploy/unraid/sanctuary.ouro/psyche/SOUL.md +25 -0
  48. package/deploy/unraid/sanctuary.ouro/psyche/TACIT.md +15 -0
  49. package/deploy/unraid/sanctuary.ouro/state/policy/steward.json +7 -0
  50. package/deploy/unraid/sanctuary.ouro/tool-profiles.json +135 -0
  51. package/deploy/unraid/sanctuary.xml +28 -0
  52. package/dist/a2a/card.js +57 -0
  53. package/dist/a2a/client.js +180 -0
  54. package/dist/a2a/config.js +50 -0
  55. package/dist/a2a/delegation-stores.js +56 -0
  56. package/dist/a2a/did-resolution.js +93 -0
  57. package/dist/a2a/identity.js +170 -0
  58. package/dist/a2a/inbound-share.js +107 -0
  59. package/dist/a2a/mission-result-wire.js +196 -0
  60. package/dist/a2a/onboarding.js +115 -0
  61. package/dist/a2a/pin-store.js +132 -0
  62. package/dist/a2a/seen-ledger.js +120 -0
  63. package/dist/a2a/server.js +604 -0
  64. package/dist/a2a/task-store.js +69 -0
  65. package/dist/a2a/transport.js +45 -0
  66. package/dist/a2a/types.js +3 -0
  67. package/dist/arc/attention-types.js +8 -0
  68. package/dist/arc/cares.js +360 -0
  69. package/dist/arc/episodes.js +118 -0
  70. package/dist/arc/evolution.js +490 -0
  71. package/dist/arc/flight-recorder.js +1034 -0
  72. package/dist/arc/intentions.js +134 -0
  73. package/dist/arc/json-store.js +117 -0
  74. package/dist/arc/obligations.js +386 -0
  75. package/dist/arc/packets.js +336 -0
  76. package/dist/arc/presence.js +185 -0
  77. package/dist/arc/task-lifecycle.js +57 -0
  78. package/dist/commerce/store.js +755 -0
  79. package/dist/commerce/types.js +3 -0
  80. package/dist/heart/active-work.js +798 -40
  81. package/dist/heart/agent-entry.js +252 -3
  82. package/dist/heart/approval-files.js +113 -0
  83. package/dist/heart/approval-store.js +960 -0
  84. package/dist/heart/attachments/image-normalize.js +194 -0
  85. package/dist/heart/attachments/materialize.js +97 -0
  86. package/dist/heart/attachments/originals.js +88 -0
  87. package/dist/heart/attachments/render.js +29 -0
  88. package/dist/heart/attachments/sources/bluebubbles.js +156 -0
  89. package/dist/heart/attachments/sources/cli-local-file.js +78 -0
  90. package/dist/heart/attachments/sources/index.js +18 -0
  91. package/dist/heart/attachments/sources/telegram.js +71 -0
  92. package/dist/heart/attachments/store.js +132 -0
  93. package/dist/heart/attachments/types.js +93 -0
  94. package/dist/heart/auth/auth-flow.js +495 -0
  95. package/dist/heart/autonomy-budget.js +478 -0
  96. package/dist/heart/awaiting/await-alert.js +148 -0
  97. package/dist/heart/awaiting/await-expiry.js +143 -0
  98. package/dist/heart/awaiting/await-loader.js +91 -0
  99. package/dist/heart/awaiting/await-parser.js +149 -0
  100. package/dist/heart/awaiting/await-runtime-state.js +111 -0
  101. package/dist/heart/awaiting/await-scheduler.js +382 -0
  102. package/dist/heart/background-operations.js +281 -0
  103. package/dist/heart/bridges/manager.js +137 -17
  104. package/dist/heart/bridges/store.js +14 -2
  105. package/dist/heart/bundle-state.js +168 -0
  106. package/dist/heart/commitments.js +44 -18
  107. package/dist/heart/config-registry.js +331 -0
  108. package/dist/heart/config.js +174 -131
  109. package/dist/heart/context-loss-gauntlet.js +387 -0
  110. package/dist/heart/context-loss-sentinel.js +1157 -0
  111. package/dist/heart/core.js +2169 -581
  112. package/dist/heart/cross-chat-delivery.js +9 -22
  113. package/dist/heart/daemon/agent-config-check.js +451 -0
  114. package/dist/heart/daemon/agent-discovery.js +185 -3
  115. package/dist/heart/daemon/agent-service.js +542 -0
  116. package/dist/heart/daemon/agentic-repair.js +547 -0
  117. package/dist/heart/daemon/await-private-wake.js +59 -0
  118. package/dist/heart/daemon/bluebubbles-cli-integration.js +71 -0
  119. package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
  120. package/dist/heart/daemon/bluebubbles-host-protocol.js +433 -0
  121. package/dist/heart/daemon/bluebubbles-host.js +273 -0
  122. package/dist/heart/daemon/boot-sync-probe.js +197 -0
  123. package/dist/heart/daemon/cadence.js +180 -0
  124. package/dist/heart/daemon/cli-defaults.js +854 -0
  125. package/dist/heart/daemon/cli-desk.js +322 -0
  126. package/dist/heart/daemon/cli-exec.js +8822 -0
  127. package/dist/heart/daemon/cli-help.js +716 -0
  128. package/dist/heart/daemon/cli-parse.js +2771 -0
  129. package/dist/heart/daemon/cli-render-doctor.js +57 -0
  130. package/dist/heart/daemon/cli-render.js +777 -0
  131. package/dist/heart/daemon/cli-types.js +8 -0
  132. package/dist/heart/daemon/connect-bay.js +323 -0
  133. package/dist/heart/daemon/container-credential-bootstrap.js +273 -0
  134. package/dist/heart/daemon/container-healthcheck.js +112 -0
  135. package/dist/heart/daemon/container-runtime.js +97 -0
  136. package/dist/heart/daemon/container-spec-auditor-main.js +171 -0
  137. package/dist/heart/daemon/container-spec-auditor.js +229 -0
  138. package/dist/heart/daemon/daemon-bootstrap-startup.js +115 -0
  139. package/dist/heart/daemon/daemon-cli.js +29 -2349
  140. package/dist/heart/daemon/daemon-entry.js +930 -11
  141. package/dist/heart/daemon/daemon-health.js +186 -0
  142. package/dist/heart/daemon/daemon-rollup.js +57 -0
  143. package/dist/heart/daemon/daemon-runtime-sync.js +88 -13
  144. package/dist/heart/daemon/daemon-tombstone.js +236 -0
  145. package/dist/heart/daemon/daemon.js +2084 -105
  146. package/dist/heart/daemon/dns-workflow.js +394 -0
  147. package/dist/heart/daemon/doctor-types.js +8 -0
  148. package/dist/heart/daemon/doctor.js +1755 -0
  149. package/dist/heart/daemon/freshness.js +345 -0
  150. package/dist/heart/daemon/habit-private-wake.js +61 -0
  151. package/dist/heart/daemon/health-monitor.js +142 -11
  152. package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
  153. package/dist/heart/daemon/hooks/bundle-meta.js +135 -1
  154. package/dist/heart/daemon/http-health-probe.js +80 -0
  155. package/dist/heart/daemon/human-command-screens.js +234 -0
  156. package/dist/heart/daemon/human-readiness.js +114 -0
  157. package/dist/heart/daemon/inner-status.js +78 -0
  158. package/dist/heart/daemon/interactive-repair.js +394 -0
  159. package/dist/heart/daemon/launchd.js +37 -8
  160. package/dist/heart/daemon/log-tailer.js +79 -10
  161. package/dist/heart/daemon/logs-prune.js +132 -0
  162. package/dist/heart/daemon/mcp-canary.js +524 -0
  163. package/dist/heart/daemon/message-router.js +65 -5
  164. package/dist/heart/daemon/migrate-to-desk.js +848 -0
  165. package/dist/heart/daemon/os-cron-deps.js +135 -0
  166. package/dist/heart/daemon/os-cron.js +22 -14
  167. package/dist/heart/daemon/ouro-bot-entry.js +4 -2
  168. package/dist/heart/daemon/ouro-entry.js +3 -1
  169. package/dist/heart/daemon/plugin-cli.js +432 -0
  170. package/dist/heart/daemon/process-manager.js +733 -61
  171. package/dist/heart/daemon/provider-discovery.js +137 -0
  172. package/dist/heart/daemon/provider-ping-progress.js +83 -0
  173. package/dist/heart/daemon/prunable-bundle.js +144 -0
  174. package/dist/heart/daemon/pulse.js +511 -0
  175. package/dist/heart/daemon/readiness-repair.js +365 -0
  176. package/dist/heart/daemon/run-hooks.js +2 -0
  177. package/dist/heart/daemon/runtime-logging.js +47 -14
  178. package/dist/heart/daemon/runtime-metadata.js +2 -30
  179. package/dist/heart/daemon/runtime-mode.js +13 -2
  180. package/dist/heart/daemon/safe-mode.js +161 -0
  181. package/dist/heart/daemon/sanctuary-acceptance-adapter.js +2654 -0
  182. package/dist/heart/daemon/sanctuary-acceptance-harness.js +1830 -0
  183. package/dist/heart/daemon/sanctuary-acceptance-marker.js +267 -0
  184. package/dist/heart/daemon/sanctuary-acceptance-scenarios.js +955 -0
  185. package/dist/heart/daemon/sanctuary-authority-codec.js +86 -0
  186. package/dist/heart/daemon/sanctuary-authority-epoch.js +256 -0
  187. package/dist/heart/daemon/sanctuary-authority-installation.js +140 -0
  188. package/dist/heart/daemon/sanctuary-authority-ledger.js +241 -0
  189. package/dist/heart/daemon/sanctuary-authority-root-lifecycle.js +819 -0
  190. package/dist/heart/daemon/sanctuary-authority-vault-migration.js +79 -0
  191. package/dist/heart/daemon/sanctuary-bundle-migration.js +729 -0
  192. package/dist/heart/daemon/sanctuary-host-authority.js +1008 -0
  193. package/dist/heart/daemon/sanctuary-host-detached-supervisor.js +494 -0
  194. package/dist/heart/daemon/sanctuary-host-executor.js +670 -0
  195. package/dist/heart/daemon/sanctuary-host-linux-kernel.js +334 -0
  196. package/dist/heart/daemon/sanctuary-host-supervisor-entry.js +207 -0
  197. package/dist/heart/daemon/sanctuary-host-supervisor.js +95 -0
  198. package/dist/heart/daemon/sanctuary-package-management.js +103 -0
  199. package/dist/heart/daemon/sanctuary-scheduler-liveness.js +227 -0
  200. package/dist/heart/daemon/sanctuary-scheduler-origin.js +132 -0
  201. package/dist/heart/daemon/sanctuary-telegram-authority-entry.js +445 -0
  202. package/dist/heart/daemon/sanctuary-telegram-authority-gateway.js +585 -0
  203. package/dist/heart/daemon/sanctuary-telegram-authority-service.js +630 -0
  204. package/dist/heart/daemon/sense-manager.js +699 -48
  205. package/dist/heart/daemon/session-id-resolver.js +131 -0
  206. package/dist/heart/daemon/skill-management-installer.js +1 -1
  207. package/dist/heart/daemon/socket-client.js +180 -12
  208. package/dist/heart/daemon/stale-bundle-prune.js +113 -0
  209. package/dist/heart/daemon/startup-tui.js +339 -0
  210. package/dist/heart/daemon/supercronic-supervisor.js +282 -0
  211. package/dist/heart/daemon/task-scheduler.js +117 -39
  212. package/dist/heart/daemon/terminal-ui.js +499 -0
  213. package/dist/heart/daemon/thoughts.js +138 -54
  214. package/dist/heart/daemon/up-progress.js +366 -0
  215. package/dist/heart/daemon/vault-items.js +56 -0
  216. package/dist/heart/delegation.js +1 -4
  217. package/dist/heart/external-events/router.js +1430 -0
  218. package/dist/heart/frontend-approval-runtime.js +506 -0
  219. package/dist/heart/frontend-journal.js +215 -0
  220. package/dist/heart/frontend-session-service.js +237 -0
  221. package/dist/heart/frontend-socket-client.js +164 -0
  222. package/dist/heart/frontend-socket.js +400 -0
  223. package/dist/heart/habits/habit-cancel.js +810 -0
  224. package/dist/heart/habits/habit-lifecycle.js +1327 -0
  225. package/dist/heart/habits/habit-migration.js +194 -0
  226. package/dist/heart/habits/habit-parser.js +278 -0
  227. package/dist/heart/habits/habit-runtime-state.js +93 -0
  228. package/dist/heart/habits/habit-scheduler.js +653 -0
  229. package/dist/heart/habits/habit-session-summary.js +318 -0
  230. package/dist/heart/habits/habit-session.js +636 -0
  231. package/dist/heart/{daemon → hatch}/hatch-flow.js +55 -62
  232. package/dist/heart/{daemon → hatch}/hatch-specialist.js +6 -8
  233. package/dist/heart/{daemon → hatch}/specialist-prompt.js +12 -9
  234. package/dist/heart/{daemon → hatch}/specialist-tools.js +53 -18
  235. package/dist/heart/identity.js +196 -59
  236. package/dist/heart/kept-notes.js +289 -0
  237. package/dist/heart/kicks.js +1 -1
  238. package/dist/heart/machine-identity.js +161 -0
  239. package/dist/heart/mail-import-discovery.js +356 -0
  240. package/dist/heart/mailbox/mailbox-http-hooks.js +98 -0
  241. package/dist/heart/mailbox/mailbox-http-response.js +7 -0
  242. package/dist/heart/mailbox/mailbox-http-routes.js +398 -0
  243. package/dist/heart/mailbox/mailbox-http-static.js +103 -0
  244. package/dist/heart/mailbox/mailbox-http-transport.js +129 -0
  245. package/dist/heart/mailbox/mailbox-http.js +99 -0
  246. package/dist/heart/mailbox/mailbox-read.js +39 -0
  247. package/dist/heart/mailbox/mailbox-types.js +27 -0
  248. package/dist/heart/mailbox/mailbox-view.js +197 -0
  249. package/dist/heart/mailbox/readers/agent-machine.js +428 -0
  250. package/dist/heart/mailbox/readers/continuity-readers.js +329 -0
  251. package/dist/heart/mailbox/readers/mail.js +375 -0
  252. package/dist/heart/mailbox/readers/runtime-readers.js +823 -0
  253. package/dist/heart/mailbox/readers/sessions.js +231 -0
  254. package/dist/heart/mailbox/readers/shared.js +111 -0
  255. package/dist/heart/mcp/mcp-server.js +696 -0
  256. package/dist/heart/migrate-config.js +100 -0
  257. package/dist/heart/model-capabilities.js +11 -0
  258. package/dist/heart/orientation-frame.js +345 -0
  259. package/dist/heart/platform.js +81 -0
  260. package/dist/heart/primitives.js +1 -1
  261. package/dist/heart/private-runtime/decision-renderer.js +158 -0
  262. package/dist/heart/private-runtime/index.js +18 -0
  263. package/dist/heart/private-runtime/ledger.js +221 -0
  264. package/dist/heart/private-runtime/policy.js +252 -0
  265. package/dist/heart/private-runtime/types.js +2 -0
  266. package/dist/heart/progress-story.js +1 -1
  267. package/dist/heart/provider-attempt.js +137 -0
  268. package/dist/heart/provider-binding-resolver.js +273 -0
  269. package/dist/heart/provider-credentials.js +449 -0
  270. package/dist/heart/provider-failover.js +311 -0
  271. package/dist/heart/provider-models.js +96 -0
  272. package/dist/heart/provider-ping.js +265 -0
  273. package/dist/heart/provider-readiness-cache.js +119 -0
  274. package/dist/heart/provider-visibility.js +188 -0
  275. package/dist/heart/providers/anthropic-token.js +131 -0
  276. package/dist/heart/providers/anthropic.js +173 -60
  277. package/dist/heart/providers/azure.js +23 -11
  278. package/dist/heart/providers/error-classification.js +127 -0
  279. package/dist/heart/providers/github-copilot.js +34 -38
  280. package/dist/heart/providers/minimax-vlm.js +189 -0
  281. package/dist/heart/providers/minimax.js +26 -8
  282. package/dist/heart/providers/openai-codex-token.js +349 -0
  283. package/dist/heart/providers/openai-codex.js +55 -40
  284. package/dist/heart/providers/openai-compatible.js +177 -0
  285. package/dist/heart/run-ledger.js +247 -0
  286. package/dist/heart/runtime-capability-check.js +170 -0
  287. package/dist/heart/runtime-credentials.js +607 -0
  288. package/dist/heart/runtime-cwd.js +87 -0
  289. package/dist/heart/sense-truth.js +22 -4
  290. package/dist/heart/session-activity.js +92 -24
  291. package/dist/heart/session-events.js +1504 -0
  292. package/dist/heart/session-playback-cli-main.js +5 -0
  293. package/dist/heart/session-playback-cli.js +36 -0
  294. package/dist/heart/session-playback.js +231 -0
  295. package/dist/heart/session-redaction-repair-cli-main.js +31 -0
  296. package/dist/heart/session-redaction-repair.js +552 -0
  297. package/dist/heart/session-stats-cli-main.js +5 -0
  298. package/dist/heart/session-stats.js +183 -0
  299. package/dist/heart/session-transcript.js +133 -0
  300. package/dist/heart/start-of-turn-packet.js +372 -0
  301. package/dist/heart/steward-policy.js +618 -0
  302. package/dist/heart/streaming.js +604 -207
  303. package/dist/heart/structured-output.js +196 -0
  304. package/dist/heart/sync-classification.js +176 -0
  305. package/dist/heart/sync.js +449 -0
  306. package/dist/heart/target-resolution.js +11 -7
  307. package/dist/heart/tempo.js +93 -0
  308. package/dist/heart/temporal-view.js +41 -0
  309. package/dist/heart/timeouts.js +101 -0
  310. package/dist/heart/tool-activity-callbacks.js +59 -0
  311. package/dist/heart/tool-approval.js +382 -0
  312. package/dist/heart/tool-description.js +155 -0
  313. package/dist/heart/tool-friction.js +55 -0
  314. package/dist/heart/tool-loop.js +200 -0
  315. package/dist/heart/turn-context.js +482 -0
  316. package/dist/heart/turn-execution-lease.js +30 -0
  317. package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +6 -5
  318. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
  319. package/dist/heart/versioning/ouro-path-installer.js +432 -0
  320. package/dist/heart/versioning/ouro-recovery-launcher.js +76 -0
  321. package/dist/heart/versioning/ouro-version-manager.js +409 -0
  322. package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
  323. package/dist/heart/{daemon → versioning}/update-checker.js +6 -1
  324. package/dist/heart/versioning/update-hooks.js +154 -0
  325. package/dist/heart/versioning/version-intent.js +114 -0
  326. package/dist/heart/work-card.js +380 -0
  327. package/dist/mailbox-ui/assets/index-Cyv4mw3Y.js +1 -0
  328. package/dist/mailbox-ui/assets/index-Du_9G9WO.css +1 -0
  329. package/dist/mailbox-ui/assets/vendor-CcN1XpQ9.js +61 -0
  330. package/dist/mailbox-ui/index.html +16 -0
  331. package/dist/mailroom/attention.js +167 -0
  332. package/dist/mailroom/autonomy.js +209 -0
  333. package/dist/mailroom/blob-store.js +889 -0
  334. package/dist/mailroom/body-cache.js +61 -0
  335. package/dist/mailroom/cache-sync-cli.js +58 -0
  336. package/dist/mailroom/core.js +803 -0
  337. package/dist/mailroom/entry.js +160 -0
  338. package/dist/mailroom/file-store.js +568 -0
  339. package/dist/mailroom/hosted-cache-sync.js +384 -0
  340. package/dist/mailroom/mbox-import.js +393 -0
  341. package/dist/mailroom/migration.js +164 -0
  342. package/dist/mailroom/outbound.js +380 -0
  343. package/dist/mailroom/policy.js +263 -0
  344. package/dist/mailroom/reader.js +292 -0
  345. package/dist/mailroom/search-cache.js +513 -0
  346. package/dist/mailroom/search-relevance.js +319 -0
  347. package/dist/mailroom/smtp-ingress.js +176 -0
  348. package/dist/mailroom/source-state.js +176 -0
  349. package/dist/mailroom/thread.js +109 -0
  350. package/dist/mailroom/travel-extract.js +89 -0
  351. package/dist/mind/bundle-manifest.js +24 -2
  352. package/dist/mind/context.js +309 -111
  353. package/dist/mind/desk-section.js +362 -0
  354. package/dist/mind/diary-integrity.js +60 -0
  355. package/dist/mind/{memory.js → diary.js} +68 -77
  356. package/dist/mind/embedding-provider.js +60 -0
  357. package/dist/mind/file-state.js +179 -0
  358. package/dist/mind/{associative-recall.js → note-search.js} +47 -58
  359. package/dist/mind/obligation-steering.js +199 -3
  360. package/dist/mind/pending.js +36 -10
  361. package/dist/mind/prompt-budget.js +479 -0
  362. package/dist/mind/prompt-refresh.js +3 -2
  363. package/dist/mind/prompt.js +1050 -189
  364. package/dist/mind/provenance-trust.js +26 -0
  365. package/dist/mind/record-paths.js +312 -0
  366. package/dist/mind/scrutiny.js +173 -0
  367. package/dist/mind/session-transaction.js +564 -0
  368. package/dist/nerves/cli-logging.js +7 -1
  369. package/dist/nerves/coverage/audit-rules.js +15 -6
  370. package/dist/nerves/coverage/audit.js +28 -2
  371. package/dist/nerves/coverage/cli.js +1 -1
  372. package/dist/nerves/coverage/contract.js +5 -5
  373. package/dist/nerves/coverage/file-completeness.js +139 -5
  374. package/dist/nerves/coverage/run-artifacts.js +55 -35
  375. package/dist/nerves/coverage/source-scanner.js +4 -4
  376. package/dist/nerves/event-buffer.js +111 -0
  377. package/dist/nerves/index.js +293 -9
  378. package/dist/nerves/observation.js +20 -0
  379. package/dist/nerves/redact.js +79 -0
  380. package/dist/nerves/review/cli-main.js +5 -0
  381. package/dist/nerves/review/cli.js +156 -0
  382. package/dist/nerves/review/core.js +159 -0
  383. package/dist/nerves/runtime.js +17 -1
  384. package/dist/repertoire/ado-client.js +15 -56
  385. package/dist/repertoire/ado-semantic.js +16 -10
  386. package/dist/repertoire/api-client.js +97 -0
  387. package/dist/repertoire/bitwarden-store.js +1045 -0
  388. package/dist/repertoire/bundle-templates.js +71 -0
  389. package/dist/repertoire/bw-installer.js +180 -0
  390. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  391. package/dist/repertoire/coding/context-pack.js +331 -0
  392. package/dist/repertoire/coding/feedback.js +174 -32
  393. package/dist/repertoire/coding/index.js +15 -3
  394. package/dist/repertoire/coding/manager.js +159 -10
  395. package/dist/repertoire/coding/spawner.js +58 -11
  396. package/dist/repertoire/coding/tools.js +221 -15
  397. package/dist/repertoire/coding/workbench-client.js +272 -0
  398. package/dist/repertoire/coding/workbench-manager.js +346 -0
  399. package/dist/repertoire/commerce-errors.js +109 -0
  400. package/dist/repertoire/commerce-self-test.js +156 -0
  401. package/dist/repertoire/credential-access.js +178 -0
  402. package/dist/repertoire/desk/classifier.js +362 -0
  403. package/dist/repertoire/duffel-client.js +185 -0
  404. package/dist/repertoire/github-client.js +14 -55
  405. package/dist/repertoire/graph-client.js +11 -52
  406. package/dist/repertoire/guardrails.js +811 -18
  407. package/dist/repertoire/mcp-client.js +44 -3
  408. package/dist/repertoire/mcp-manager.js +427 -139
  409. package/dist/repertoire/mcp-tools.js +124 -0
  410. package/dist/repertoire/plugin-mcp.js +175 -0
  411. package/dist/repertoire/plugins.js +253 -0
  412. package/dist/repertoire/relationship-authorization.js +322 -0
  413. package/dist/repertoire/shell-sessions.js +134 -0
  414. package/dist/repertoire/skills.js +48 -4
  415. package/dist/repertoire/stripe-client.js +131 -0
  416. package/dist/repertoire/tool-arguments.js +170 -0
  417. package/dist/repertoire/tool-results.js +29 -0
  418. package/dist/repertoire/tools-a2a.js +699 -0
  419. package/dist/repertoire/tools-attachments.js +322 -0
  420. package/dist/repertoire/tools-awaiting.js +575 -0
  421. package/dist/repertoire/tools-base.js +77 -1138
  422. package/dist/repertoire/tools-bluebubbles.js +2 -0
  423. package/dist/repertoire/tools-bridge.js +144 -0
  424. package/dist/repertoire/tools-bundle.js +993 -0
  425. package/dist/repertoire/tools-commerce.js +253 -0
  426. package/dist/repertoire/tools-config.js +186 -0
  427. package/dist/repertoire/tools-continuity.js +575 -0
  428. package/dist/repertoire/tools-credential.js +383 -0
  429. package/dist/repertoire/tools-evolution.js +527 -0
  430. package/dist/repertoire/tools-files.js +344 -0
  431. package/dist/repertoire/tools-flight.js +290 -0
  432. package/dist/repertoire/tools-flow.js +119 -0
  433. package/dist/repertoire/tools-github.js +3 -8
  434. package/dist/repertoire/tools-habits.js +103 -0
  435. package/dist/repertoire/tools-mail.js +1828 -0
  436. package/dist/repertoire/tools-notes.js +485 -0
  437. package/dist/repertoire/tools-obligations.js +182 -0
  438. package/dist/repertoire/tools-orientation.js +31 -0
  439. package/dist/repertoire/tools-record.js +482 -0
  440. package/dist/repertoire/tools-rsvp.js +139 -0
  441. package/dist/repertoire/tools-runtime.js +150 -0
  442. package/dist/repertoire/tools-sanctuary-host.js +202 -0
  443. package/dist/repertoire/tools-session.js +1133 -0
  444. package/dist/repertoire/tools-shell.js +149 -0
  445. package/dist/repertoire/tools-steward-policy.js +119 -0
  446. package/dist/repertoire/tools-stripe.js +224 -0
  447. package/dist/repertoire/tools-surface.js +369 -0
  448. package/dist/repertoire/tools-teams.js +12 -39
  449. package/dist/repertoire/tools-telegram-contacts.js +37 -0
  450. package/dist/repertoire/tools-travel.js +125 -0
  451. package/dist/repertoire/tools-trip.js +982 -0
  452. package/dist/repertoire/tools-unraid.js +531 -0
  453. package/dist/repertoire/tools-user-profile.js +146 -0
  454. package/dist/repertoire/tools-vault.js +40 -0
  455. package/dist/repertoire/tools-voice.js +145 -0
  456. package/dist/repertoire/tools.js +494 -148
  457. package/dist/repertoire/travel-api-client.js +360 -0
  458. package/dist/repertoire/unraid-client.js +155 -0
  459. package/dist/repertoire/unraid-restart.js +339 -0
  460. package/dist/repertoire/user-profile.js +131 -0
  461. package/dist/repertoire/vault-setup.js +246 -0
  462. package/dist/repertoire/vault-unlock.js +628 -0
  463. package/dist/rsvp/aisleplanner-client.js +354 -0
  464. package/dist/rsvp/cli.js +926 -0
  465. package/dist/rsvp/config.js +327 -0
  466. package/dist/rsvp/cutover.js +433 -0
  467. package/dist/rsvp/diagnostics.js +232 -0
  468. package/dist/rsvp/diff-renderer.js +176 -0
  469. package/dist/rsvp/habit-policy.js +110 -0
  470. package/dist/rsvp/habit-stage.js +123 -0
  471. package/dist/rsvp/incident-bundle.js +97 -0
  472. package/dist/rsvp/migration.js +204 -0
  473. package/dist/rsvp/native-habit-runner.js +630 -0
  474. package/dist/rsvp/outbound-state.js +628 -0
  475. package/dist/rsvp/query.js +118 -0
  476. package/dist/rsvp/replay.js +233 -0
  477. package/dist/rsvp/snapshot.js +232 -0
  478. package/dist/rsvp/spend-ledger.js +175 -0
  479. package/dist/scripts/claude-code-hook.js +41 -0
  480. package/dist/scripts/claude-code-stop-hook.js +47 -0
  481. package/dist/senses/a2a-entry.js +115 -0
  482. package/dist/senses/acp-server.js +386 -0
  483. package/dist/senses/attention-queue.js +186 -0
  484. package/dist/senses/await-turn-message.js +58 -0
  485. package/dist/senses/bluebubbles/active-turns.js +216 -0
  486. package/dist/senses/bluebubbles/attachment-cache.js +53 -0
  487. package/dist/senses/bluebubbles/attachment-download.js +137 -0
  488. package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +355 -40
  489. package/dist/senses/bluebubbles/context-packet.js +207 -0
  490. package/dist/senses/bluebubbles/context-smoke.js +144 -0
  491. package/dist/senses/bluebubbles/entry.js +90 -0
  492. package/dist/senses/{bluebubbles-inbound-log.js → bluebubbles/inbound-log.js} +21 -3
  493. package/dist/senses/bluebubbles/index.js +3841 -0
  494. package/dist/senses/bluebubbles/latest-turn.js +311 -0
  495. package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -71
  496. package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +159 -28
  497. package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +24 -13
  498. package/dist/senses/bluebubbles/outbound-state.js +308 -0
  499. package/dist/senses/bluebubbles/processed-log.js +133 -0
  500. package/dist/senses/bluebubbles/reaction-policy.js +47 -0
  501. package/dist/senses/bluebubbles/replay.js +284 -0
  502. package/dist/senses/{bluebubbles-runtime-state.js → bluebubbles/runtime-state.js} +30 -2
  503. package/dist/senses/bluebubbles/semantic-receipts.js +1986 -0
  504. package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
  505. package/dist/senses/bluebubbles/webhook-registration.js +228 -0
  506. package/dist/senses/bluebubbles-meta-guard.js +39 -0
  507. package/dist/senses/cli/bracketed-paste.js +82 -0
  508. package/dist/senses/cli/image-paste.js +287 -0
  509. package/dist/senses/cli/image-ref-navigation.js +75 -0
  510. package/dist/senses/cli/ink-app.js +156 -0
  511. package/dist/senses/cli/inline-diff.js +64 -0
  512. package/dist/senses/cli/input-keys.js +174 -0
  513. package/dist/senses/cli/kill-ring.js +86 -0
  514. package/dist/senses/cli/message-list.js +51 -0
  515. package/dist/senses/cli/ouro-tui.js +607 -0
  516. package/dist/senses/cli/spinner-imperative.js +135 -0
  517. package/dist/senses/cli/spinner.js +101 -0
  518. package/dist/senses/cli/status-line.js +60 -0
  519. package/dist/senses/cli/streaming-markdown.js +526 -0
  520. package/dist/senses/cli/tool-display.js +85 -0
  521. package/dist/senses/cli/tool-render.js +85 -0
  522. package/dist/senses/cli/tui-store.js +241 -0
  523. package/dist/senses/cli/virtual-list.js +35 -0
  524. package/dist/senses/cli-entry.js +60 -8
  525. package/dist/senses/cli.js +604 -320
  526. package/dist/senses/commands.js +66 -3
  527. package/dist/senses/context-packet-ledger.js +371 -0
  528. package/dist/senses/context-packets.js +154 -0
  529. package/dist/senses/habit-lifecycle-guard.js +24 -0
  530. package/dist/senses/habit-turn-message.js +160 -0
  531. package/dist/senses/ingress-evidence.js +2 -0
  532. package/dist/senses/mail-entry.js +66 -0
  533. package/dist/senses/mail.js +524 -0
  534. package/dist/senses/pipeline.js +1115 -204
  535. package/dist/senses/private-runtime-worker.js +859 -0
  536. package/dist/senses/private-runtime.js +1517 -0
  537. package/dist/senses/proactive-content-guard.js +51 -0
  538. package/dist/senses/root-host-approval-port.js +345 -0
  539. package/dist/senses/root-host-approval-runtime.js +393 -0
  540. package/dist/senses/sanctuary-authority-resident.js +102 -0
  541. package/dist/senses/sanctuary-download-credit-presentation.js +31 -0
  542. package/dist/senses/sanctuary-full-visibility-contract.js +302 -0
  543. package/dist/senses/sanctuary-grounding.js +118 -0
  544. package/dist/senses/sanctuary-health-acceptance-probe-entry.js +12 -0
  545. package/dist/senses/sanctuary-health-acceptance-probe.js +774 -0
  546. package/dist/senses/sanctuary-health-runner.js +100 -0
  547. package/dist/senses/sanctuary-health.js +408 -0
  548. package/dist/senses/sanctuary-install-state-contract.js +126 -0
  549. package/dist/senses/sanctuary-interactive-control.js +410 -0
  550. package/dist/senses/sanctuary-media-catalog-contract.js +297 -0
  551. package/dist/senses/sanctuary-media-optimization.js +514 -0
  552. package/dist/senses/sanctuary-runtime.js +340 -0
  553. package/dist/senses/sanctuary-sab.js +85 -0
  554. package/dist/senses/sanctuary-storage-optimization-contract.js +32 -0
  555. package/dist/senses/shared-turn.js +772 -0
  556. package/dist/senses/surface-tool.js +109 -0
  557. package/dist/senses/teams-entry.js +60 -8
  558. package/dist/senses/teams.js +519 -218
  559. package/dist/senses/telegram-admission.js +718 -0
  560. package/dist/senses/telegram-approval-runtime.js +793 -0
  561. package/dist/senses/telegram-attachments.js +186 -0
  562. package/dist/senses/telegram-audit-ledger.js +220 -0
  563. package/dist/senses/telegram-authority-transport.js +231 -0
  564. package/dist/senses/telegram-client.js +1749 -0
  565. package/dist/senses/telegram-effect-adapter.js +896 -0
  566. package/dist/senses/telegram-entry.js +83 -0
  567. package/dist/senses/telegram.js +2324 -0
  568. package/dist/senses/trust-gate.js +104 -9
  569. package/dist/senses/voice/audio-playback.js +237 -0
  570. package/dist/senses/voice/audio-routing.js +119 -0
  571. package/dist/senses/voice/elevenlabs.js +202 -0
  572. package/dist/senses/voice/floor-control.js +431 -0
  573. package/dist/senses/voice/floor-controller.js +115 -0
  574. package/dist/senses/voice/golden-path.js +116 -0
  575. package/dist/senses/voice/index.js +29 -0
  576. package/dist/senses/voice/meeting.js +113 -0
  577. package/dist/senses/voice/outbound.js +190 -0
  578. package/dist/senses/voice/phone.js +33 -0
  579. package/dist/senses/voice/playback.js +139 -0
  580. package/dist/senses/voice/realtime-eval.js +496 -0
  581. package/dist/senses/voice/realtime-trace.js +531 -0
  582. package/dist/senses/voice/transcript.js +70 -0
  583. package/dist/senses/voice/turn.js +191 -0
  584. package/dist/senses/voice/twilio-phone-runtime.js +807 -0
  585. package/dist/senses/voice/twilio-phone.js +5031 -0
  586. package/dist/senses/voice/types.js +2 -0
  587. package/dist/senses/voice/whisper.js +161 -0
  588. package/dist/senses/voice-entry.js +81 -0
  589. package/dist/senses/voice-realtime-eval-command.js +99 -0
  590. package/dist/senses/voice-realtime-eval-entry.js +21 -0
  591. package/dist/senses/voice-twilio-entry.js +87 -0
  592. package/dist/trips/core.js +138 -0
  593. package/dist/trips/store.js +265 -0
  594. package/dist/util/frontmatter.js +69 -0
  595. package/npm-shrinkwrap.json +8138 -0
  596. package/package.json +72 -13
  597. package/skills/agent-commerce.md +113 -0
  598. package/skills/browser-navigation.md +117 -0
  599. package/skills/commerce-setup-guide.md +116 -0
  600. package/skills/commerce-setup.md +84 -0
  601. package/skills/configure-dev-tools.md +99 -0
  602. package/skills/travel-planning.md +138 -0
  603. package/dist/heart/daemon/auth-flow.js +0 -430
  604. package/dist/heart/daemon/ouro-path-installer.js +0 -260
  605. package/dist/heart/daemon/ouro-version-manager.js +0 -171
  606. package/dist/heart/daemon/update-hooks.js +0 -138
  607. package/dist/heart/obligations.js +0 -191
  608. package/dist/heart/safe-workspace.js +0 -228
  609. package/dist/heart/session-recall.js +0 -116
  610. package/dist/mind/friends/channel.js +0 -84
  611. package/dist/mind/friends/group-context.js +0 -144
  612. package/dist/mind/friends/resolver.js +0 -84
  613. package/dist/mind/friends/store-file.js +0 -190
  614. package/dist/mind/friends/store.js +0 -4
  615. package/dist/mind/friends/tokens.js +0 -26
  616. package/dist/mind/friends/trust-explanation.js +0 -74
  617. package/dist/mind/friends/types.js +0 -29
  618. package/dist/repertoire/tasks/board.js +0 -134
  619. package/dist/repertoire/tasks/index.js +0 -224
  620. package/dist/repertoire/tasks/lifecycle.js +0 -80
  621. package/dist/repertoire/tasks/middleware.js +0 -65
  622. package/dist/repertoire/tasks/parser.js +0 -173
  623. package/dist/repertoire/tasks/scanner.js +0 -132
  624. package/dist/repertoire/tasks/transitions.js +0 -144
  625. package/dist/senses/bluebubbles-entry.js +0 -13
  626. package/dist/senses/bluebubbles.js +0 -1181
  627. package/dist/senses/debug-activity.js +0 -154
  628. package/dist/senses/inner-dialog-worker.js +0 -90
  629. package/dist/senses/inner-dialog.js +0 -525
  630. package/subagents/README.md +0 -7
  631. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  632. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  633. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  634. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  635. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  636. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  637. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  638. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  639. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  640. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  641. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  642. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  643. /package/dist/{repertoire/tasks/types.js → heart/attachments/sources/adapter.js} +0 -0
  644. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  645. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  646. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
  647. /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
@@ -0,0 +1,955 @@
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-acceptance-delivery-probe",
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_AUDIT_PATH = "/home/ouro/AgentBundles/sanctuary.ouro/state/acceptance/telegram-audit-chain.ndjson";
125
+ function exactContainmentAudit(evidence) {
126
+ const digestFields = [evidence.keyInventoryDigest, evidence.auditLedgerDigest, evidence.writeApprovalPolicyDigest];
127
+ return evidence.schemaVersion === "sanctuary-containment-audit-v2"
128
+ && evidence.keyCount === 2
129
+ && evidence.readScopeDigest === hash(CONTAINMENT_READ_SCOPE)
130
+ && evidence.writeScopeDigest === hash([...CONTAINMENT_READ_SCOPE, "DOCKER:UPDATE_ANY"].sort())
131
+ && evidence.keyRoleAssignmentCount === 0
132
+ && (0, sanctuary_acceptance_harness_1.exactSanctuaryContainmentProfileBoundaries)(evidence.profileBoundaries)
133
+ && evidence.auditPathDigest === (0, node_crypto_1.createHash)("sha256").update(CONTAINMENT_AUDIT_PATH).digest("hex")
134
+ && evidence.auditRecordCount >= 2 && evidence.auditLifecyclePairCount >= 1
135
+ && evidence.containerUser === "10001:10001" && evidence.liveProcessUser === "10001:10001" && evidence.mountCount === 4 && evidence.publishedPortCount === 0
136
+ && evidence.networkMode === "host" && evidence.readOnlyRoot === false && evidence.mountsExact && evidence.securityExact && evidence.updaterDisabled
137
+ && !evidence.writableKeyExposure && evidence.rawWriteMaterialFieldCount === 0 && evidence.typedWriteExecutorCount === 1
138
+ && evidence.writeApprovalPolicyExact && !evidence.sensitiveMaterialObserved
139
+ && digestFields.every((value) => SHA256.test(value));
140
+ }
141
+ function atomicPrivateJson(filePath, value) {
142
+ fs.mkdirSync(path.dirname(filePath), { recursive: true, mode: 0o700 });
143
+ const temporary = `${filePath}.${process.pid}.tmp`;
144
+ fs.writeFileSync(temporary, `${JSON.stringify(value)}\n`, { encoding: "utf8", mode: 0o600, flag: "wx" });
145
+ fs.renameSync(temporary, filePath);
146
+ fs.chmodSync(filePath, 0o600);
147
+ }
148
+ function eventCount(facts, event) {
149
+ return facts.events.filter((entry) => entry.event === event).length;
150
+ }
151
+ function delta(after, before, event) {
152
+ return Math.max(0, eventCount(after, event) - eventCount(before, event));
153
+ }
154
+ function turnHasGroundedRead(after, turn, toolName) {
155
+ 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] : []));
156
+ return turn.toolResultDigests.some((digest) => resultDigests.has(digest));
157
+ }
158
+ function exactGroundedResponse(after, turn, toolName) {
159
+ const grounding = turn.toolGroundings?.length === 1 ? turn.toolGroundings[0] : undefined;
160
+ const live = after.liveGrounding;
161
+ const audit = after.events.filter((entry) => entry.event === "senses.sanctuary_read_receipt" && entry.meta.toolName === toolName && entry.meta.success === true
162
+ && entry.meta.resultDigest === grounding?.resultDigest && entry.meta.groundingDigest === grounding?.groundingDigest
163
+ && entry.meta.sourceIdentityDigest === grounding?.sourceIdentityDigest && entry.meta.observedAt === grounding?.observedAt);
164
+ const turnObservedAt = Date.parse(grounding?.observedAt ?? "");
165
+ const liveObservedAt = Date.parse(live?.observedAt ?? "");
166
+ const timeBound = Number.isFinite(turnObservedAt) && Number.isFinite(liveObservedAt)
167
+ && turnObservedAt <= turn.completedAt && turn.completedAt - turnObservedAt <= 300_000
168
+ && liveObservedAt >= turnObservedAt && liveObservedAt - turnObservedAt <= 300_000;
169
+ const liveFactsBound = toolName === "unraid_get_storage"
170
+ ? live?.groundingDigest === (0, sanctuary_grounding_1.sanctuaryGroundingDigest)(live?.facts ?? {})
171
+ : grounding?.groundingDigest === live?.groundingDigest && JSON.stringify(grounding?.facts) === JSON.stringify(live?.facts);
172
+ return Boolean(grounding && live && grounding.toolName === toolName && live.toolName === toolName
173
+ && grounding.groundingDigest === (0, sanctuary_grounding_1.sanctuaryGroundingDigest)(grounding.facts) && live.groundingDigest === (0, sanctuary_grounding_1.sanctuaryGroundingDigest)(live.facts)
174
+ && grounding.sourceIdentityDigest === live.sourceIdentityDigest && SHA256.test(grounding.sourceIdentityDigest) && timeBound && liveFactsBound
175
+ && audit.length === 1 && typeof turn.responseText === "string" && turn.responseText.length > 0
176
+ && turn.responseUtf16Units === turn.responseText.length && turn.responseUtf16Units <= 1_200
177
+ && (0, sanctuary_grounding_1.sanctuaryGroundedResponseAccurate)(toolName, grounding.facts, turn.responseText));
178
+ }
179
+ function recordsAdded(before, after, key) {
180
+ const remaining = new Map();
181
+ for (const value of before)
182
+ remaining.set(key(value), (remaining.get(key(value)) ?? 0) + 1);
183
+ return after.filter((value) => {
184
+ const digest = key(value);
185
+ const count = remaining.get(digest) ?? 0;
186
+ if (count === 0)
187
+ return true;
188
+ remaining.set(digest, count - 1);
189
+ return false;
190
+ });
191
+ }
192
+ function exactFreshTelegramLifecycle(before, after, expectedUpdateDigest) {
193
+ if (before.events.length > after.events.length || before.events.some((entry, index) => hash(entry) !== hash(after.events[index])))
194
+ return false;
195
+ const fresh = recordsAdded(before.events, after.events, (entry) => hash(entry));
196
+ const starts = fresh.filter((entry) => entry.event === "senses.telegram_turn_start");
197
+ const terminals = fresh.filter((entry) => entry.event === "senses.telegram_turn_end" || entry.event === "senses.telegram_turn_error");
198
+ if (starts.length !== 1 || terminals.length !== 1)
199
+ return false;
200
+ const start = starts[0];
201
+ const terminal = terminals[0];
202
+ const coordinateKeys = ["scenarioHandleDigest", "turnDigest", "updateDigest", "subject", "identityDigest", "sessionDigest", "argumentDigest"];
203
+ if (coordinateKeys.some((key) => start.meta[key] !== terminal.meta[key]))
204
+ return false;
205
+ if (!SHA256.test(String(start.meta.scenarioHandleDigest)) || !SHA256.test(String(start.meta.turnDigest))
206
+ || !SHA256.test(String(start.meta.updateDigest)) || (expectedUpdateDigest !== undefined && start.meta.updateDigest !== expectedUpdateDigest)
207
+ || typeof start.meta.subject !== "string" || !/^tg_[A-Za-z0-9_-]{43}$/u.test(start.meta.subject)
208
+ || ![start.meta.identityDigest, start.meta.sessionDigest, start.meta.argumentDigest, start.meta.lifecycleMac, terminal.meta.lifecycleMac].every((value) => typeof value === "string" && SHA256.test(value)))
209
+ return false;
210
+ if (!Number.isSafeInteger(terminal.meta.deliveryCount) || Number(terminal.meta.deliveryCount) < 0)
211
+ return false;
212
+ if (!Number.isSafeInteger(start.meta.lifecycleAt) || !Number.isSafeInteger(terminal.meta.lifecycleAt)
213
+ || Number(start.meta.lifecycleAt) < 0 || Number(start.meta.lifecycleAt) >= Number(terminal.meta.lifecycleAt))
214
+ return false;
215
+ return terminal.event === "senses.telegram_turn_end" && terminal.meta.outcome === "success" && terminal.meta.errorDigest === null;
216
+ }
217
+ function intendedApproval(before, after) {
218
+ const previous = new Set(before.approvals.map((record) => record.approvalId));
219
+ const candidates = after.approvals.filter((record) => !previous.has(record.approvalId) && record.createdAt >= before.capturedAt);
220
+ return candidates.length === 1 ? candidates[0] : null;
221
+ }
222
+ function intendedRestartApproval(approval) {
223
+ const argumentDigest = (0, node_crypto_1.createHash)("sha256").update(JSON.stringify({ container: CANONICAL_RESTART_TARGET })).digest("hex");
224
+ return approval?.toolName === "unraid_restart_container"
225
+ && approval.target === CANONICAL_RESTART_TARGET
226
+ && approval.argumentDigest === argumentDigest;
227
+ }
228
+ function approvalEvidenceCoordinates(approval) {
229
+ return {
230
+ actionDigest: (0, node_crypto_1.createHash)("sha256").update(JSON.stringify({ toolName: approval.toolName, argumentDigest: approval.argumentDigest })).digest("hex"),
231
+ targetDigest: (0, node_crypto_1.createHash)("sha256").update(JSON.stringify({ container: CANONICAL_RESTART_TARGET })).digest("hex"),
232
+ };
233
+ }
234
+ function exactApprovalEvidence(before, after, approval) {
235
+ const fresh = recordsAdded(before.events, after.events, (entry) => hash(entry));
236
+ const prompt = fresh.filter((entry) => entry.event === "senses.telegram_approval_prompt_bound" && entry.meta.approvalId === approval.approvalId);
237
+ const terminals = fresh.filter((entry) => entry.event === "telegram.approval_prompt_terminalized" && entry.meta.approvalId === approval.approvalId);
238
+ const callbackCandidates = fresh.filter((entry) => ["telegram.callback_settled", "telegram.callback_recovery_settled"].includes(entry.event) && entry.meta.approvalId === approval.approvalId);
239
+ const callbacks = [...new Map(callbackCandidates.map((entry) => [hash({ event: entry.event, meta: entry.meta }), entry])).values()];
240
+ const continuations = fresh.filter((entry) => entry.event === "senses.telegram_approval_continuation_delivered" && entry.meta.approvalId === approval.approvalId);
241
+ const staleCallbacks = fresh.filter((entry) => entry.event === "telegram.approval_stale_callback_settled" && entry.meta.approvalId === approval.approvalId);
242
+ const expiryObservations = fresh.filter((entry) => entry.event === "telegram.approval_expiry_observed" && entry.meta.approvalId === approval.approvalId);
243
+ if (prompt.length !== 1 || terminals.length !== 1 || callbacks.length > 1 || continuations.length > 1 || staleCallbacks.length > 1)
244
+ return null;
245
+ if ((approval.state === "expired" && (expiryObservations.length < 1
246
+ || new Set(expiryObservations.map((entry) => hash({ event: entry.event, meta: entry.meta }))).size !== 1))
247
+ || (approval.state !== "expired" && expiryObservations.length !== 0))
248
+ return null;
249
+ const expected = approvalEvidenceCoordinates(approval);
250
+ const coordinatesMatch = (entry) => entry.meta.scenarioHandleDigest !== undefined
251
+ && typeof entry.meta.scenarioHandleDigest === "string" && SHA256.test(entry.meta.scenarioHandleDigest)
252
+ && entry.meta.actionDigest === expected.actionDigest && entry.meta.targetDigest === expected.targetDigest
253
+ && entry.meta.checkpointDigest === approval.checkpointDigest
254
+ && typeof approval.suspendedSessionRevision === "string"
255
+ && entry.meta.suspendedSessionRevisionDigest === (0, node_crypto_1.createHash)("sha256").update(approval.suspendedSessionRevision, "utf8").digest("hex")
256
+ && typeof entry.meta.messageIdDigest === "string" && SHA256.test(entry.meta.messageIdDigest)
257
+ && typeof entry.meta.evidenceMac === "string" && SHA256.test(entry.meta.evidenceMac);
258
+ if (![...prompt, ...terminals, ...callbacks, ...continuations, ...staleCallbacks, ...expiryObservations].every(coordinatesMatch))
259
+ return null;
260
+ const promptScenarioHandleDigest = prompt[0].meta.scenarioHandleDigest;
261
+ const promptMessageIdDigest = prompt[0].meta.messageIdDigest;
262
+ if (![...terminals, ...callbacks, ...continuations, ...staleCallbacks, ...expiryObservations].every((entry) => entry.meta.scenarioHandleDigest === promptScenarioHandleDigest
263
+ && entry.meta.messageIdDigest === promptMessageIdDigest))
264
+ return null;
265
+ const boundAt = Number(prompt[0].meta.boundAt);
266
+ const terminalEditStartedAt = Number(terminals[0].meta.terminalEditStartedAt);
267
+ const terminalizedAt = Number(terminals[0].meta.terminalizedAt);
268
+ const expiryObservation = expiryObservations[0] ?? null;
269
+ const expiryObservedAt = expiryObservation === null || expiryObservation.meta.expiryObservedAt === null ? null : Number(expiryObservation.meta.expiryObservedAt);
270
+ const expiryDeadlineAt = expiryObservation === null ? null : Number(expiryObservation.meta.expiryDeadlineAt);
271
+ if (!Number.isSafeInteger(boundAt) || boundAt < 0
272
+ || !Number.isSafeInteger(terminalEditStartedAt) || terminalEditStartedAt < boundAt
273
+ || !Number.isSafeInteger(terminalizedAt) || terminalizedAt < terminalEditStartedAt
274
+ || terminalizedAt - terminalEditStartedAt > exports.SANCTUARY_APPROVAL_TERMINAL_EDIT_TIMEOUT_MS
275
+ || terminals[0].meta.boundAt !== boundAt || terminals[0].meta.buttonsRemoved !== true
276
+ || approval.expiresAt !== boundAt + exports.SANCTUARY_APPROVAL_TTL_MS)
277
+ return null;
278
+ if (approval.state === "expired") {
279
+ if (expiryObservedAt === null || !Number.isSafeInteger(expiryObservedAt) || expiryObservedAt < boundAt
280
+ || expiryObservation?.meta.expiryObservationSchemaVersion !== "telegram-approval-expiry-observation-v1"
281
+ || expiryObservation.meta.boundAt !== boundAt
282
+ || terminals[0].meta.expiryObservedAt !== expiryObservedAt || terminals[0].meta.expiryDeadlineAt !== expiryDeadlineAt
283
+ || terminalEditStartedAt < expiryObservedAt || terminalizedAt - expiryObservedAt > exports.SANCTUARY_APPROVAL_TERMINAL_EDIT_TIMEOUT_MS)
284
+ return null;
285
+ }
286
+ else if (expiryObservedAt !== null || terminals[0].meta.expiryObservedAt !== undefined || terminals[0].meta.expiryDeadlineAt !== undefined)
287
+ return null;
288
+ const callback = callbacks[0] ?? null;
289
+ if (callback) {
290
+ const callbackAt = Number(callback.meta.callbackAt);
291
+ if (!Number.isSafeInteger(callbackAt) || callbackAt < boundAt || callbackAt > terminalEditStartedAt
292
+ || callback.meta.boundAt !== boundAt
293
+ || (callback.event === "telegram.callback_settled" && (callback.meta.acknowledged !== true || callback.meta.acknowledgementState !== "acknowledged"))
294
+ || (callback.event === "telegram.callback_recovery_settled" && (callback.meta.acknowledgementState !== "indeterminate_after_restart"
295
+ || !Number.isSafeInteger(Number(callback.meta.recoveredAt)) || Number(callback.meta.recoveredAt) < terminalizedAt
296
+ || !Number.isSafeInteger(callback.at) || callback.at < Number(callback.meta.recoveredAt)
297
+ || typeof callback.meta.decisionAttemptDigest !== "string" || !SHA256.test(callback.meta.decisionAttemptDigest))))
298
+ return null;
299
+ }
300
+ const continuation = continuations[0] ?? null;
301
+ if (continuation) {
302
+ const deliveredAt = Number(continuation.meta.deliveredAt);
303
+ const callbackAt = callback === null ? null : Number(callback.meta.callbackAt);
304
+ if (!Number.isSafeInteger(deliveredAt) || deliveredAt < boundAt || deliveredAt > terminalEditStartedAt
305
+ || callbackAt !== null && deliveredAt < callbackAt
306
+ || continuation.meta.boundAt !== boundAt || typeof continuation.meta.resultDigest !== "string" || !SHA256.test(continuation.meta.resultDigest)
307
+ || continuation.meta.resultDigest !== approval.resultDigest
308
+ || typeof continuation.meta.deliveryDigest !== "string" || !SHA256.test(continuation.meta.deliveryDigest)
309
+ || typeof continuation.meta.deliveryMessageIdDigest !== "string" || !SHA256.test(continuation.meta.deliveryMessageIdDigest))
310
+ return null;
311
+ }
312
+ const staleCallback = staleCallbacks[0] ?? null;
313
+ if (staleCallback) {
314
+ const staleAt = Number(staleCallback.meta.staleAt);
315
+ if (!Number.isSafeInteger(staleAt) || staleAt < terminalizedAt || staleCallback.meta.acknowledged !== true
316
+ || staleCallback.meta.accepted !== false || staleCallback.meta.reason !== "stale_callback")
317
+ return null;
318
+ }
319
+ return { boundAt, callback, continuation, expiryObservedAt, expiryDeadlineAt, terminalizedAt, staleCallback };
320
+ }
321
+ function terminalizedWithinTtlJitter(evidence) {
322
+ const expiryObservedAt = evidence?.expiryObservedAt ?? null;
323
+ const expiryDeadlineAt = evidence?.expiryDeadlineAt ?? null;
324
+ if ([expiryObservedAt, expiryDeadlineAt].some((value) => value === null))
325
+ return false;
326
+ return expiryDeadlineAt === evidence.boundAt + exports.SANCTUARY_APPROVAL_TTL_MS
327
+ && expiryObservedAt >= expiryDeadlineAt
328
+ && expiryObservedAt <= expiryDeadlineAt + exports.SANCTUARY_APPROVAL_RECONCILIATION_JITTER_MS;
329
+ }
330
+ function interactiveReceiptBindsApproval(receipt, approval) {
331
+ const digestText = (value) => (0, node_crypto_1.createHash)("sha256").update(value, "utf8").digest("hex");
332
+ return receipt.approvalIdDigest === digestText(approval.approvalId)
333
+ && receipt.checkpointDigest === approval.checkpointDigest
334
+ && receipt.approvalEpochBefore === approval.approvalEpoch
335
+ && typeof approval.suspendedSessionRevision === "string"
336
+ && receipt.suspendedSessionRevisionDigest === digestText(approval.suspendedSessionRevision);
337
+ }
338
+ function healthProbeRestored(probe, currentCron) {
339
+ return probe.ownerImageDigestBefore === probe.ownerImageDigestAfter
340
+ && probe.ownerContainerDigestBefore === probe.ownerContainerDigestAfter
341
+ && (probe.schedulerReceipt !== null || probe.beforeStateDigest === probe.restoredStateDigest)
342
+ && probe.cronFingerprintBefore === probe.cronFingerprintAfter
343
+ && probe.cronRegisteredBefore && probe.cronRegisteredAfter && !probe.cronDegradedBefore && !probe.cronDegradedAfter
344
+ && currentCron?.fingerprint === probe.cronFingerprintAfter && currentCron.registered === probe.cronRegisteredAfter
345
+ && probe.workspaceAbsent && probe.socketAbsent && probe.snapshotAbsent && probe.realCheckEquivalent && probe.productionRestored;
346
+ }
347
+ function deriveSanctuaryScenarioAssertions(label, before, after, _now, scenarioHandleDigest) {
348
+ const approval = intendedApproval(before, after);
349
+ const newTurns = recordsAdded(before.telegramTurns, after.telegramTurns, (turn) => hash(turn));
350
+ const newAttempts = recordsAdded(before.restartAttempts, after.restartAttempts, (attempt) => hash(attempt));
351
+ const newApprovals = recordsAdded(before.approvals, after.approvals, (record) => record.approvalId);
352
+ const linkedAttempts = approval ? after.restartAttempts
353
+ .filter((attempt) => approval.toolName === "unraid_restart_container" && attempt.approvalId === approval.approvalId && attempt.argumentDigest === approval.argumentDigest && attempt.target === approval.target)
354
+ .sort((left, right) => left.observedAt - right.observedAt) : [];
355
+ const attemptIsLinked = (attempt) => {
356
+ if (!approval || !intendedRestartApproval(approval) || attempt.approvalId !== approval.approvalId
357
+ || attempt.argumentDigest !== approval.argumentDigest || attempt.target !== CANONICAL_RESTART_TARGET
358
+ || typeof attempt.targetId !== "string" || attempt.targetId.length < 1 || attempt.targetId.length > 128
359
+ || approval.resultTargetId === null || attempt.targetId !== approval.resultTargetId)
360
+ return false;
361
+ const expectedActionDigest = (0, node_crypto_1.createHash)("sha256").update(JSON.stringify({ operation: "restart", container: { id: attempt.targetId, name: CANONICAL_RESTART_TARGET } })).digest("hex");
362
+ return attempt.actionDigest === expectedActionDigest;
363
+ };
364
+ const completeAttemptLedgerLinked = newAttempts.every(attemptIsLinked) && linkedAttempts.length === newAttempts.length
365
+ && new Set(linkedAttempts.map((attempt) => attempt.targetId)).size <= 1;
366
+ const mutationAttemptIds = new Set(linkedAttempts.filter((attempt) => attempt.state === "attempting").map((attempt) => attempt.attemptId));
367
+ const mutationCount = mutationAttemptIds.size;
368
+ const unsafeAttemptStates = new Set(["attempting", "succeeded", "attempted_or_indeterminate"]);
369
+ const scenarioMutationCount = new Set(after.restartAttempts.filter((attempt) => unsafeAttemptStates.has(attempt.state)).map((attempt) => attempt.attemptId)).size;
370
+ const restartSucceeded = linkedAttempts.some((attempt) => attempt.state === "succeeded" && mutationAttemptIds.has(attempt.attemptId)
371
+ && (attempt.mutationAcknowledged || attempt.afterState === "running"));
372
+ const firstIndeterminate = linkedAttempts.find((attempt) => attempt.state === "attempted_or_indeterminate");
373
+ const attemptedIndeterminateRetryCount = firstIndeterminate
374
+ ? new Set(linkedAttempts.filter((attempt) => attempt.state === "attempting" && attempt.observedAt > firstIndeterminate.observedAt && attempt.attemptId !== firstIndeterminate.attemptId).map((attempt) => attempt.attemptId)).size
375
+ : 0;
376
+ const deliveredTurns = newTurns.filter((turn) => turn.status === "success" && turn.deliveryCount > 0);
377
+ const telegramResponses = deliveredTurns.length;
378
+ const approvalTransitions = delta(after, before, "approval.acceptance_transition");
379
+ const postbootIntegrity = after.prebootIntegrity && after.postbootIntegrity
380
+ && typeof scenarioHandleDigest === "string"
381
+ ? verifySanctuaryPostbootIntegrity(after.prebootIntegrity, after.postbootIntegrity, scenarioHandleDigest) : null;
382
+ switch (label) {
383
+ case "unit-12c-1-opaque-identity":
384
+ case "unit-14b-3-opaque-identity-live":
385
+ if (!after.identity || (after.identity.inspectedRecordCount ?? 0) < 1 || (after.identity.opaqueSubjectCount ?? 0) < 1 || after.identity.mismatchCount !== 0 || after.identity.rawLeakCount !== 0
386
+ || (label.includes("live") && (telegramResponses !== 1 || newTurns.length !== 1 || !after.identity.liveSubjectObserved
387
+ || after.identity.canonicalSessionCount !== 1 || after.identity.canonicalFriendCount !== 1
388
+ || !exactFreshTelegramLifecycle(before, after, newTurns[0]?.updateDigest))))
389
+ return null;
390
+ return { identityBound: after.identity.keyPresent, opaqueSubject: after.identity.subjectOpaque, rawIdentityAbsent: after.identity.rawIdentityAbsent };
391
+ case "unit-15c-1-no-callback-terminalization": {
392
+ if (!intendedRestartApproval(approval) || approval.state !== "expired" || approval.createdAt < before.capturedAt)
393
+ return null;
394
+ const evidence = exactApprovalEvidence(before, after, approval);
395
+ if (!terminalizedWithinTtlJitter(evidence) || !evidence || evidence.callback !== null || evidence.continuation !== null)
396
+ return null;
397
+ const elapsedMs = evidence.terminalizedAt - evidence.boundAt;
398
+ if (!approval.buttonsRemoved || !approval.terminalPrompt)
399
+ return null;
400
+ const baseline = before.sourceValues["no-callback-baseline"];
401
+ if (!baseline || typeof baseline !== "object" || Array.isArray(baseline))
402
+ return null;
403
+ const baselineRecord = baseline;
404
+ const inboundEventCount = after.events.filter((entry) => entry.event === "telegram.callback_settled" || entry.event === "telegram.callback_recovery_settled" || entry.event === "telegram.update_dropped").length;
405
+ const noInboundUpdate = baselineRecord.approvalId === approval.approvalId
406
+ && baselineRecord.offsetDigest === hash(after.sourceValues["telegram-offset"])
407
+ && baselineRecord.inboundEventCount === inboundEventCount
408
+ && approval.callbackCount === 0;
409
+ if (!noInboundUpdate || scenarioMutationCount !== 0 || !completeAttemptLedgerLinked)
410
+ return null;
411
+ return { buttonsRemoved: approval.buttonsRemoved, elapsedMs, mutationCount: 0, noInboundUpdate, replayMutationCount: approval.replayMutationCount, terminalExpired: approval.state === "expired", ttlMs: exports.SANCTUARY_APPROVAL_TTL_MS };
412
+ }
413
+ case "unit-16a-pre-reboot-checkpoint":
414
+ 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)
415
+ return null;
416
+ 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 };
417
+ case "unit-16a-reboot-request":
418
+ if (!after.reboot || after.reboot.phase !== "requested" || !SHA256.test(after.reboot.processBindingDigest) || after.reboot.requestCount !== 1 || !after.reboot.checkpointPersisted)
419
+ return null;
420
+ return { exactlyOnce: true, processBindingDigest: after.reboot.processBindingDigest, requestCheckpointPersisted: true, requestDigest: after.reboot.requestDigest };
421
+ case "unit-16a-boot-recovery-milestones":
422
+ 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)
423
+ return null;
424
+ 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 };
425
+ case "unit-16b-runtime-vault-readiness":
426
+ if (!after.container)
427
+ return null;
428
+ if (!after.container.autostartExact || !after.container.exactImage || !after.container.mountsExact || after.container.manualAuthRequired
429
+ || !after.container.updaterDisabled || !after.container.vaultUnlocked)
430
+ return null;
431
+ return { autostartExact: after.container.autostartExact, exactImage: after.container.exactImage, manualAuthRequired: after.container.manualAuthRequired, updaterDisabled: after.container.updaterDisabled, vaultUnlocked: after.container.vaultUnlocked };
432
+ case "unit-16c-provider-readiness":
433
+ return after.provider && after.provider.outwardReady && after.provider.innerReady && !after.provider.silentFallback
434
+ && after.provider.credentialRevisionsPresent === true && after.provider.requestSemanticsExact === true && after.provider.fallbackAttemptCount === 0
435
+ && after.provider.laneSelectionExact === true && after.provider.vaultCoordinatesExact === true && after.provider.singleCredentialExact === true
436
+ ? { outwardReady: true, innerReady: true, silentFallback: false, laneSelectionExact: true, vaultCoordinatesExact: true, singleCredentialExact: true } : null;
437
+ case "unit-16d-whats-up":
438
+ 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"))
439
+ return null;
440
+ return { accurate: true, authorized: true, grounded: true, liveFactsMatched: true, responseCount: telegramResponses, responseWithinLimit: true, telegramDelivered: true };
441
+ case "unit-16d-1-space":
442
+ 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"))
443
+ return null;
444
+ return { accurate: true, authorized: true, grounded: true, liveFactsMatched: true, mutationCount: scenarioMutationCount, responseCount: telegramResponses, responseWithinLimit: true, telegramDelivered: true };
445
+ case "unit-16d-2-unknown-admission": {
446
+ const freshEvents = recordsAdded(before.events, after.events, (entry) => hash(entry));
447
+ const beforeAdmissions = before.telegramAdmissions ?? [];
448
+ const afterAdmissions = after.telegramAdmissions ?? [];
449
+ const newAdmissions = recordsAdded(beforeAdmissions, afterAdmissions, (entry) => entry.admissionDigest);
450
+ const admission = newAdmissions.length === 1 ? newAdmissions[0] : null;
451
+ const providerInvocationCount = newTurns.reduce((sum, turn) => sum + turn.providerTurnCount, 0);
452
+ const toolInvocationCount = newTurns.reduce((sum, turn) => sum + turn.toolInvocationCount, 0);
453
+ const durableToolRecordCount = delta(after, before, "senses.sanctuary_read_receipt");
454
+ const workItemCount = newApprovals.length;
455
+ const forbiddenWorkEvent = freshEvents.some((entry) => entry.event.startsWith("senses.telegram_turn_") || entry.event === "senses.sanctuary_read_receipt"
456
+ || entry.event.startsWith("senses.telegram_approved_restart_") || entry.event === "senses.sanctuary_health_delivered");
457
+ const offsetAdvanced = Number.isSafeInteger(before.telegramNextUpdateId) && Number.isSafeInteger(after.telegramNextUpdateId)
458
+ && Number(after.telegramNextUpdateId) === Number(before.telegramNextUpdateId) + 1;
459
+ const exactQuarantine = admission?.status === "pending" && admission.identityExact && admission.acknowledgementExact && admission.ownerCardExact
460
+ && admission.contentQuarantined && admission.relationshipAbsent && admission.capturedAt <= admission.updatedAt;
461
+ const zeroAgentWork = Boolean(before.zeroWork && after.zeroWork
462
+ && before.zeroWork.providerToolDigest === after.zeroWork.providerToolDigest
463
+ && before.zeroWork.approvalMutationDigest === after.zeroWork.approvalMutationDigest
464
+ && before.zeroWork.sessionFriendDigest === after.zeroWork.sessionFriendDigest);
465
+ if (!exactQuarantine || !offsetAdvanced || !zeroAgentWork || newTurns.length !== 0 || telegramResponses !== 0
466
+ || providerInvocationCount !== 0 || toolInvocationCount !== 0 || workItemCount !== 0 || approvalTransitions !== 0 || newAttempts.length !== 0
467
+ || scenarioMutationCount !== 0 || durableToolRecordCount !== 0 || forbiddenWorkEvent)
468
+ return null;
469
+ return {
470
+ acknowledgementSent: true,
471
+ agentTurnCount: 0,
472
+ distinctAccount: true,
473
+ mutationCount: 0,
474
+ ownerCardSent: true,
475
+ providerInvocationCount: 0,
476
+ quarantined: true,
477
+ responseCount: 0,
478
+ workItemCount: 0,
479
+ };
480
+ }
481
+ case "unit-16e-containment-audit":
482
+ if (!after.containment || !exactContainmentAudit(after.containment) || scenarioMutationCount !== 0)
483
+ return null;
484
+ return {
485
+ schemaVersion: after.containment.schemaVersion,
486
+ keyCount: after.containment.keyCount, keyInventoryDigest: after.containment.keyInventoryDigest,
487
+ readScopeDigest: after.containment.readScopeDigest, writeScopeDigest: after.containment.writeScopeDigest,
488
+ keyRoleAssignmentCount: after.containment.keyRoleAssignmentCount,
489
+ profileBoundaries: after.containment.profileBoundaries,
490
+ auditPathDigest: after.containment.auditPathDigest, auditLedgerDigest: after.containment.auditLedgerDigest,
491
+ auditRecordCount: after.containment.auditRecordCount, auditLifecyclePairCount: after.containment.auditLifecyclePairCount,
492
+ containerUser: after.containment.containerUser, liveProcessUser: after.containment.liveProcessUser, mountCount: after.containment.mountCount,
493
+ publishedPortCount: after.containment.publishedPortCount, networkMode: after.containment.networkMode,
494
+ readOnlyRoot: after.containment.readOnlyRoot, mountsExact: after.containment.mountsExact,
495
+ securityExact: after.containment.securityExact, updaterDisabled: after.containment.updaterDisabled,
496
+ writableKeyExposure: after.containment.writableKeyExposure,
497
+ rawWriteMaterialFieldCount: after.containment.rawWriteMaterialFieldCount,
498
+ typedWriteExecutorCount: after.containment.typedWriteExecutorCount,
499
+ writeApprovalPolicyDigest: after.containment.writeApprovalPolicyDigest,
500
+ writeApprovalPolicyExact: after.containment.writeApprovalPolicyExact,
501
+ sensitiveMaterialObserved: after.containment.sensitiveMaterialObserved,
502
+ mutationCount: scenarioMutationCount,
503
+ };
504
+ case "unit-16e-1-stop-denial":
505
+ case "unit-16e-2-restart-denial": {
506
+ const receipt = after.denial;
507
+ const expectedOperation = label === "unit-16e-1-stop-denial" ? "stop" : "restart";
508
+ if (!receipt || receipt.label !== label || receipt.operation !== expectedOperation || receipt.scenarioHandleDigest.length !== 64
509
+ || receipt.attemptCount !== 1 || ![200, 401, 403].includes(receipt.httpStatus)
510
+ || (receipt.errorCode !== "FORBIDDEN" && receipt.errorCode !== "PERMISSION_DENIED")
511
+ || receipt.targetDigest !== receipt.before.targetContainerIdDigest || receipt.targetDigest !== receipt.after.targetContainerIdDigest
512
+ || JSON.stringify(receipt.before) !== JSON.stringify(receipt.after) || scenarioMutationCount !== 0)
513
+ return null;
514
+ return { attemptCount: receipt.attemptCount, cursorBoundaryCount: 7, denied: true, mutationCount: 0, restartCountUnchanged: true, resumed: true };
515
+ }
516
+ case "unit-16f-cron-fingerprint":
517
+ if (!after.container?.running || !after.container.healthy || !after.healthProbe || !healthProbeRestored(after.healthProbe, after.cron) || after.healthProbe.clockMode !== "ambient" || after.healthProbe.phases.length !== 1
518
+ || !after.healthProbe.schedulerReceipt || after.healthProbe.schedulerReceipt.trigger !== "cron" || after.healthProbe.schedulerReceipt.sweepDelta !== 1
519
+ || after.healthProbe.schedulerReceipt.deliveryDelta !== 0 || after.healthProbe.schedulerReceipt.nonReplay !== true
520
+ || after.healthProbe.schedulerReceipt.sweep.recordDigest !== after.healthProbe.phases[0]?.sweepReceiptDigest
521
+ || after.healthProbe.phases[0]?.name !== "cron-unchanged" || after.healthProbe.phases[0].trigger !== "cron" || after.healthProbe.phases[0].fixtureStatus !== null
522
+ || after.healthProbe.phases[0].opened !== 0 || after.healthProbe.phases[0].recovered !== 0 || after.healthProbe.phases[0].digestDue
523
+ || after.healthProbe.phases[0].deliveryKind !== null || after.healthProbe.phases[0].deliveryReceiptDigest !== null
524
+ || after.healthProbe.privateTurnCount !== 0 || after.healthProbe.providerInvocationCount !== 0 || after.healthProbe.deliveryCount !== 0 || telegramResponses !== 0 || delta(after, before, "senses.telegram_turn_start") !== 0)
525
+ return null;
526
+ return { fingerprintUnchanged: true, messageCount: 0, providerInvocationCount: 0, receiptUnchanged: true, scheduleRegistered: true, sweepObserved: true };
527
+ case "unit-16g-health-transition": {
528
+ const probe = after.healthProbe;
529
+ const exactPhases = [
530
+ ["live-baseline", null, 0, 0, null], ["live-repeat", null, 0, 0, null], ["fixture-fail", 503, 1, 0, "transition"],
531
+ ["fixture-repeat", 503, 0, 0, null], ["fixture-recover", 200, 0, 1, "transition"], ["fixture-refail", 503, 1, 0, "transition"],
532
+ ];
533
+ if (!after.container?.running || !after.container.healthy || !probe || !healthProbeRestored(probe, after.cron) || probe.clockMode !== "ambient" || probe.privateTurnCount !== 3
534
+ || probe.providerInvocationCount < probe.privateTurnCount || probe.providerInvocationCount > 1_000 || probe.deliveryCount !== 3 || probe.phases.length !== exactPhases.length
535
+ || !probe.phases.every((phase, index) => phase.ordinal === index + 1 && phase.name === exactPhases[index][0] && phase.trigger === "acceptance"
536
+ && phase.fixtureStatus === exactPhases[index][1] && phase.opened === exactPhases[index][2] && phase.recovered === exactPhases[index][3]
537
+ && phase.deliveryKind === exactPhases[index][4] && phase.digestDue === false && (phase.deliveryKind === null) === (phase.deliveryReceiptDigest === null)))
538
+ return null;
539
+ return { alertCount: 3, productionRestored: true, transitionObserved: true };
540
+ }
541
+ case "unit-16h-acceptance-delivery-probe": {
542
+ const probe = after.healthProbe;
543
+ if (!after.container?.running || !after.container.healthy || !probe || !healthProbeRestored(probe, after.cron) || probe.acceptanceOnly !== true
544
+ || probe.productionScheduleChanged !== false || probe.clockMode !== "local-daily-boundary" || probe.privateTurnCount !== 1
545
+ || probe.providerInvocationCount < probe.privateTurnCount || probe.providerInvocationCount > 1_000 || probe.deliveryCount !== 1 || probe.phases.length !== 2
546
+ || probe.phases[0]?.ordinal !== 1 || probe.phases[0].name !== "delivery-probe-first" || probe.phases[0].trigger !== "acceptance" || probe.phases[0].fixtureStatus !== 503
547
+ || !probe.phases[0].digestDue || probe.phases[0].deliveryKind !== "digest" || probe.phases[0].deliveryReceiptDigest === null
548
+ || probe.phases[1]?.ordinal !== 2 || probe.phases[1].name !== "delivery-probe-repeat" || probe.phases[1].trigger !== "acceptance" || probe.phases[1].fixtureStatus !== 503
549
+ || probe.phases[1].digestDue || probe.phases[1].deliveryKind !== null || probe.phases[1].deliveryReceiptDigest !== null)
550
+ return null;
551
+ return { acceptanceOnly: true, deliveryPathObserved: true, firedWithinMs: 0, messageCount: 1, productionRestored: true, productionScheduleChanged: false };
552
+ }
553
+ case "unit-16i-delayed-approval":
554
+ if (!exactFreshTelegramLifecycle(before, after) || !intendedRestartApproval(approval) || !completeAttemptLedgerLinked || approval.state !== "succeeded" || mutationCount !== 1 || scenarioMutationCount !== 1 || !restartSucceeded || approval.replayMutationCount !== 0 || !approval.continuationCompleted)
555
+ return null;
556
+ if (!approval.terminalPrompt)
557
+ return null;
558
+ {
559
+ const evidence = exactApprovalEvidence(before, after, approval);
560
+ const callbackAt = Number(evidence?.callback?.meta.callbackAt);
561
+ if (!evidence || !evidence.callback || !evidence.continuation || callbackAt - evidence.boundAt < 120_000
562
+ || evidence.callback.event !== "telegram.callback_settled" || evidence.callback.meta.accepted !== true || evidence.callback.meta.reason !== "accepted")
563
+ return null;
564
+ return { elapsedMs: callbackAt - evidence.boundAt, mutationCount, promptTerminal: approval.terminalPrompt, replayMutationCount: approval.replayMutationCount, resumed: approval.continuationCompleted, state: approval.state };
565
+ }
566
+ case "unit-16j-denial": {
567
+ if (!exactFreshTelegramLifecycle(before, after) || !intendedRestartApproval(approval) || !completeAttemptLedgerLinked || approval.state !== "denied" || scenarioMutationCount !== 0 || approval.replayMutationCount !== 0 || !approval.continuationCompleted)
568
+ return null;
569
+ if (!approval.terminalPrompt)
570
+ return null;
571
+ const evidence = exactApprovalEvidence(before, after, approval);
572
+ if (!evidence?.callback || evidence.callback.event !== "telegram.callback_settled" || !evidence.continuation || evidence.callback.meta.accepted !== false || evidence.callback.meta.reason !== "decision_refused")
573
+ return null;
574
+ return { mutationCount, promptTerminal: approval.terminalPrompt, replayMutationCount: approval.replayMutationCount, resumed: approval.continuationCompleted, state: approval.state };
575
+ }
576
+ case "unit-16k-timeout-stale": {
577
+ if (!intendedRestartApproval(approval) || !completeAttemptLedgerLinked || approval.state !== "expired" || scenarioMutationCount !== 0 || approval.replayMutationCount !== 0)
578
+ return null;
579
+ if (!approval.buttonsRemoved || !approval.terminalPrompt || !approval.staleAcknowledged)
580
+ return null;
581
+ const evidence = exactApprovalEvidence(before, after, approval);
582
+ if (!terminalizedWithinTtlJitter(evidence) || !evidence || evidence.callback !== null || evidence.continuation !== null || evidence.staleCallback === null)
583
+ return null;
584
+ const driver = after.interactiveDriver;
585
+ if (!driver || driver.label !== "unit-16k-timeout-stale" || !interactiveReceiptBindsApproval(driver, approval)
586
+ || driver.callbackAttempts !== 1 || driver.distinctQueryCount !== 1 || driver.settledCount !== 1
587
+ || driver.claimCount !== 0 || driver.mutationCount !== 0 || !driver.staleAcknowledged || !driver.promptTerminal)
588
+ return null;
589
+ return { buttonsRemoved: approval.buttonsRemoved, mutationCount, promptTerminal: approval.terminalPrompt, staleAcknowledged: approval.staleAcknowledged, staleReplayMutationCount: approval.replayMutationCount, state: approval.state };
590
+ }
591
+ case "unit-16l-duplicate-callback": {
592
+ if (!intendedRestartApproval(approval) || !completeAttemptLedgerLinked || approval.callbackCount < 1 || approval.settledCount < 1 || approval.claimCount !== 1 || !approval.terminalPrompt || approval.replayMutationCount !== 0 || mutationCount !== 1 || scenarioMutationCount !== 1 || !restartSucceeded)
593
+ return null;
594
+ const evidence = exactApprovalEvidence(before, after, approval);
595
+ if (!evidence?.callback || evidence.callback.event !== "telegram.callback_settled" || !evidence.continuation || evidence.callback.meta.accepted !== true || evidence.callback.meta.reason !== "accepted")
596
+ return null;
597
+ const driver = after.interactiveDriver;
598
+ if (!driver || driver.label !== "unit-16l-duplicate-callback"
599
+ || !interactiveReceiptBindsApproval(driver, approval) || driver.callbackAttempts !== 2 || driver.distinctQueryCount !== 2
600
+ || !driver.barrierObserved || driver.settledCount !== 2 || driver.claimCount !== 1 || driver.mutationCount !== 1
601
+ || driver.staleReplayAttempts !== 1 || !driver.staleReplaySettled || driver.staleReplayMutationCount !== 0
602
+ || !driver.promptTerminal || driver.writeCredentialObserved)
603
+ return null;
604
+ return { callbackCount: driver.callbackAttempts, claimCount: driver.claimCount, mutationCount, promptTerminal: approval.terminalPrompt, replayMutationCount: approval.replayMutationCount, settledCount: driver.settledCount, staleReplaySettled: true, writeCredentialAbsent: true };
605
+ }
606
+ case "unit-16m-restart-continuation":
607
+ if (!intendedRestartApproval(approval) || !completeAttemptLedgerLinked || approval.state !== "succeeded" || mutationCount !== 1 || scenarioMutationCount !== 1 || !restartSucceeded || attemptedIndeterminateRetryCount !== 0)
608
+ return null;
609
+ {
610
+ const evidence = exactApprovalEvidence(before, after, approval);
611
+ if (!evidence?.continuation || !evidence.callback || evidence.callback.event !== "telegram.callback_recovery_settled"
612
+ || evidence.callback.meta.acknowledgementState !== "indeterminate_after_restart"
613
+ || evidence.callback.meta.accepted !== true || evidence.callback.meta.reason !== "accepted"
614
+ || typeof evidence.callback.meta.decisionAttemptDigest !== "string" || !SHA256.test(evidence.callback.meta.decisionAttemptDigest))
615
+ return null;
616
+ }
617
+ if (!after.interactiveDriver || after.interactiveDriver.label !== "unit-16m-restart-continuation"
618
+ || !interactiveReceiptBindsApproval(after.interactiveDriver, approval)
619
+ || !after.interactiveDriver.pendingRestored || after.interactiveDriver.pendingDigestBefore !== after.interactiveDriver.pendingDigestAfter || after.interactiveDriver.callbackAttempts !== 1 || after.interactiveDriver.mutationCount !== 1
620
+ || after.interactiveDriver.approvalEpochBefore !== after.interactiveDriver.approvalEpochAfterRestart
621
+ || after.interactiveDriver.continuationEpochAfter <= after.interactiveDriver.approvalEpochAfterRestart
622
+ || approval.continuationEpoch !== after.interactiveDriver.continuationEpochAfter || approval.continuationState !== "completed"
623
+ || after.interactiveDriver.restartCountAfter !== after.interactiveDriver.restartCountBefore + 1
624
+ || !after.interactiveDriver.indeterminateRecoveryObserved || !after.interactiveDriver.attemptedRecoveryReopened
625
+ || !SHA256.test(after.interactiveDriver.attemptedRecordDigest) || !SHA256.test(after.interactiveDriver.recoveredRecordDigest)
626
+ || after.interactiveDriver.attemptedRecordDigest === after.interactiveDriver.recoveredRecordDigest
627
+ || after.interactiveDriver.indeterminateRetryCount !== 0)
628
+ return null;
629
+ return {
630
+ attemptedIndeterminateRetryCount, mutationCount, preAttemptResumed: after.interactiveDriver.pendingRestored,
631
+ butlerRestartObserved: true, checkpointEpochPreserved: true, continuationEpochAdvanced: true,
632
+ 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,
633
+ state: approval.state,
634
+ };
635
+ }
636
+ return null;
637
+ }
638
+ function createSanctuaryScenarioCapture(deps) {
639
+ const receiptRoot = deps.receiptRoot ?? path.join(deps.agentRoot, "state", "acceptance", "receipts");
640
+ const receiptPath = (checkpointDigest) => path.join(receiptRoot, `${checkpointDigest}.json`);
641
+ return async (input) => {
642
+ if (input.phase === "begin") {
643
+ const startedAt = new Date(deps.now()).toISOString();
644
+ const scenarioHandleDigest = (0, node_crypto_1.createHash)("sha256").update((0, node_crypto_1.randomBytes)(32)).digest("hex");
645
+ const checkpointDigest = hash({ label: input.label, gate: input.externalGate, sources: input.sources, startedAt, scenarioHandleDigest });
646
+ const healthScenario = HEALTH_SCENARIO_LABELS.has(input.label);
647
+ const capturedBefore = await deps.readFacts(input.label, scenarioHandleDigest, healthScenario ? { skipContainerSnapshot: true } : undefined);
648
+ const before = { ...capturedBefore, sourceValues: Object.fromEntries(Object.entries(capturedBefore.sourceValues).map(([source, value]) => [source, hash(value)])) };
649
+ const receipt = { schemaVersion: "sanctuary-acceptance-receipt-v1", label: input.label, gate: input.externalGate, sources: [...input.sources], checkpointDigest, scenarioHandleDigest, startedAt, before };
650
+ (0, sanctuary_acceptance_marker_1.writeSanctuaryAcceptanceMarker)("sanctuary", { schemaVersion: "sanctuary-acceptance-marker-v1", label: input.label, scenarioHandleDigest, startedAt }, deps.agentRoot);
651
+ atomicPrivateJson(receiptPath(checkpointDigest), receipt);
652
+ (0, sanctuary_acceptance_marker_1.publishSanctuaryAcceptanceGateStatus)({ label: input.label, gate: input.externalGate, phase: "waiting", startedAt }, deps.gateStatusPath);
653
+ if (healthScenario) {
654
+ if (!deps.healthDriver)
655
+ throw new Error("health scenario driver is unavailable");
656
+ await deps.healthDriver.begin(input.label, scenarioHandleDigest);
657
+ }
658
+ (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 } });
659
+ return { state: "waiting", checkpointDigest };
660
+ }
661
+ if (!input.checkpointDigest || !SHA256.test(input.checkpointDigest))
662
+ throw new Error("scenario checkpoint digest is invalid");
663
+ const filePath = receiptPath(input.checkpointDigest);
664
+ const receipt = JSON.parse(fs.readFileSync(filePath, "utf8"));
665
+ if (receipt.checkpointDigest !== input.checkpointDigest || receipt.label !== input.label || receipt.gate !== input.externalGate || JSON.stringify(receipt.sources) !== JSON.stringify(input.sources))
666
+ throw new Error("scenario checkpoint binding mismatch");
667
+ let factOptions;
668
+ if (HEALTH_SCENARIO_LABELS.has(input.label)) {
669
+ if (!deps.healthDriver)
670
+ throw new Error("health scenario driver is unavailable");
671
+ const probe = await deps.healthDriver.poll(input.label, receipt.scenarioHandleDigest);
672
+ if (probe.state === "waiting")
673
+ return { state: "waiting", checkpointDigest: receipt.checkpointDigest };
674
+ factOptions = { containerSnapshot: probe.containerSnapshot };
675
+ }
676
+ if (INTERACTIVE_DRIVER_LABELS.has(input.label)) {
677
+ if (!deps.interactiveDriver)
678
+ throw new Error("interactive scenario driver is unavailable");
679
+ const driven = await deps.interactiveDriver.poll(input.label, receipt.scenarioHandleDigest);
680
+ if (driven.state === "waiting")
681
+ return { state: "waiting", checkpointDigest: receipt.checkpointDigest };
682
+ }
683
+ if (READ_ONLY_DENIAL_LABELS.has(input.label)) {
684
+ if (!deps.denialDriver)
685
+ throw new Error("read-only denial scenario driver is unavailable");
686
+ const driven = await deps.denialDriver.poll(input.label, receipt.scenarioHandleDigest);
687
+ if (driven.state === "waiting")
688
+ return { state: "waiting", checkpointDigest: receipt.checkpointDigest };
689
+ }
690
+ const after = await deps.readFacts(input.label, receipt.scenarioHandleDigest, factOptions);
691
+ if (input.label === "unit-15c-1-no-callback-terminalization" && !receipt.noCallbackBaseline) {
692
+ const activeApproval = after.approvals.find((approval) => approval.state === "proposed" || approval.state === "claimed");
693
+ if (activeApproval) {
694
+ const baseline = {
695
+ approvalId: activeApproval.approvalId,
696
+ offsetDigest: hash(after.sourceValues["telegram-offset"]),
697
+ inboundEventCount: after.events.filter((entry) => entry.event === "telegram.callback_settled" || entry.event === "telegram.callback_recovery_settled" || entry.event === "telegram.update_dropped").length,
698
+ };
699
+ receipt.noCallbackBaseline = baseline;
700
+ receipt.before.sourceValues["no-callback-baseline"] = baseline;
701
+ atomicPrivateJson(filePath, receipt);
702
+ return { state: "waiting", checkpointDigest: receipt.checkpointDigest };
703
+ }
704
+ }
705
+ const candidate = deriveSanctuaryScenarioAssertions(input.label, receipt.before, after, deps.now(), receipt.scenarioHandleDigest);
706
+ if (!candidate)
707
+ return { state: "waiting", checkpointDigest: receipt.checkpointDigest };
708
+ const assertions = (0, sanctuary_acceptance_harness_1.validateSanctuaryUnit16EvidenceAssertions)(input.label, candidate);
709
+ const sourceDigests = Object.fromEntries(input.sources.map((source) => [source, hash(after.sourceValues[source])]));
710
+ if (HEALTH_SCENARIO_LABELS.has(input.label))
711
+ await deps.healthDriver.recover(input.label, receipt.scenarioHandleDigest);
712
+ if (INTERACTIVE_DRIVER_LABELS.has(input.label) && await deps.interactiveDriver.complete(input.label, receipt.scenarioHandleDigest) !== "complete") {
713
+ throw new Error("interactive scenario requires inspect-before-retry");
714
+ }
715
+ if (INTERACTIVE_DRIVER_LABELS.has(input.label))
716
+ await deps.interactiveDriver.cleanup(input.label, receipt.scenarioHandleDigest);
717
+ if (READ_ONLY_DENIAL_LABELS.has(input.label))
718
+ await deps.denialDriver.complete(input.label, receipt.scenarioHandleDigest);
719
+ (0, sanctuary_acceptance_marker_1.publishSanctuaryAcceptanceGateStatus)({ label: input.label, gate: input.externalGate, phase: "complete", startedAt: receipt.startedAt }, deps.gateStatusPath);
720
+ (0, sanctuary_acceptance_marker_1.clearSanctuaryAcceptanceMarker)("sanctuary", receipt.scenarioHandleDigest, deps.agentRoot);
721
+ fs.unlinkSync(filePath);
722
+ (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 } });
723
+ return { state: "complete", checkpointDigest: receipt.checkpointDigest, sourceDigests, assertions };
724
+ };
725
+ }
726
+ function boundedReceiptEntries(receiptRoot) {
727
+ const rootHandle = fs.openSync(receiptRoot, fs.constants.O_RDONLY | fs.constants.O_DIRECTORY | fs.constants.O_NOFOLLOW);
728
+ try {
729
+ const rootMetadata = fs.fstatSync(rootHandle);
730
+ const pathMetadata = fs.lstatSync(receiptRoot);
731
+ if (!rootMetadata.isDirectory() || !pathMetadata.isDirectory() || rootMetadata.dev !== pathMetadata.dev || rootMetadata.ino !== pathMetadata.ino) {
732
+ throw new Error("acceptance receipt root changed during cleanup");
733
+ }
734
+ const directory = fs.opendirSync(receiptRoot);
735
+ const entries = [];
736
+ try {
737
+ for (;;) {
738
+ const entry = directory.readSync();
739
+ if (!entry)
740
+ break;
741
+ entries.push(entry);
742
+ if (entries.length === 33)
743
+ throw new Error("acceptance receipt cleanup exceeds its bound");
744
+ }
745
+ }
746
+ finally {
747
+ directory.closeSync();
748
+ }
749
+ const finalMetadata = fs.lstatSync(receiptRoot);
750
+ if (!finalMetadata.isDirectory() || rootMetadata.dev !== finalMetadata.dev || rootMetadata.ino !== finalMetadata.ino) {
751
+ throw new Error("acceptance receipt root changed during cleanup");
752
+ }
753
+ return entries;
754
+ }
755
+ finally {
756
+ fs.closeSync(rootHandle);
757
+ }
758
+ }
759
+ function prepareSafeQuarantineRoot(sourceParent, sourceParentHandle) {
760
+ const quarantineRoot = path.join(sourceParent, "quarantine");
761
+ const rejectedPath = path.join(sourceParent, `.quarantine-rejected-${(0, node_crypto_1.randomUUID)()}`);
762
+ const boundQuarantineRoot = (0, sanctuary_acceptance_marker_1.boundDirectoryEntryPath)(sourceParentHandle, sourceParent, path.basename(quarantineRoot));
763
+ const boundRejectedPath = (0, sanctuary_acceptance_marker_1.boundDirectoryEntryPath)(sourceParentHandle, sourceParent, path.basename(rejectedPath));
764
+ let quarantineExists = false;
765
+ let rejectedMetadata = null;
766
+ try {
767
+ const existing = fs.lstatSync(boundQuarantineRoot);
768
+ if (existing.isDirectory())
769
+ quarantineExists = true;
770
+ else {
771
+ (0, sanctuary_acceptance_marker_1.secureRenameBoundInodeSync)(sourceParentHandle, path.basename(quarantineRoot), sourceParentHandle, path.basename(rejectedPath), existing);
772
+ const rejected = fs.lstatSync(boundRejectedPath);
773
+ if (rejected.dev !== existing.dev || rejected.ino !== existing.ino)
774
+ throw new Error("acceptance quarantine rejection changed during move");
775
+ rejectedMetadata = rejected;
776
+ }
777
+ }
778
+ catch (error) {
779
+ if (error.code !== "ENOENT")
780
+ throw error;
781
+ }
782
+ if (!quarantineExists)
783
+ fs.mkdirSync(boundQuarantineRoot, { mode: 0o700 });
784
+ const quarantineHandle = fs.openSync(boundQuarantineRoot, fs.constants.O_RDONLY | fs.constants.O_DIRECTORY | fs.constants.O_NOFOLLOW);
785
+ try {
786
+ const metadata = fs.fstatSync(quarantineHandle);
787
+ const pathMetadata = fs.lstatSync(boundQuarantineRoot);
788
+ if (!metadata.isDirectory() || !pathMetadata.isDirectory() || metadata.dev !== pathMetadata.dev || metadata.ino !== pathMetadata.ino) {
789
+ throw new Error("acceptance quarantine root changed");
790
+ }
791
+ fs.fchmodSync(quarantineHandle, 0o700);
792
+ if (rejectedMetadata) {
793
+ (0, sanctuary_acceptance_marker_1.secureRenameBoundInodeSync)(sourceParentHandle, path.basename(rejectedPath), quarantineHandle, path.basename(rejectedPath).slice(1), rejectedMetadata);
794
+ }
795
+ fs.fsyncSync(quarantineHandle);
796
+ fs.fsyncSync(sourceParentHandle);
797
+ return { quarantineRoot, quarantineHandle };
798
+ }
799
+ catch (error) {
800
+ fs.closeSync(quarantineHandle);
801
+ throw error;
802
+ }
803
+ }
804
+ function durableQuarantineReceiptRoot(receiptRoot) {
805
+ const sourceParent = path.dirname(receiptRoot);
806
+ const sourceParentHandle = fs.openSync(sourceParent, fs.constants.O_RDONLY | fs.constants.O_DIRECTORY | fs.constants.O_NOFOLLOW);
807
+ let rootHandle = null;
808
+ let quarantineHandle = null;
809
+ let quarantineRoot = "";
810
+ try {
811
+ const sourceParentMetadata = fs.fstatSync(sourceParentHandle);
812
+ const sourceParentPathMetadata = fs.lstatSync(sourceParent);
813
+ if (!sourceParentMetadata.isDirectory() || !sourceParentPathMetadata.isDirectory()
814
+ || sourceParentMetadata.dev !== sourceParentPathMetadata.dev || sourceParentMetadata.ino !== sourceParentPathMetadata.ino)
815
+ throw new Error("acceptance receipt parent changed during quarantine");
816
+ const prepared = prepareSafeQuarantineRoot(sourceParent, sourceParentHandle);
817
+ quarantineRoot = prepared.quarantineRoot;
818
+ quarantineHandle = prepared.quarantineHandle;
819
+ const boundReceiptRoot = (0, sanctuary_acceptance_marker_1.boundDirectoryEntryPath)(sourceParentHandle, sourceParent, path.basename(receiptRoot));
820
+ const rootPathMetadata = fs.lstatSync(boundReceiptRoot);
821
+ if (rootPathMetadata.isDirectory() || rootPathMetadata.isFile()) {
822
+ rootHandle = fs.openSync(boundReceiptRoot, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK);
823
+ }
824
+ if (rootHandle !== null) {
825
+ const rootMetadata = fs.fstatSync(rootHandle);
826
+ const reboundMetadata = fs.lstatSync(boundReceiptRoot);
827
+ if (rootMetadata.dev !== reboundMetadata.dev || rootMetadata.ino !== reboundMetadata.ino)
828
+ throw new Error("acceptance receipt root changed during cleanup");
829
+ }
830
+ const finalRootPathMetadata = fs.lstatSync(boundReceiptRoot);
831
+ if (finalRootPathMetadata.dev !== rootPathMetadata.dev || finalRootPathMetadata.ino !== rootPathMetadata.ino)
832
+ throw new Error("acceptance receipt root changed before quarantine move");
833
+ const quarantinePath = path.join(quarantineRoot, `receipts-${(0, node_crypto_1.randomUUID)()}`);
834
+ (0, sanctuary_acceptance_marker_1.secureRenameBoundInodeSync)(sourceParentHandle, path.basename(receiptRoot), quarantineHandle, path.basename(quarantinePath), rootPathMetadata);
835
+ const movedMetadata = fs.lstatSync((0, sanctuary_acceptance_marker_1.boundDirectoryEntryPath)(quarantineHandle, quarantineRoot, path.basename(quarantinePath)));
836
+ if (movedMetadata.dev !== rootPathMetadata.dev || movedMetadata.ino !== rootPathMetadata.ino) {
837
+ throw new Error("acceptance receipt quarantine changed during move");
838
+ }
839
+ if (rootHandle !== null)
840
+ fs.fsyncSync(rootHandle);
841
+ fs.fsyncSync(sourceParentHandle);
842
+ fs.fsyncSync(quarantineHandle);
843
+ fs.mkdirSync(boundReceiptRoot, { mode: 0o700 });
844
+ const replacementHandle = fs.openSync(boundReceiptRoot, fs.constants.O_RDONLY | fs.constants.O_DIRECTORY | fs.constants.O_NOFOLLOW);
845
+ try {
846
+ fs.fsyncSync(replacementHandle);
847
+ }
848
+ finally {
849
+ fs.closeSync(replacementHandle);
850
+ }
851
+ fs.fsyncSync(sourceParentHandle);
852
+ }
853
+ finally {
854
+ fs.closeSync(sourceParentHandle);
855
+ if (quarantineHandle !== null)
856
+ fs.closeSync(quarantineHandle);
857
+ if (rootHandle !== null)
858
+ fs.closeSync(rootHandle);
859
+ }
860
+ }
861
+ function finalizeSanctuaryScenarioCapture(gateStatusPath, configuredReceiptRoot, configuredAgentRoot) {
862
+ const receiptRoot = configuredReceiptRoot ?? path.join(configuredAgentRoot ?? (0, identity_1.getAgentRoot)("sanctuary"), "state", "acceptance", "receipts");
863
+ const errors = [];
864
+ let marker = null;
865
+ let markerReadable = true;
866
+ let normalCleanupFailed = false;
867
+ try {
868
+ marker = (0, sanctuary_acceptance_marker_1.readSanctuaryAcceptanceMarker)("sanctuary", configuredAgentRoot);
869
+ }
870
+ catch (error) {
871
+ markerReadable = false;
872
+ errors.push(error);
873
+ }
874
+ let activeReceipt = null;
875
+ let receiptSetCanonical = true;
876
+ try {
877
+ const entries = boundedReceiptEntries(receiptRoot);
878
+ if (entries.length !== (marker ? 1 : 0))
879
+ throw new Error(marker ? "active acceptance receipt is absent or ambiguous" : "acceptance receipt exists without an active marker");
880
+ if (marker) {
881
+ const entry = entries[0];
882
+ if (!entry.isFile() || !entry.name.endsWith(".json") || !SHA256.test(entry.name.slice(0, -5)))
883
+ throw new Error("acceptance receipt cleanup found a noncanonical entry");
884
+ const filePath = path.join(receiptRoot, entry.name);
885
+ const handle = fs.openSync(filePath, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
886
+ try {
887
+ const metadata = fs.fstatSync(handle);
888
+ if (!metadata.isFile() || metadata.size > 4 * 1024 * 1024 || (metadata.mode & 0o777) !== 0o600)
889
+ throw new Error("acceptance receipt cleanup found an invalid file");
890
+ const receipt = JSON.parse(fs.readFileSync(handle, "utf8"));
891
+ const pathMetadata = fs.lstatSync(filePath);
892
+ if (!pathMetadata.isFile() || pathMetadata.dev !== metadata.dev || pathMetadata.ino !== metadata.ino)
893
+ throw new Error("acceptance receipt changed during cleanup");
894
+ if (receipt.checkpointDigest !== entry.name.slice(0, -5) || receipt.scenarioHandleDigest !== marker.scenarioHandleDigest
895
+ || receipt.label !== marker.label || receipt.startedAt !== marker.startedAt)
896
+ throw new Error("active acceptance receipt is absent or ambiguous");
897
+ activeReceipt = filePath;
898
+ }
899
+ finally {
900
+ fs.closeSync(handle);
901
+ }
902
+ }
903
+ }
904
+ catch (error) {
905
+ if (error.code !== "ENOENT") {
906
+ receiptSetCanonical = false;
907
+ errors.push(error);
908
+ }
909
+ else if (marker) {
910
+ receiptSetCanonical = false;
911
+ errors.push(new Error("active acceptance receipt is absent or ambiguous"));
912
+ }
913
+ }
914
+ if (markerReadable && marker && receiptSetCanonical && activeReceipt) {
915
+ try {
916
+ fs.unlinkSync(activeReceipt);
917
+ }
918
+ catch (error) {
919
+ normalCleanupFailed = true;
920
+ errors.push(error);
921
+ }
922
+ if (!normalCleanupFailed) {
923
+ try {
924
+ (0, sanctuary_acceptance_marker_1.clearSanctuaryAcceptanceMarker)("sanctuary", marker.scenarioHandleDigest, configuredAgentRoot);
925
+ }
926
+ catch (error) {
927
+ normalCleanupFailed = true;
928
+ errors.push(error);
929
+ }
930
+ }
931
+ }
932
+ if (!markerReadable || !receiptSetCanonical || (marker && activeReceipt === null) || normalCleanupFailed) {
933
+ try {
934
+ durableQuarantineReceiptRoot(receiptRoot);
935
+ }
936
+ catch (error) {
937
+ if (error.code !== "ENOENT")
938
+ errors.push(error);
939
+ }
940
+ try {
941
+ (0, sanctuary_acceptance_marker_1.quarantineSanctuaryAcceptanceMarker)("sanctuary", configuredAgentRoot);
942
+ }
943
+ catch (error) {
944
+ errors.push(error);
945
+ }
946
+ }
947
+ try {
948
+ (0, sanctuary_acceptance_marker_1.clearSanctuaryAcceptanceGateStatus)(gateStatusPath);
949
+ }
950
+ catch (error) {
951
+ errors.push(error);
952
+ }
953
+ if (errors.length > 0)
954
+ throw new AggregateError(errors, "Sanctuary scenario finalization failed");
955
+ }