@ouro.bot/cli 0.1.0-alpha.98 → 0.1.0-alpha.981

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 (763) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +171 -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 +6195 -16
  16. package/deploy/unraid/Dockerfile +58 -0
  17. package/deploy/unraid/INSTALL.md +64 -0
  18. package/deploy/unraid/README.txt +2997 -0
  19. package/deploy/unraid/audit-container-spec.sh +4 -0
  20. package/deploy/unraid/container-runtime.json +1 -0
  21. package/deploy/unraid/credit-gateway.md +95 -0
  22. package/deploy/unraid/docker-man-template-transaction.mjs +727 -0
  23. package/deploy/unraid/docker-man-template-xml.cjs +105 -0
  24. package/deploy/unraid/install.sh +274 -0
  25. package/deploy/unraid/migrate-sanctuary-bundle.mjs +63 -0
  26. package/deploy/unraid/ouro-events/bootstrap-spool.sh +92 -0
  27. package/deploy/unraid/ouro-events/emit-event.mjs +310 -0
  28. package/deploy/unraid/ouro-events/emit-usenet-event.sh +54 -0
  29. package/deploy/unraid/ouro-events/install-usenet-guard.sh +435 -0
  30. package/deploy/unraid/ouro-events/usenet-health.sh +176 -0
  31. package/deploy/unraid/sanctuary-acceptance-adapter.sh +43 -0
  32. package/deploy/unraid/sanctuary-acceptance-contract.json +198 -0
  33. package/deploy/unraid/sanctuary-acceptance-harness.sh +23 -0
  34. package/deploy/unraid/sanctuary-authority-installation.json +36 -0
  35. package/deploy/unraid/sanctuary-authority-service.sh +30 -0
  36. package/deploy/unraid/sanctuary-butler-upgrade.mjs +1377 -0
  37. package/deploy/unraid/sanctuary-deployment-target.mjs +710 -0
  38. package/deploy/unraid/sanctuary-replay-gate.mjs +1776 -0
  39. package/deploy/unraid/sanctuary-safe-rename.py +24 -0
  40. package/deploy/unraid/sanctuary-unit16-host-broker.mjs +2088 -0
  41. package/deploy/unraid/sanctuary-unit16-run.sh +570 -0
  42. package/deploy/unraid/sanctuary-unit18-target-audit.sh +27 -0
  43. package/deploy/unraid/sanctuary.ouro/agent.json +66 -0
  44. package/deploy/unraid/sanctuary.ouro/arc/README.md +3 -0
  45. package/deploy/unraid/sanctuary.ouro/awaiting/house-care-sweep.md +17 -0
  46. package/deploy/unraid/sanctuary.ouro/bundle-meta.json +5 -0
  47. package/deploy/unraid/sanctuary.ouro/habits/sanctuary-health.md +8 -0
  48. package/deploy/unraid/sanctuary.ouro/mcp/media-mcp.mjs +2255 -0
  49. package/deploy/unraid/sanctuary.ouro/provider-readiness.json +11 -0
  50. package/deploy/unraid/sanctuary.ouro/psyche/ASPIRATIONS.md +5 -0
  51. package/deploy/unraid/sanctuary.ouro/psyche/IDENTITY.md +7 -0
  52. package/deploy/unraid/sanctuary.ouro/psyche/LORE.md +33 -0
  53. package/deploy/unraid/sanctuary.ouro/psyche/SOUL.md +27 -0
  54. package/deploy/unraid/sanctuary.ouro/psyche/TACIT.md +21 -0
  55. package/deploy/unraid/sanctuary.ouro/state/policy/steward.json +7 -0
  56. package/deploy/unraid/sanctuary.ouro/tool-profiles.json +232 -0
  57. package/deploy/unraid/sanctuary.xml +29 -0
  58. package/dist/a2a/card.js +57 -0
  59. package/dist/a2a/client.js +250 -0
  60. package/dist/a2a/config.js +50 -0
  61. package/dist/a2a/delegated-command-grants.js +93 -0
  62. package/dist/a2a/delegated-command.js +197 -0
  63. package/dist/a2a/delegation-stores.js +56 -0
  64. package/dist/a2a/did-resolution.js +93 -0
  65. package/dist/a2a/escalation-grants.js +84 -0
  66. package/dist/a2a/escalation-wiring.js +60 -0
  67. package/dist/a2a/identity.js +258 -0
  68. package/dist/a2a/inbound-share.js +110 -0
  69. package/dist/a2a/mission-result-wire.js +196 -0
  70. package/dist/a2a/onboarding.js +133 -0
  71. package/dist/a2a/operator-trust.js +399 -0
  72. package/dist/a2a/outbox-client.js +57 -0
  73. package/dist/a2a/outbox-store.js +188 -0
  74. package/dist/a2a/outbox-wire.js +95 -0
  75. package/dist/a2a/pin-store.js +132 -0
  76. package/dist/a2a/replay-grant-holders.js +33 -0
  77. package/dist/a2a/replay-harness.js +188 -0
  78. package/dist/a2a/resolution-proof.js +53 -0
  79. package/dist/a2a/sealed-chat.js +113 -0
  80. package/dist/a2a/seen-ledger.js +120 -0
  81. package/dist/a2a/server.js +771 -0
  82. package/dist/a2a/task-store.js +69 -0
  83. package/dist/a2a/transport.js +45 -0
  84. package/dist/a2a/trust-commands.js +203 -0
  85. package/dist/a2a/trusted-files.js +185 -0
  86. package/dist/a2a/types.js +3 -0
  87. package/dist/arc/attention-types.js +8 -0
  88. package/dist/arc/cares.js +360 -0
  89. package/dist/arc/episodes.js +118 -0
  90. package/dist/arc/evolution.js +490 -0
  91. package/dist/arc/flight-recorder.js +1034 -0
  92. package/dist/arc/intentions.js +134 -0
  93. package/dist/arc/json-store.js +117 -0
  94. package/dist/arc/obligations.js +386 -0
  95. package/dist/arc/packets.js +336 -0
  96. package/dist/arc/presence.js +185 -0
  97. package/dist/arc/task-lifecycle.js +57 -0
  98. package/dist/commerce/store.js +755 -0
  99. package/dist/commerce/types.js +3 -0
  100. package/dist/gateway/keys.js +117 -0
  101. package/dist/gateway/server.js +429 -0
  102. package/dist/heart/active-work.js +798 -40
  103. package/dist/heart/agent-entry.js +252 -3
  104. package/dist/heart/approval-files.js +113 -0
  105. package/dist/heart/approval-store.js +960 -0
  106. package/dist/heart/attachments/image-normalize.js +201 -0
  107. package/dist/heart/attachments/materialize.js +97 -0
  108. package/dist/heart/attachments/originals.js +88 -0
  109. package/dist/heart/attachments/render.js +29 -0
  110. package/dist/heart/attachments/sources/bluebubbles.js +156 -0
  111. package/dist/heart/attachments/sources/cli-local-file.js +78 -0
  112. package/dist/heart/attachments/sources/index.js +18 -0
  113. package/dist/heart/attachments/sources/telegram.js +71 -0
  114. package/dist/heart/attachments/store.js +132 -0
  115. package/dist/heart/attachments/types.js +93 -0
  116. package/dist/heart/auth/auth-flow.js +511 -0
  117. package/dist/heart/autonomy-budget.js +478 -0
  118. package/dist/heart/awaiting/a2a-await-delivery.js +109 -0
  119. package/dist/heart/awaiting/await-alert.js +152 -0
  120. package/dist/heart/awaiting/await-expiry.js +143 -0
  121. package/dist/heart/awaiting/await-loader.js +91 -0
  122. package/dist/heart/awaiting/await-parser.js +152 -0
  123. package/dist/heart/awaiting/await-runtime-state.js +130 -0
  124. package/dist/heart/awaiting/await-scheduler.js +391 -0
  125. package/dist/heart/background-operations.js +281 -0
  126. package/dist/heart/bluebubbles-host-account.js +78 -0
  127. package/dist/heart/bridges/manager.js +137 -17
  128. package/dist/heart/bridges/store.js +14 -2
  129. package/dist/heart/bundle-state.js +168 -0
  130. package/dist/heart/commitments.js +44 -18
  131. package/dist/heart/config-registry.js +331 -0
  132. package/dist/heart/config.js +178 -131
  133. package/dist/heart/context-loss-gauntlet.js +387 -0
  134. package/dist/heart/context-loss-sentinel.js +1204 -0
  135. package/dist/heart/core.js +2377 -586
  136. package/dist/heart/cross-chat-delivery.js +9 -22
  137. package/dist/heart/daemon/agent-config-check.js +474 -0
  138. package/dist/heart/daemon/agent-discovery.js +283 -3
  139. package/dist/heart/daemon/agent-home.js +319 -0
  140. package/dist/heart/daemon/agent-service.js +542 -0
  141. package/dist/heart/daemon/agentic-repair.js +547 -0
  142. package/dist/heart/daemon/await-private-wake.js +59 -0
  143. package/dist/heart/daemon/bluebubbles-cli-integration.js +71 -0
  144. package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
  145. package/dist/heart/daemon/bluebubbles-host-protocol.js +433 -0
  146. package/dist/heart/daemon/bluebubbles-host.js +273 -0
  147. package/dist/heart/daemon/boot-sync-probe.js +197 -0
  148. package/dist/heart/daemon/cadence.js +180 -0
  149. package/dist/heart/daemon/cli-arg-redaction.js +26 -0
  150. package/dist/heart/daemon/cli-defaults.js +881 -0
  151. package/dist/heart/daemon/cli-desk.js +331 -0
  152. package/dist/heart/daemon/cli-exec.js +9272 -0
  153. package/dist/heart/daemon/cli-help.js +754 -0
  154. package/dist/heart/daemon/cli-parse.js +3329 -0
  155. package/dist/heart/daemon/cli-render-doctor.js +57 -0
  156. package/dist/heart/daemon/cli-render.js +819 -0
  157. package/dist/heart/daemon/cli-types.js +8 -0
  158. package/dist/heart/daemon/connect-bay.js +323 -0
  159. package/dist/heart/daemon/container-credential-bootstrap.js +273 -0
  160. package/dist/heart/daemon/container-healthcheck.js +112 -0
  161. package/dist/heart/daemon/container-runtime.js +97 -0
  162. package/dist/heart/daemon/container-spec-auditor-main.js +171 -0
  163. package/dist/heart/daemon/container-spec-auditor.js +241 -0
  164. package/dist/heart/daemon/daemon-bootstrap-startup.js +115 -0
  165. package/dist/heart/daemon/daemon-cli.js +29 -2373
  166. package/dist/heart/daemon/daemon-entry.js +953 -11
  167. package/dist/heart/daemon/daemon-health.js +186 -0
  168. package/dist/heart/daemon/daemon-rollup.js +57 -0
  169. package/dist/heart/daemon/daemon-runtime-sync.js +88 -13
  170. package/dist/heart/daemon/daemon-tombstone.js +236 -0
  171. package/dist/heart/daemon/daemon.js +2110 -104
  172. package/dist/heart/daemon/desk-admin.js +420 -0
  173. package/dist/heart/daemon/dns-workflow.js +394 -0
  174. package/dist/heart/daemon/doctor-types.js +8 -0
  175. package/dist/heart/daemon/doctor.js +1766 -0
  176. package/dist/heart/daemon/freshness.js +345 -0
  177. package/dist/heart/daemon/habit-private-wake.js +61 -0
  178. package/dist/heart/daemon/health-monitor.js +154 -11
  179. package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
  180. package/dist/heart/daemon/hooks/bundle-meta.js +135 -1
  181. package/dist/heart/daemon/http-health-probe.js +80 -0
  182. package/dist/heart/daemon/human-command-screens.js +234 -0
  183. package/dist/heart/daemon/human-readiness.js +114 -0
  184. package/dist/heart/daemon/inner-status.js +78 -0
  185. package/dist/heart/daemon/interactive-repair.js +394 -0
  186. package/dist/heart/daemon/launchd.js +37 -8
  187. package/dist/heart/daemon/log-tailer.js +79 -10
  188. package/dist/heart/daemon/logs-prune.js +132 -0
  189. package/dist/heart/daemon/mcp-canary.js +524 -0
  190. package/dist/heart/daemon/message-router.js +65 -5
  191. package/dist/heart/daemon/migrate-to-desk.js +848 -0
  192. package/dist/heart/daemon/os-cron-deps.js +135 -0
  193. package/dist/heart/daemon/os-cron.js +22 -14
  194. package/dist/heart/daemon/ouro-bot-entry.js +6 -3
  195. package/dist/heart/daemon/ouro-entry.js +9 -3
  196. package/dist/heart/daemon/plugin-cli.js +432 -0
  197. package/dist/heart/daemon/process-manager.js +760 -61
  198. package/dist/heart/daemon/provider-discovery.js +137 -0
  199. package/dist/heart/daemon/provider-ping-progress.js +83 -0
  200. package/dist/heart/daemon/prunable-bundle.js +144 -0
  201. package/dist/heart/daemon/pulse.js +511 -0
  202. package/dist/heart/daemon/readiness-repair.js +365 -0
  203. package/dist/heart/daemon/run-hooks.js +2 -0
  204. package/dist/heart/daemon/runtime-logging.js +47 -14
  205. package/dist/heart/daemon/runtime-metadata.js +2 -30
  206. package/dist/heart/daemon/runtime-mode.js +13 -2
  207. package/dist/heart/daemon/safe-mode.js +161 -0
  208. package/dist/heart/daemon/sanctuary-acceptance-adapter.js +2662 -0
  209. package/dist/heart/daemon/sanctuary-acceptance-harness.js +1832 -0
  210. package/dist/heart/daemon/sanctuary-acceptance-marker.js +267 -0
  211. package/dist/heart/daemon/sanctuary-acceptance-scenarios.js +956 -0
  212. package/dist/heart/daemon/sanctuary-authority-codec.js +86 -0
  213. package/dist/heart/daemon/sanctuary-authority-epoch.js +291 -0
  214. package/dist/heart/daemon/sanctuary-authority-installation.js +161 -0
  215. package/dist/heart/daemon/sanctuary-authority-ledger.js +241 -0
  216. package/dist/heart/daemon/sanctuary-authority-root-lifecycle.js +1326 -0
  217. package/dist/heart/daemon/sanctuary-authority-vault-migration.js +79 -0
  218. package/dist/heart/daemon/sanctuary-bundle-migration.js +851 -0
  219. package/dist/heart/daemon/sanctuary-host-authority.js +1008 -0
  220. package/dist/heart/daemon/sanctuary-host-detached-supervisor.js +493 -0
  221. package/dist/heart/daemon/sanctuary-host-executor.js +670 -0
  222. package/dist/heart/daemon/sanctuary-host-linux-kernel.js +334 -0
  223. package/dist/heart/daemon/sanctuary-host-supervisor-entry.js +207 -0
  224. package/dist/heart/daemon/sanctuary-host-supervisor.js +95 -0
  225. package/dist/heart/daemon/sanctuary-package-management.js +103 -0
  226. package/dist/heart/daemon/sanctuary-scheduler-liveness.js +227 -0
  227. package/dist/heart/daemon/sanctuary-scheduler-origin.js +132 -0
  228. package/dist/heart/daemon/sanctuary-telegram-authority-entry.js +475 -0
  229. package/dist/heart/daemon/sanctuary-telegram-authority-gateway.js +585 -0
  230. package/dist/heart/daemon/sanctuary-telegram-authority-service.js +676 -0
  231. package/dist/heart/daemon/sense-manager.js +786 -48
  232. package/dist/heart/daemon/session-id-resolver.js +131 -0
  233. package/dist/heart/daemon/skill-management-installer.js +1 -1
  234. package/dist/heart/daemon/socket-client.js +180 -12
  235. package/dist/heart/daemon/stale-bundle-prune.js +113 -0
  236. package/dist/heart/daemon/startup-tui.js +339 -0
  237. package/dist/heart/daemon/supercronic-supervisor.js +282 -0
  238. package/dist/heart/daemon/task-scheduler.js +117 -39
  239. package/dist/heart/daemon/terminal-ui.js +503 -0
  240. package/dist/heart/daemon/thoughts.js +138 -54
  241. package/dist/heart/daemon/up-progress.js +366 -0
  242. package/dist/heart/daemon/vault-items.js +56 -0
  243. package/dist/heart/delegation.js +1 -4
  244. package/dist/heart/external-events/router.js +1531 -0
  245. package/dist/heart/failure-reports.js +336 -0
  246. package/dist/heart/file-lock.js +109 -0
  247. package/dist/heart/frontend-approval-runtime.js +506 -0
  248. package/dist/heart/frontend-journal.js +215 -0
  249. package/dist/heart/frontend-session-service.js +271 -0
  250. package/dist/heart/frontend-socket-client.js +173 -0
  251. package/dist/heart/frontend-socket.js +434 -0
  252. package/dist/heart/frontend-turn-errors.js +20 -0
  253. package/dist/heart/habits/habit-cancel.js +810 -0
  254. package/dist/heart/habits/habit-lifecycle.js +1327 -0
  255. package/dist/heart/habits/habit-migration.js +194 -0
  256. package/dist/heart/habits/habit-parser.js +278 -0
  257. package/dist/heart/habits/habit-runtime-state.js +93 -0
  258. package/dist/heart/habits/habit-scheduler.js +653 -0
  259. package/dist/heart/habits/habit-session-summary.js +318 -0
  260. package/dist/heart/habits/habit-session.js +636 -0
  261. package/dist/heart/{daemon → hatch}/hatch-flow.js +55 -62
  262. package/dist/heart/{daemon → hatch}/hatch-specialist.js +6 -8
  263. package/dist/heart/{daemon → hatch}/specialist-prompt.js +12 -9
  264. package/dist/heart/{daemon → hatch}/specialist-tools.js +53 -18
  265. package/dist/heart/identity.js +239 -59
  266. package/dist/heart/kept-notes.js +289 -0
  267. package/dist/heart/kicks.js +1 -1
  268. package/dist/heart/machine-identity.js +175 -0
  269. package/dist/heart/mail-import-discovery.js +356 -0
  270. package/dist/heart/mailbox/mailbox-http-hooks.js +99 -0
  271. package/dist/heart/mailbox/mailbox-http-response.js +7 -0
  272. package/dist/heart/mailbox/mailbox-http-routes.js +406 -0
  273. package/dist/heart/mailbox/mailbox-http-static.js +103 -0
  274. package/dist/heart/mailbox/mailbox-http-transport.js +272 -0
  275. package/dist/heart/mailbox/mailbox-http.js +99 -0
  276. package/dist/heart/mailbox/mailbox-read.js +41 -0
  277. package/dist/heart/mailbox/mailbox-supervision.js +131 -0
  278. package/dist/heart/mailbox/mailbox-types.js +27 -0
  279. package/dist/heart/mailbox/mailbox-view.js +197 -0
  280. package/dist/heart/mailbox/readers/agent-machine.js +428 -0
  281. package/dist/heart/mailbox/readers/continuity-readers.js +329 -0
  282. package/dist/heart/mailbox/readers/mail.js +375 -0
  283. package/dist/heart/mailbox/readers/runtime-readers.js +823 -0
  284. package/dist/heart/mailbox/readers/sessions.js +231 -0
  285. package/dist/heart/mailbox/readers/shared.js +111 -0
  286. package/dist/heart/mcp/mcp-server.js +760 -0
  287. package/dist/heart/migrate-config.js +100 -0
  288. package/dist/heart/model-capabilities.js +83 -16
  289. package/dist/heart/orientation-frame.js +376 -0
  290. package/dist/heart/platform.js +81 -0
  291. package/dist/heart/primitives.js +1 -1
  292. package/dist/heart/private-runtime/decision-renderer.js +158 -0
  293. package/dist/heart/private-runtime/index.js +18 -0
  294. package/dist/heart/private-runtime/ledger.js +221 -0
  295. package/dist/heart/private-runtime/policy.js +260 -0
  296. package/dist/heart/private-runtime/types.js +2 -0
  297. package/dist/heart/progress-story.js +1 -1
  298. package/dist/heart/provider-attempt.js +137 -0
  299. package/dist/heart/provider-binding-resolver.js +273 -0
  300. package/dist/heart/provider-credentials.js +449 -0
  301. package/dist/heart/provider-failover.js +311 -0
  302. package/dist/heart/provider-models.js +96 -0
  303. package/dist/heart/provider-ping.js +266 -0
  304. package/dist/heart/provider-readiness-cache.js +119 -0
  305. package/dist/heart/provider-visibility.js +188 -0
  306. package/dist/heart/providers/anthropic-token.js +131 -0
  307. package/dist/heart/providers/anthropic.js +248 -67
  308. package/dist/heart/providers/azure.js +23 -11
  309. package/dist/heart/providers/error-classification.js +129 -0
  310. package/dist/heart/providers/github-copilot.js +38 -39
  311. package/dist/heart/providers/minimax-vlm.js +189 -0
  312. package/dist/heart/providers/minimax.js +26 -8
  313. package/dist/heart/providers/openai-codex-token.js +349 -0
  314. package/dist/heart/providers/openai-codex.js +55 -40
  315. package/dist/heart/providers/openai-compatible.js +177 -0
  316. package/dist/heart/reach-channel.js +85 -0
  317. package/dist/heart/reply-style.js +52 -0
  318. package/dist/heart/run-ledger.js +247 -0
  319. package/dist/heart/runtime-capability-check.js +170 -0
  320. package/dist/heart/runtime-credentials.js +608 -0
  321. package/dist/heart/runtime-cwd.js +87 -0
  322. package/dist/heart/sense-truth.js +24 -4
  323. package/dist/heart/session-activity.js +94 -24
  324. package/dist/heart/session-events.js +1521 -0
  325. package/dist/heart/session-playback-cli-main.js +5 -0
  326. package/dist/heart/session-playback-cli.js +36 -0
  327. package/dist/heart/session-playback.js +231 -0
  328. package/dist/heart/session-redaction-repair-cli-main.js +31 -0
  329. package/dist/heart/session-redaction-repair.js +552 -0
  330. package/dist/heart/session-stats-cli-main.js +5 -0
  331. package/dist/heart/session-stats.js +183 -0
  332. package/dist/heart/session-transcript.js +133 -0
  333. package/dist/heart/shelf-claims.js +78 -0
  334. package/dist/heart/shepherd/custody-types.js +2 -0
  335. package/dist/heart/shepherd/mandates.js +352 -0
  336. package/dist/heart/shepherd/operation-commands.js +19 -0
  337. package/dist/heart/shepherd/operations.js +135 -0
  338. package/dist/heart/shepherd/supervision.js +114 -0
  339. package/dist/heart/shepherd/types.js +2 -0
  340. package/dist/heart/shepherd-config.js +123 -0
  341. package/dist/heart/source-grounding.js +313 -0
  342. package/dist/heart/start-of-turn-packet.js +372 -0
  343. package/dist/heart/steward-policy.js +656 -0
  344. package/dist/heart/streaming.js +631 -207
  345. package/dist/heart/structured-output.js +196 -0
  346. package/dist/heart/sync-classification.js +176 -0
  347. package/dist/heart/sync-health.js +152 -0
  348. package/dist/heart/sync.js +726 -0
  349. package/dist/heart/target-resolution.js +11 -7
  350. package/dist/heart/tempo.js +93 -0
  351. package/dist/heart/temporal-view.js +41 -0
  352. package/dist/heart/timeouts.js +101 -0
  353. package/dist/heart/tool-activity-callbacks.js +59 -0
  354. package/dist/heart/tool-approval.js +382 -0
  355. package/dist/heart/tool-description.js +155 -0
  356. package/dist/heart/tool-friction.js +55 -0
  357. package/dist/heart/tool-loop.js +206 -0
  358. package/dist/heart/turn-context.js +496 -0
  359. package/dist/heart/turn-execution-lease.js +30 -0
  360. package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +6 -5
  361. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +3 -2
  362. package/dist/heart/versioning/ouro-path-installer.js +432 -0
  363. package/dist/heart/versioning/ouro-recovery-launcher.js +76 -0
  364. package/dist/heart/versioning/ouro-version-manager.js +432 -0
  365. package/dist/heart/versioning/release-sources.js +173 -0
  366. package/dist/heart/versioning/self-update-defaults.js +294 -0
  367. package/dist/heart/versioning/self-update.js +196 -0
  368. package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
  369. package/dist/heart/{daemon → versioning}/update-checker.js +6 -1
  370. package/dist/heart/versioning/update-hooks.js +154 -0
  371. package/dist/heart/versioning/update-state.js +126 -0
  372. package/dist/heart/versioning/updater-launchd.js +150 -0
  373. package/dist/heart/versioning/version-intent.js +114 -0
  374. package/dist/heart/work-card.js +380 -0
  375. package/dist/mailbox-ui/assets/index-Dbska1tZ.js +1 -0
  376. package/dist/mailbox-ui/assets/index-DsiKaPyH.css +1 -0
  377. package/dist/mailbox-ui/assets/vendor-CcN1XpQ9.js +61 -0
  378. package/dist/mailbox-ui/index.html +16 -0
  379. package/dist/mailroom/attention.js +167 -0
  380. package/dist/mailroom/autonomy.js +209 -0
  381. package/dist/mailroom/blob-store.js +889 -0
  382. package/dist/mailroom/body-cache.js +61 -0
  383. package/dist/mailroom/cache-sync-cli.js +58 -0
  384. package/dist/mailroom/core.js +803 -0
  385. package/dist/mailroom/entry.js +160 -0
  386. package/dist/mailroom/file-store.js +568 -0
  387. package/dist/mailroom/hosted-cache-sync.js +384 -0
  388. package/dist/mailroom/mbox-import.js +393 -0
  389. package/dist/mailroom/migration.js +164 -0
  390. package/dist/mailroom/outbound.js +380 -0
  391. package/dist/mailroom/policy.js +263 -0
  392. package/dist/mailroom/reader.js +292 -0
  393. package/dist/mailroom/search-cache.js +513 -0
  394. package/dist/mailroom/search-relevance.js +319 -0
  395. package/dist/mailroom/smtp-ingress.js +176 -0
  396. package/dist/mailroom/source-state.js +176 -0
  397. package/dist/mailroom/thread.js +109 -0
  398. package/dist/mailroom/travel-extract.js +89 -0
  399. package/dist/mind/bundle-manifest.js +24 -2
  400. package/dist/mind/context.js +328 -139
  401. package/dist/mind/desk-section.js +362 -0
  402. package/dist/mind/diary-integrity.js +60 -0
  403. package/dist/mind/{memory.js → diary.js} +68 -77
  404. package/dist/mind/embedding-provider.js +60 -0
  405. package/dist/mind/file-state.js +179 -0
  406. package/dist/mind/friend-link-codes.js +209 -0
  407. package/dist/mind/friend-merge.js +336 -0
  408. package/dist/mind/{associative-recall.js → note-search.js} +47 -58
  409. package/dist/mind/obligation-steering.js +199 -3
  410. package/dist/mind/outbound-record.js +168 -0
  411. package/dist/mind/pending.js +36 -10
  412. package/dist/mind/prompt-budget.js +479 -0
  413. package/dist/mind/prompt-refresh.js +3 -2
  414. package/dist/mind/prompt.js +1166 -203
  415. package/dist/mind/provenance-trust.js +26 -0
  416. package/dist/mind/record-paths.js +312 -0
  417. package/dist/mind/scrutiny.js +173 -0
  418. package/dist/mind/session-transaction.js +622 -0
  419. package/dist/nerves/cli-logging.js +7 -1
  420. package/dist/nerves/coverage/audit-rules.js +15 -6
  421. package/dist/nerves/coverage/audit.js +28 -2
  422. package/dist/nerves/coverage/cli.js +1 -1
  423. package/dist/nerves/coverage/contract.js +5 -5
  424. package/dist/nerves/coverage/file-completeness.js +143 -5
  425. package/dist/nerves/coverage/run-artifacts.js +55 -35
  426. package/dist/nerves/coverage/source-scanner.js +4 -4
  427. package/dist/nerves/event-buffer.js +111 -0
  428. package/dist/nerves/index.js +293 -9
  429. package/dist/nerves/observation.js +20 -0
  430. package/dist/nerves/redact.js +79 -0
  431. package/dist/nerves/review/cli-main.js +5 -0
  432. package/dist/nerves/review/cli.js +156 -0
  433. package/dist/nerves/review/core.js +159 -0
  434. package/dist/nerves/runtime.js +17 -1
  435. package/dist/repertoire/ado-client.js +15 -56
  436. package/dist/repertoire/ado-semantic.js +16 -10
  437. package/dist/repertoire/api-client.js +97 -0
  438. package/dist/repertoire/bitwarden-store.js +1199 -0
  439. package/dist/repertoire/bundle-templates.js +71 -0
  440. package/dist/repertoire/bw-installer.js +231 -0
  441. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  442. package/dist/repertoire/coding/context-pack.js +331 -0
  443. package/dist/repertoire/coding/feedback.js +146 -34
  444. package/dist/repertoire/coding/index.js +15 -3
  445. package/dist/repertoire/coding/manager.js +159 -10
  446. package/dist/repertoire/coding/spawner.js +40 -11
  447. package/dist/repertoire/coding/tools.js +182 -18
  448. package/dist/repertoire/coding/workbench-client.js +272 -0
  449. package/dist/repertoire/coding/workbench-manager.js +346 -0
  450. package/dist/repertoire/commerce-errors.js +109 -0
  451. package/dist/repertoire/commerce-self-test.js +156 -0
  452. package/dist/repertoire/credential-access.js +183 -0
  453. package/dist/repertoire/desk/classifier.js +362 -0
  454. package/dist/repertoire/duffel-client.js +185 -0
  455. package/dist/repertoire/github-client.js +14 -55
  456. package/dist/repertoire/graph-client.js +11 -52
  457. package/dist/repertoire/guardrails.js +971 -20
  458. package/dist/repertoire/house-sweep.js +471 -0
  459. package/dist/repertoire/mcp-client.js +58 -5
  460. package/dist/repertoire/mcp-manager.js +427 -139
  461. package/dist/repertoire/mcp-tools.js +132 -0
  462. package/dist/repertoire/mcp-write-guard.js +46 -0
  463. package/dist/repertoire/plugin-mcp.js +175 -0
  464. package/dist/repertoire/plugins.js +253 -0
  465. package/dist/repertoire/relationship-authorization.js +322 -0
  466. package/dist/repertoire/sanctuary-container-policy.js +50 -0
  467. package/dist/repertoire/shell-sessions.js +134 -0
  468. package/dist/repertoire/skills.js +48 -4
  469. package/dist/repertoire/stripe-client.js +131 -0
  470. package/dist/repertoire/tool-arguments.js +182 -0
  471. package/dist/repertoire/tool-results.js +29 -0
  472. package/dist/repertoire/tools-a2a.js +699 -0
  473. package/dist/repertoire/tools-attachments.js +322 -0
  474. package/dist/repertoire/tools-awaiting.js +897 -0
  475. package/dist/repertoire/tools-base.js +83 -1168
  476. package/dist/repertoire/tools-bluebubbles.js +2 -0
  477. package/dist/repertoire/tools-bridge.js +144 -0
  478. package/dist/repertoire/tools-bundle.js +995 -0
  479. package/dist/repertoire/tools-commerce.js +253 -0
  480. package/dist/repertoire/tools-config.js +186 -0
  481. package/dist/repertoire/tools-continuity.js +588 -0
  482. package/dist/repertoire/tools-credential.js +383 -0
  483. package/dist/repertoire/tools-escalation.js +84 -0
  484. package/dist/repertoire/tools-evolution.js +527 -0
  485. package/dist/repertoire/tools-files.js +344 -0
  486. package/dist/repertoire/tools-flight.js +290 -0
  487. package/dist/repertoire/tools-flow.js +119 -0
  488. package/dist/repertoire/tools-friend-link.js +57 -0
  489. package/dist/repertoire/tools-github.js +3 -8
  490. package/dist/repertoire/tools-habits.js +103 -0
  491. package/dist/repertoire/tools-house-care.js +174 -0
  492. package/dist/repertoire/tools-mail.js +1828 -0
  493. package/dist/repertoire/tools-notes.js +501 -0
  494. package/dist/repertoire/tools-obligations.js +182 -0
  495. package/dist/repertoire/tools-orientation.js +31 -0
  496. package/dist/repertoire/tools-record.js +482 -0
  497. package/dist/repertoire/tools-rsvp.js +139 -0
  498. package/dist/repertoire/tools-runtime.js +238 -0
  499. package/dist/repertoire/tools-sanctuary-host.js +202 -0
  500. package/dist/repertoire/tools-session.js +1141 -0
  501. package/dist/repertoire/tools-shell.js +154 -0
  502. package/dist/repertoire/tools-shepherd-guidance.js +171 -0
  503. package/dist/repertoire/tools-shepherd.js +325 -0
  504. package/dist/repertoire/tools-steward-policy.js +147 -0
  505. package/dist/repertoire/tools-stripe.js +224 -0
  506. package/dist/repertoire/tools-surface.js +369 -0
  507. package/dist/repertoire/tools-teams.js +50 -39
  508. package/dist/repertoire/tools-telegram-contacts.js +37 -0
  509. package/dist/repertoire/tools-travel.js +125 -0
  510. package/dist/repertoire/tools-trip.js +982 -0
  511. package/dist/repertoire/tools-unraid.js +556 -0
  512. package/dist/repertoire/tools-user-profile.js +146 -0
  513. package/dist/repertoire/tools-vault.js +40 -0
  514. package/dist/repertoire/tools-voice-local.js +198 -0
  515. package/dist/repertoire/tools-voice.js +145 -0
  516. package/dist/repertoire/tools.js +518 -156
  517. package/dist/repertoire/travel-api-client.js +360 -0
  518. package/dist/repertoire/unraid-client.js +155 -0
  519. package/dist/repertoire/unraid-restart.js +339 -0
  520. package/dist/repertoire/user-profile.js +131 -0
  521. package/dist/repertoire/vault-setup.js +246 -0
  522. package/dist/repertoire/vault-unlock.js +686 -0
  523. package/dist/rsvp/aisleplanner-client.js +354 -0
  524. package/dist/rsvp/cli.js +936 -0
  525. package/dist/rsvp/config.js +327 -0
  526. package/dist/rsvp/cutover.js +453 -0
  527. package/dist/rsvp/diagnostics.js +232 -0
  528. package/dist/rsvp/diff-renderer.js +176 -0
  529. package/dist/rsvp/habit-policy.js +110 -0
  530. package/dist/rsvp/habit-stage.js +123 -0
  531. package/dist/rsvp/incident-bundle.js +97 -0
  532. package/dist/rsvp/migration.js +204 -0
  533. package/dist/rsvp/native-habit-runner.js +630 -0
  534. package/dist/rsvp/outbound-state.js +628 -0
  535. package/dist/rsvp/query.js +118 -0
  536. package/dist/rsvp/replay.js +233 -0
  537. package/dist/rsvp/snapshot.js +232 -0
  538. package/dist/rsvp/spend-ledger.js +175 -0
  539. package/dist/scripts/claude-code-hook.js +41 -0
  540. package/dist/scripts/claude-code-stop-hook.js +47 -0
  541. package/dist/senses/a2a-entry.js +132 -0
  542. package/dist/senses/acp-server.js +386 -0
  543. package/dist/senses/attention-queue.js +186 -0
  544. package/dist/senses/await-turn-message.js +65 -0
  545. package/dist/senses/bluebubbles/active-turns.js +216 -0
  546. package/dist/senses/bluebubbles/attachment-cache.js +53 -0
  547. package/dist/senses/bluebubbles/attachment-download.js +137 -0
  548. package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +393 -43
  549. package/dist/senses/bluebubbles/context-packet.js +207 -0
  550. package/dist/senses/bluebubbles/context-smoke.js +144 -0
  551. package/dist/senses/bluebubbles/entry.js +90 -0
  552. package/dist/senses/bluebubbles/host-account-verifier.js +88 -0
  553. package/dist/senses/{bluebubbles-inbound-log.js → bluebubbles/inbound-log.js} +21 -3
  554. package/dist/senses/bluebubbles/index.js +4054 -0
  555. package/dist/senses/bluebubbles/latest-turn.js +311 -0
  556. package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -71
  557. package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +159 -28
  558. package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +24 -13
  559. package/dist/senses/bluebubbles/outbound-state.js +335 -0
  560. package/dist/senses/bluebubbles/processed-log.js +133 -0
  561. package/dist/senses/bluebubbles/reaction-policy.js +47 -0
  562. package/dist/senses/bluebubbles/replay.js +284 -0
  563. package/dist/senses/{bluebubbles-runtime-state.js → bluebubbles/runtime-state.js} +30 -2
  564. package/dist/senses/bluebubbles/semantic-receipts.js +1986 -0
  565. package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
  566. package/dist/senses/bluebubbles/webhook-registration.js +257 -0
  567. package/dist/senses/bluebubbles-meta-guard.js +39 -0
  568. package/dist/senses/cli/bracketed-paste.js +82 -0
  569. package/dist/senses/cli/image-paste.js +287 -0
  570. package/dist/senses/cli/image-ref-navigation.js +75 -0
  571. package/dist/senses/cli/ink-app.js +156 -0
  572. package/dist/senses/cli/inline-diff.js +64 -0
  573. package/dist/senses/cli/input-keys.js +174 -0
  574. package/dist/senses/cli/kill-ring.js +86 -0
  575. package/dist/senses/cli/message-list.js +51 -0
  576. package/dist/senses/cli/ouro-tui.js +607 -0
  577. package/dist/senses/cli/spinner-imperative.js +135 -0
  578. package/dist/senses/cli/spinner.js +101 -0
  579. package/dist/senses/cli/status-line.js +60 -0
  580. package/dist/senses/cli/streaming-markdown.js +526 -0
  581. package/dist/senses/cli/tool-display.js +85 -0
  582. package/dist/senses/cli/tool-render.js +85 -0
  583. package/dist/senses/cli/tui-store.js +241 -0
  584. package/dist/senses/cli/virtual-list.js +35 -0
  585. package/dist/senses/cli-entry.js +60 -8
  586. package/dist/senses/cli.js +570 -309
  587. package/dist/senses/commands.js +66 -3
  588. package/dist/senses/context-packet-ledger.js +371 -0
  589. package/dist/senses/context-packets.js +154 -0
  590. package/dist/senses/habit-lifecycle-guard.js +24 -0
  591. package/dist/senses/habit-turn-message.js +160 -0
  592. package/dist/senses/ingress-evidence.js +2 -0
  593. package/dist/senses/mail-entry.js +66 -0
  594. package/dist/senses/mail.js +524 -0
  595. package/dist/senses/pipeline.js +1158 -204
  596. package/dist/senses/private-runtime-worker.js +857 -0
  597. package/dist/senses/private-runtime.js +1627 -0
  598. package/dist/senses/proactive-content-guard.js +51 -0
  599. package/dist/senses/root-host-approval-port.js +345 -0
  600. package/dist/senses/root-host-approval-runtime.js +393 -0
  601. package/dist/senses/sanctuary-authority-resident.js +102 -0
  602. package/dist/senses/sanctuary-download-credit-presentation.js +31 -0
  603. package/dist/senses/sanctuary-full-visibility-contract.js +302 -0
  604. package/dist/senses/sanctuary-grounding.js +118 -0
  605. package/dist/senses/sanctuary-health-acceptance-probe-entry.js +12 -0
  606. package/dist/senses/sanctuary-health-acceptance-probe.js +786 -0
  607. package/dist/senses/sanctuary-health-runner.js +106 -0
  608. package/dist/senses/sanctuary-health.js +442 -0
  609. package/dist/senses/sanctuary-house.js +97 -0
  610. package/dist/senses/sanctuary-install-state-contract.js +169 -0
  611. package/dist/senses/sanctuary-interactive-control.js +410 -0
  612. package/dist/senses/sanctuary-media-catalog-contract.js +268 -0
  613. package/dist/senses/sanctuary-media-optimization.js +517 -0
  614. package/dist/senses/sanctuary-runtime.js +390 -0
  615. package/dist/senses/sanctuary-sab.js +85 -0
  616. package/dist/senses/sanctuary-storage-optimization-contract.js +32 -0
  617. package/dist/senses/shared-turn.js +832 -0
  618. package/dist/senses/shepherd/agent-presentation.js +88 -0
  619. package/dist/senses/shepherd/attention-page.js +166 -0
  620. package/dist/senses/shepherd/attention-sidebar.js +76 -0
  621. package/dist/senses/shepherd/attention.js +295 -0
  622. package/dist/senses/shepherd/checkin-config.js +76 -0
  623. package/dist/senses/shepherd/checkin-store.js +386 -0
  624. package/dist/senses/shepherd/client.js +196 -0
  625. package/dist/senses/shepherd/cmux.js +483 -0
  626. package/dist/senses/shepherd/composer.js +336 -0
  627. package/dist/senses/shepherd/copilot-app.js +648 -0
  628. package/dist/senses/shepherd/feedback-reply.js +108 -0
  629. package/dist/senses/shepherd/feedback-source.js +20 -0
  630. package/dist/senses/shepherd/feedback-store.js +331 -0
  631. package/dist/senses/shepherd/feedback-submit.js +182 -0
  632. package/dist/senses/shepherd/feedback-worker.js +208 -0
  633. package/dist/senses/shepherd/guidance.js +273 -0
  634. package/dist/senses/shepherd/herdr.js +427 -0
  635. package/dist/senses/shepherd/host.js +17 -0
  636. package/dist/senses/shepherd/human-decision-guard.js +29 -0
  637. package/dist/senses/shepherd/judge-evaluation-command.js +183 -0
  638. package/dist/senses/shepherd/judge-evaluation-entry.js +13 -0
  639. package/dist/senses/shepherd/judge-evaluation.js +155 -0
  640. package/dist/senses/shepherd/judge.js +210 -0
  641. package/dist/senses/shepherd/notice-outbox.js +262 -0
  642. package/dist/senses/shepherd/observation-publisher.js +320 -0
  643. package/dist/senses/shepherd/operations-defaults.js +86 -0
  644. package/dist/senses/shepherd/operations.js +159 -0
  645. package/dist/senses/shepherd/owner-notice.js +489 -0
  646. package/dist/senses/shepherd/prompt-state.js +60 -0
  647. package/dist/senses/shepherd/recovery.js +543 -0
  648. package/dist/senses/shepherd/redact.js +66 -0
  649. package/dist/senses/shepherd/returns.js +79 -0
  650. package/dist/senses/shepherd/sense.js +849 -0
  651. package/dist/senses/shepherd/shepherd-judge-corpus-v1.json +170 -0
  652. package/dist/senses/shepherd/surface.js +123 -0
  653. package/dist/senses/shepherd/target-binding.js +13 -0
  654. package/dist/senses/shepherd/terminal-process.js +147 -0
  655. package/dist/senses/shepherd-entry.js +98 -0
  656. package/dist/senses/surface-tool.js +109 -0
  657. package/dist/senses/teams-entry.js +68 -8
  658. package/dist/senses/teams-image.js +119 -0
  659. package/dist/senses/teams.js +998 -337
  660. package/dist/senses/telegram-admission.js +718 -0
  661. package/dist/senses/telegram-approval-runtime.js +793 -0
  662. package/dist/senses/telegram-attachments.js +186 -0
  663. package/dist/senses/telegram-audit-ledger.js +220 -0
  664. package/dist/senses/telegram-authority-transport.js +231 -0
  665. package/dist/senses/telegram-client.js +1755 -0
  666. package/dist/senses/telegram-effect-adapter.js +904 -0
  667. package/dist/senses/telegram-entry.js +83 -0
  668. package/dist/senses/telegram-handoff.js +154 -0
  669. package/dist/senses/telegram.js +2474 -0
  670. package/dist/senses/trust-gate.js +104 -9
  671. package/dist/senses/voice/audio-playback.js +237 -0
  672. package/dist/senses/voice/audio-routing.js +119 -0
  673. package/dist/senses/voice/call-auth.js +250 -0
  674. package/dist/senses/voice/elevenlabs.js +202 -0
  675. package/dist/senses/voice/floor-control.js +431 -0
  676. package/dist/senses/voice/floor-controller.js +115 -0
  677. package/dist/senses/voice/golden-path.js +116 -0
  678. package/dist/senses/voice/index.js +29 -0
  679. package/dist/senses/voice/local-audio-control.js +344 -0
  680. package/dist/senses/voice/local-audio-devices.js +516 -0
  681. package/dist/senses/voice/local-audio-disclosure.js +174 -0
  682. package/dist/senses/voice/local-audio-identity.js +103 -0
  683. package/dist/senses/voice/local-audio-join.js +325 -0
  684. package/dist/senses/voice/local-audio-mute.js +199 -0
  685. package/dist/senses/voice/local-audio-notify.js +124 -0
  686. package/dist/senses/voice/local-audio-prompts.js +116 -0
  687. package/dist/senses/voice/local-audio-transport.js +427 -0
  688. package/dist/senses/voice/local-audio.js +281 -0
  689. package/dist/senses/voice/local-media-stream.js +145 -0
  690. package/dist/senses/voice/meeting.js +113 -0
  691. package/dist/senses/voice/outbound.js +190 -0
  692. package/dist/senses/voice/phone.js +33 -0
  693. package/dist/senses/voice/playback.js +139 -0
  694. package/dist/senses/voice/process-guards.js +51 -0
  695. package/dist/senses/voice/realtime-eval.js +496 -0
  696. package/dist/senses/voice/realtime-tool-schema.js +195 -0
  697. package/dist/senses/voice/realtime-trace.js +531 -0
  698. package/dist/senses/voice/transcript.js +70 -0
  699. package/dist/senses/voice/turn.js +191 -0
  700. package/dist/senses/voice/twilio-phone-runtime.js +836 -0
  701. package/dist/senses/voice/twilio-phone.js +5454 -0
  702. package/dist/senses/voice/types.js +2 -0
  703. package/dist/senses/voice/whisper.js +161 -0
  704. package/dist/senses/voice-entry.js +83 -0
  705. package/dist/senses/voice-realtime-eval-command.js +99 -0
  706. package/dist/senses/voice-realtime-eval-entry.js +21 -0
  707. package/dist/senses/voice-twilio-entry.js +87 -0
  708. package/dist/trips/core.js +138 -0
  709. package/dist/trips/store.js +265 -0
  710. package/dist/util/frontmatter.js +69 -0
  711. package/npm-shrinkwrap.json +8342 -0
  712. package/package.json +76 -13
  713. package/skills/agent-commerce.md +113 -0
  714. package/skills/browser-navigation.md +117 -0
  715. package/skills/commerce-setup-guide.md +116 -0
  716. package/skills/commerce-setup.md +84 -0
  717. package/skills/configure-dev-tools.md +99 -0
  718. package/skills/travel-planning.md +138 -0
  719. package/dist/heart/daemon/auth-flow.js +0 -430
  720. package/dist/heart/daemon/ouro-path-installer.js +0 -260
  721. package/dist/heart/daemon/ouro-version-manager.js +0 -171
  722. package/dist/heart/daemon/update-hooks.js +0 -138
  723. package/dist/heart/obligations.js +0 -191
  724. package/dist/heart/safe-workspace.js +0 -381
  725. package/dist/heart/session-recall.js +0 -116
  726. package/dist/mind/friends/channel.js +0 -84
  727. package/dist/mind/friends/group-context.js +0 -144
  728. package/dist/mind/friends/resolver.js +0 -84
  729. package/dist/mind/friends/store-file.js +0 -190
  730. package/dist/mind/friends/store.js +0 -4
  731. package/dist/mind/friends/tokens.js +0 -26
  732. package/dist/mind/friends/trust-explanation.js +0 -74
  733. package/dist/mind/friends/types.js +0 -29
  734. package/dist/repertoire/tasks/board.js +0 -134
  735. package/dist/repertoire/tasks/index.js +0 -224
  736. package/dist/repertoire/tasks/lifecycle.js +0 -80
  737. package/dist/repertoire/tasks/middleware.js +0 -65
  738. package/dist/repertoire/tasks/parser.js +0 -173
  739. package/dist/repertoire/tasks/scanner.js +0 -132
  740. package/dist/repertoire/tasks/transitions.js +0 -144
  741. package/dist/senses/bluebubbles-entry.js +0 -13
  742. package/dist/senses/bluebubbles.js +0 -1181
  743. package/dist/senses/debug-activity.js +0 -154
  744. package/dist/senses/inner-dialog-worker.js +0 -90
  745. package/dist/senses/inner-dialog.js +0 -525
  746. package/subagents/README.md +0 -7
  747. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  748. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  749. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  750. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  751. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  752. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  753. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  754. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  755. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  756. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  757. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  758. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  759. /package/dist/{repertoire/tasks/types.js → heart/attachments/sources/adapter.js} +0 -0
  760. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  761. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  762. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
  763. /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
@@ -0,0 +1,727 @@
1
+ #!/usr/local/bin/node
2
+
3
+ import { execFileSync } from "node:child_process"
4
+ import { createHash } from "node:crypto"
5
+ import { closeSync, constants, fchmodSync, fchownSync, fsyncSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, unlinkSync, writeFileSync } from "node:fs"
6
+ import { basename, dirname, resolve } from "node:path"
7
+ import { fileURLToPath } from "node:url"
8
+
9
+ import xmlValidator from "./docker-man-template-xml.cjs"
10
+
11
+ const TARGET_PATH = "/boot/config/plugins/dockerMan/templates-user/my-ouro-butler.xml"
12
+ const JOURNAL_PATH = "/boot/config/custom/ouro-butler/docker-man-template-transaction.json"
13
+ const TEMPLATE_URL = "https://raw.githubusercontent.com/ourostack/ouroboros/main/deploy/unraid/sanctuary.xml"
14
+ const ICON = "https://raw.githubusercontent.com/ourostack/ouroboros/main/assets/ouroboros.png"
15
+ const COMMUNITY_APPS_ENTRY_PATH = "/usr/local/emhttp/plugins/community.applications/include/exec.php"
16
+ const COMMUNITY_APPS_HELPER_PATH = "/usr/local/emhttp/plugins/community.applications/include/previous_apps_helpers.php"
17
+ const IMAGE_ID = /^sha256:[0-9a-f]{64}$/u
18
+ const CONTAINER_ID = /^[0-9a-f]{64}$/u
19
+ const VERSION_TAG = /^ghcr\.io\/ourostack\/ouroboros-butler:[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?$/u
20
+ const JELLYFIN_STATES = new Set(["created", "running", "paused", "restarting", "removing", "exited", "dead"])
21
+ const JELLYFIN_FORMAT = '{"name":{{json .Name}},"containerId":{{json .Id}},"imageId":{{json .Image}},"state":{{json .State.Status}},"restartCount":{{json .RestartCount}}}'
22
+ const AUTHORITY_INSTALL_STEPS = ["freeze-resident", "stage-authority", "verify-token-rotation", "transfer-cursor", "remove-resident-token", "start-gateway", "configure-resident", "start-resident", "verify-install"]
23
+ const AUTHORITY_ROLLBACK_STEPS = ["freeze-resident", "retire-registrations", "reconcile-executions", "stop-gateway", "end-epoch", "restore-token-cursor", "restore-resident", "verify-rollback"]
24
+
25
+ function object(value, label) {
26
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${label} must be an object`)
27
+ return value
28
+ }
29
+
30
+ function exactKeys(value, expected, label) {
31
+ const keys = Object.keys(value).sort()
32
+ if (JSON.stringify(keys) !== JSON.stringify([...expected].sort())) throw new Error(`${label} has unexpected fields`)
33
+ }
34
+
35
+ // A household without Jellyfin checkpoints null: the container must stay absent.
36
+ function validateJellyfinState(raw, label = "Jellyfin checkpoint") {
37
+ if (raw === null) return null
38
+ const value = object(raw, label)
39
+ exactKeys(value, ["containerId", "imageId", "state", "restartCount"], label)
40
+ if (!CONTAINER_ID.test(value.containerId) || !IMAGE_ID.test(value.imageId) || !JELLYFIN_STATES.has(value.state) || !Number.isSafeInteger(value.restartCount) || value.restartCount < 0) throw new Error(`${label} is invalid`)
41
+ return { containerId: value.containerId, imageId: value.imageId, state: value.state, restartCount: value.restartCount }
42
+ }
43
+
44
+ export function inspectCurrentJellyfinState() {
45
+ let present
46
+ try {
47
+ present = execFileSync("/usr/bin/docker", ["container", "ls", "--all", "--quiet", "--filter", "name=^/jellyfin$"], { encoding: "utf8", maxBuffer: 65_536, stdio: ["ignore", "pipe", "ignore"] }).trim() !== ""
48
+ } catch {
49
+ throw new Error("Jellyfin container inspection failed")
50
+ }
51
+ if (!present) return null
52
+ let inspected
53
+ try {
54
+ inspected = JSON.parse(execFileSync("/usr/bin/docker", ["container", "inspect", "--format", JELLYFIN_FORMAT, "jellyfin"], { encoding: "utf8", maxBuffer: 65_536, stdio: ["ignore", "pipe", "ignore"] }).trim())
55
+ } catch {
56
+ throw new Error("Jellyfin container inspection failed")
57
+ }
58
+ const value = object(inspected, "Jellyfin container inspection")
59
+ exactKeys(value, ["name", "containerId", "imageId", "state", "restartCount"], "Jellyfin container inspection")
60
+ if (value.name !== "/jellyfin") throw new Error("Jellyfin container identity is invalid")
61
+ return validateJellyfinState({ containerId: value.containerId, imageId: value.imageId, state: value.state, restartCount: value.restartCount })
62
+ }
63
+
64
+ function assertJellyfinUnchanged(expected) {
65
+ const current = inspectCurrentJellyfinState()
66
+ if (JSON.stringify(current) !== JSON.stringify(expected)) throw new Error("Jellyfin changed during the Butler transaction")
67
+ }
68
+
69
+ // A fresh install has no predecessor image; its rollback image is null.
70
+ function validRollbackImage(value) {
71
+ return value === null || IMAGE_ID.test(value)
72
+ }
73
+
74
+ function digest(bytes) {
75
+ return `sha256:${createHash("sha256").update(bytes).digest("hex")}`
76
+ }
77
+
78
+ function recordDigest(record) {
79
+ const { digest: _ignored, ...unsigned } = record
80
+ return digest(Buffer.from(JSON.stringify(unsigned)))
81
+ }
82
+
83
+ function directChildren(document, name) {
84
+ return document.children.filter((child) => child.name === name)
85
+ }
86
+
87
+ function singleTextChild(document, name) {
88
+ const children = directChildren(document, name)
89
+ if (children.length !== 1 || children[0].form !== "text" || Object.keys(children[0].attributes).length !== 0) throw new Error(`template ${name} must appear exactly once as plain text`)
90
+ return children[0].text
91
+ }
92
+
93
+ function templateIdentity(bytes, expectedRepository) {
94
+ const document = xmlValidator.parseDockerManTemplateXml(bytes)
95
+ if (!document) throw new Error("canonical DockerMan XML structure is invalid")
96
+ const identity = {
97
+ name: singleTextChild(document, "Name"),
98
+ repository: singleTextChild(document, "Repository"),
99
+ templateUrl: singleTextChild(document, "TemplateURL"),
100
+ icon: singleTextChild(document, "Icon"),
101
+ }
102
+ if (identity.name !== "ouro-butler" || !/^[a-zA-Z0-9][a-zA-Z0-9_.-]+$/u.test(identity.name)) throw new Error("template technical name is invalid")
103
+ if (identity.repository !== expectedRepository || !VERSION_TAG.test(identity.repository)) throw new Error("template version reference is invalid")
104
+ if (identity.templateUrl !== TEMPLATE_URL || identity.icon !== ICON) throw new Error("template identity is invalid")
105
+ const webUi = directChildren(document, "WebUI")
106
+ if (webUi.length !== 1 || webUi[0].form !== "empty" || Object.keys(webUi[0].attributes).length !== 0) throw new Error("template WebUI must be exactly empty")
107
+ return identity
108
+ }
109
+
110
+ function validateDirectory(path, expectedUid, expectedGid) {
111
+ const metadata = lstatSync(path)
112
+ if (!metadata.isDirectory() || metadata.isSymbolicLink() || metadata.uid !== expectedUid || metadata.gid !== expectedGid || (metadata.mode & 0o777) !== 0o700 || realpathSync(path) !== resolve(path)) throw new Error("transaction parent is unsafe")
113
+ }
114
+
115
+ function validateRegularFile(path, expectedUid, expectedGid, label) {
116
+ const metadata = lstatSync(path)
117
+ if (!metadata.isFile() || metadata.isSymbolicLink() || metadata.uid !== expectedUid || metadata.gid !== expectedGid || (metadata.mode & 0o777) !== 0o600) throw new Error(`${label} metadata is invalid`)
118
+ return metadata
119
+ }
120
+
121
+ function metadataIfPresent(path) {
122
+ try {
123
+ return lstatSync(path)
124
+ } catch (error) {
125
+ if (error?.code === "ENOENT") return null
126
+ throw error
127
+ }
128
+ }
129
+
130
+ export function isSafeRecoveryTemporaryMetadata(metadata, expectedUid, expectedGid) {
131
+ return Boolean(metadata
132
+ && typeof metadata.isFile === "function"
133
+ && typeof metadata.isSymbolicLink === "function"
134
+ && metadata.isFile()
135
+ && !metadata.isSymbolicLink()
136
+ && metadata.uid === expectedUid
137
+ && metadata.gid === expectedGid
138
+ && (metadata.mode & 0o777) === 0o600)
139
+ }
140
+
141
+ function paths(options = {}) {
142
+ const targetPath = options.targetPath ?? TARGET_PATH
143
+ const journalPath = options.journalPath ?? JOURNAL_PATH
144
+ const expectedUid = options.expectedUid ?? 0
145
+ const expectedGid = options.expectedGid ?? 0
146
+ return {
147
+ targetPath,
148
+ journalPath,
149
+ templateParent: dirname(targetPath),
150
+ journalParent: dirname(journalPath),
151
+ targetTemporary: `${dirname(targetPath)}/.${basename(targetPath)}.ouro-transaction.tmp`,
152
+ journalTemporary: `${dirname(journalPath)}/.${basename(journalPath)}.ouro-transaction.tmp`,
153
+ expectedUid,
154
+ expectedGid,
155
+ }
156
+ }
157
+
158
+ function validateTemplateParent(state, allowTemporaryResidue = false) {
159
+ validateDirectory(state.templateParent, state.expectedUid, state.expectedGid)
160
+ const matches = readdirSync(state.templateParent).filter((entry) => entry.toLowerCase() === basename(state.targetPath).toLowerCase())
161
+ if (matches.length > 1 || (matches.length === 1 && matches[0] !== basename(state.targetPath))) throw new Error("case-folded DockerMan template name is ambiguous")
162
+ if (!allowTemporaryResidue && metadataIfPresent(state.targetTemporary)) throw new Error("template transaction temporary state is ambiguous")
163
+ }
164
+
165
+ function validateJournalParent(state, allowTemporaryResidue = false) {
166
+ validateDirectory(state.journalParent, state.expectedUid, state.expectedGid)
167
+ if (!allowTemporaryResidue && metadataIfPresent(state.journalTemporary)) throw new Error("template transaction temporary state is ambiguous")
168
+ }
169
+
170
+ function validateParents(state, allowTemporaryResidue = false) {
171
+ validateTemplateParent(state, allowTemporaryResidue)
172
+ validateJournalParent(state, allowTemporaryResidue)
173
+ }
174
+
175
+ function ensureJournalParent(state) {
176
+ if (!metadataIfPresent(state.journalParent)) {
177
+ const parent = dirname(state.journalParent)
178
+ validateDirectory(parent, state.expectedUid, state.expectedGid)
179
+ mkdirSync(state.journalParent, { mode: 0o700 })
180
+ syncDirectory(parent)
181
+ }
182
+ validateJournalParent(state)
183
+ }
184
+
185
+ function syncDirectory(path) {
186
+ const fd = openSync(path, constants.O_RDONLY)
187
+ try { fsyncSync(fd) } finally { closeSync(fd) }
188
+ }
189
+
190
+ function atomicWrite(path, temporaryPath, bytes, metadata) {
191
+ const fd = openSync(temporaryPath, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY | constants.O_NOFOLLOW, metadata.mode)
192
+ try {
193
+ writeFileSync(fd, bytes)
194
+ fchmodSync(fd, metadata.mode)
195
+ fchownSync(fd, metadata.uid, metadata.gid)
196
+ fsyncSync(fd)
197
+ } finally {
198
+ closeSync(fd)
199
+ }
200
+ renameSync(temporaryPath, path)
201
+ syncDirectory(dirname(path))
202
+ }
203
+
204
+ function deleteDurably(path) {
205
+ unlinkSync(path)
206
+ syncDirectory(dirname(path))
207
+ }
208
+
209
+ function decodeCanonicalBase64(value) {
210
+ if (typeof value !== "string" || Buffer.from(value, "base64").toString("base64") !== value) throw new Error("template transaction journal prior bytes are invalid")
211
+ return Buffer.from(value, "base64")
212
+ }
213
+
214
+ function validateJournalRecord(raw, state) {
215
+ const record = object(raw, "template transaction journal")
216
+ exactKeys(record, ["schemaVersion", "state", "target", "priorTemplate", "priorTemplateDigest", "targetTemplateDigest", "canonicalVersionTag", "reviewedManifestDigest", "rollbackImageId", "targetImageId", "jellyfin", "digest", ...(Object.hasOwn(record, "authority") ? ["authority"] : [])], "template transaction journal")
217
+ if (Object.hasOwn(record, "authority")) validateAuthorityHandoff(record.authority, record)
218
+ if (record.schemaVersion !== 1 || !["rollback", "committing"].includes(record.state)) throw new Error("template transaction journal state is invalid")
219
+ const target = object(record.target, "template transaction journal target")
220
+ exactKeys(target, ["path", "name", "templateUrl", "icon"], "template transaction journal target")
221
+ if (target.path !== state.targetPath || target.name !== "ouro-butler" || target.templateUrl !== TEMPLATE_URL || target.icon !== ICON) throw new Error("template transaction journal target is invalid")
222
+ const prior = object(record.priorTemplate, "template transaction journal prior template")
223
+ exactKeys(prior, ["present", "bytesBase64", "metadata"], "template transaction journal prior template")
224
+ if (typeof prior.present !== "boolean") throw new Error("template transaction journal prior presence is invalid")
225
+ let priorBytes = null
226
+ if (prior.present) {
227
+ priorBytes = decodeCanonicalBase64(prior.bytesBase64)
228
+ const metadata = object(prior.metadata, "template transaction journal prior metadata")
229
+ exactKeys(metadata, ["uid", "gid", "mode"], "template transaction journal prior metadata")
230
+ if (metadata.uid !== state.expectedUid || metadata.gid !== state.expectedGid || metadata.mode !== 0o600 || record.priorTemplateDigest !== digest(priorBytes)) throw new Error("template transaction journal prior metadata is invalid")
231
+ } else if (prior.bytesBase64 !== null || prior.metadata !== null || record.priorTemplateDigest !== null) {
232
+ throw new Error("template transaction journal absent prior state is invalid")
233
+ }
234
+ if (!VERSION_TAG.test(record.canonicalVersionTag) || !IMAGE_ID.test(record.reviewedManifestDigest) || !validRollbackImage(record.rollbackImageId) || !IMAGE_ID.test(record.targetImageId) || record.rollbackImageId === record.targetImageId || !IMAGE_ID.test(record.targetTemplateDigest)) throw new Error("template transaction journal release identity is invalid")
235
+ const jellyfin = validateJellyfinState(record.jellyfin, "template transaction journal Jellyfin checkpoint")
236
+ if (!IMAGE_ID.test(record.digest) || record.digest !== recordDigest(record)) throw new Error("template transaction journal digest is invalid")
237
+ return { ...record, jellyfin, priorBytes }
238
+ }
239
+
240
+ function readJournal(state) {
241
+ if (!metadataIfPresent(state.journalPath)) return null
242
+ validateRegularFile(state.journalPath, state.expectedUid, state.expectedGid, "template transaction journal")
243
+ let parsed
244
+ try { parsed = JSON.parse(readFileSync(state.journalPath, "utf8")) } catch { throw new Error("template transaction journal JSON is invalid") }
245
+ return validateJournalRecord(parsed, state)
246
+ }
247
+
248
+ function writeJournal(record, state) {
249
+ const complete = { ...record, digest: recordDigest(record) }
250
+ atomicWrite(state.journalPath, state.journalTemporary, Buffer.from(`${JSON.stringify(complete)}\n`), { uid: state.expectedUid, gid: state.expectedGid, mode: 0o600 })
251
+ return complete
252
+ }
253
+
254
+ function currentTargetDigest(state) {
255
+ if (!metadataIfPresent(state.targetPath)) return null
256
+ validateRegularFile(state.targetPath, state.expectedUid, state.expectedGid, "DockerMan template")
257
+ return digest(readFileSync(state.targetPath))
258
+ }
259
+
260
+ function inspectRecoveryTemporary(state) {
261
+ validateParents(state, true)
262
+ const residues = [state.targetTemporary, state.journalTemporary]
263
+ .map((path) => ({ path, metadata: metadataIfPresent(path) }))
264
+ .filter((entry) => entry.metadata)
265
+ if (residues.length === 0) return null
266
+ if (residues.length !== 1) throw new Error("template transaction recovery temporary state is ambiguous")
267
+ const residue = residues[0]
268
+ if (!isSafeRecoveryTemporaryMetadata(residue.metadata, state.expectedUid, state.expectedGid)) throw new Error("template transaction recovery temporary file is unsafe")
269
+ const journalMetadata = metadataIfPresent(state.journalPath)
270
+ if (journalMetadata) readJournal(state)
271
+ if (residue.path === state.targetTemporary) {
272
+ if (!journalMetadata) throw new Error("template transaction recovery target temporary file is orphaned")
273
+ const journal = readJournal(state)
274
+ const installedDigest = currentTargetDigest(state)
275
+ const allowedDigests = [journal.targetTemplateDigest, journal.priorTemplateDigest]
276
+ if (!(allowedDigests.includes(installedDigest) || (!journal.priorTemplate.present && installedDigest === null))) throw new Error("template transaction recovery target state is ambiguous")
277
+ }
278
+ return residue
279
+ }
280
+
281
+ function cleanupRecoveryTemporary(state) {
282
+ const residue = inspectRecoveryTemporary(state)
283
+ if (!residue) return
284
+ deleteDurably(residue.path)
285
+ }
286
+
287
+ function publicJournalRecord(result) {
288
+ if (!result) return null
289
+ const { priorBytes: _ignored, ...record } = result
290
+ return record
291
+ }
292
+
293
+ export function inspectDockerManTemplateTransactionRecoveryIdentity(options = {}) {
294
+ const state = paths(options)
295
+ inspectRecoveryTemporary(state)
296
+ const current = readJournal(state)
297
+ const jellyfin = inspectCurrentJellyfinState()
298
+ if (current && JSON.stringify(jellyfin) !== JSON.stringify(current.jellyfin)) throw new Error("Jellyfin changed during the Butler transaction")
299
+ return publicJournalRecord(current)
300
+ }
301
+
302
+ export function inspectDockerManTemplateTransactionForRecovery(options = {}) {
303
+ const state = paths(options)
304
+ const expected = inspectDockerManTemplateTransactionRecoveryIdentity(options)
305
+ cleanupRecoveryTemporary(state)
306
+ const current = inspectDockerManTemplateTransaction(options)
307
+ if (JSON.stringify(current) !== JSON.stringify(expected)) throw new Error("template transaction changed during recovery")
308
+ return current
309
+ }
310
+
311
+ export function inspectDockerManTemplateTransaction(options = {}) {
312
+ const state = paths(options)
313
+ validateParents(state)
314
+ return publicJournalRecord(readJournal(state))
315
+ }
316
+
317
+ function validateTemplateSource(input, state) {
318
+ validateTemplateParent(state)
319
+ if (metadataIfPresent(state.targetPath)) validateRegularFile(state.targetPath, state.expectedUid, state.expectedGid, "DockerMan template")
320
+ validateDirectory(dirname(input.sourceTemplatePath), state.expectedUid, state.expectedGid)
321
+ validateRegularFile(input.sourceTemplatePath, state.expectedUid, state.expectedGid, "source template")
322
+ if (!VERSION_TAG.test(input.canonicalVersionTag) || !IMAGE_ID.test(input.reviewedManifestDigest) || !validRollbackImage(input.rollbackImageId) || !IMAGE_ID.test(input.targetImageId) || input.rollbackImageId === input.targetImageId) throw new Error("reviewed release identity is invalid")
323
+ const sourceBytes = readFileSync(input.sourceTemplatePath)
324
+ const identity = templateIdentity(sourceBytes, input.canonicalVersionTag)
325
+ return { sourceBytes, identity }
326
+ }
327
+
328
+ export function prepareDockerManTemplateTransaction(input, options = {}) {
329
+ const state = paths(options)
330
+ const { sourceBytes, identity } = validateTemplateSource(input, state)
331
+ const jellyfin = inspectCurrentJellyfinState()
332
+ ensureJournalParent(state)
333
+ const existing = readJournal(state)
334
+ if (existing) {
335
+ if (existing.canonicalVersionTag !== input.canonicalVersionTag || existing.reviewedManifestDigest !== input.reviewedManifestDigest || existing.rollbackImageId !== input.rollbackImageId || existing.targetImageId !== input.targetImageId || existing.targetTemplateDigest !== digest(sourceBytes) || currentTargetDigest(state) !== existing.targetTemplateDigest || JSON.stringify(existing.jellyfin) !== JSON.stringify(jellyfin)) throw new Error("pending template transaction does not match the reviewed release")
336
+ const { priorBytes: _ignored, ...record } = existing
337
+ return record
338
+ }
339
+ let priorTemplate
340
+ let priorTemplateDigest = null
341
+ if (metadataIfPresent(state.targetPath)) {
342
+ const metadata = validateRegularFile(state.targetPath, state.expectedUid, state.expectedGid, "DockerMan template")
343
+ const bytes = readFileSync(state.targetPath)
344
+ priorTemplateDigest = digest(bytes)
345
+ priorTemplate = { present: true, bytesBase64: bytes.toString("base64"), metadata: { uid: metadata.uid, gid: metadata.gid, mode: metadata.mode & 0o777 } }
346
+ } else {
347
+ priorTemplate = { present: false, bytesBase64: null, metadata: null }
348
+ }
349
+ const unsigned = {
350
+ schemaVersion: 1,
351
+ state: "rollback",
352
+ target: { path: state.targetPath, name: identity.name, templateUrl: identity.templateUrl, icon: identity.icon },
353
+ priorTemplate,
354
+ priorTemplateDigest,
355
+ targetTemplateDigest: digest(sourceBytes),
356
+ canonicalVersionTag: input.canonicalVersionTag,
357
+ reviewedManifestDigest: input.reviewedManifestDigest,
358
+ rollbackImageId: input.rollbackImageId,
359
+ targetImageId: input.targetImageId,
360
+ jellyfin,
361
+ }
362
+ const record = writeJournal(unsigned, state)
363
+ options.checkpoint?.("after-template-journal")
364
+ atomicWrite(state.targetPath, state.targetTemporary, sourceBytes, { uid: state.expectedUid, gid: state.expectedGid, mode: 0o600 })
365
+ options.checkpoint?.("after-template-replacement")
366
+ return record
367
+ }
368
+
369
+ export function markDockerManTemplateTransactionCommitting(options = {}) {
370
+ const state = paths(options)
371
+ validateParents(state)
372
+ const current = readJournal(state)
373
+ if (!current) throw new Error("template transaction journal is absent")
374
+ requireActiveAuthority(current, state)
375
+ if (currentTargetDigest(state) !== current.targetTemplateDigest) throw new Error("installed DockerMan template does not match the transaction")
376
+ assertJellyfinUnchanged(current.jellyfin)
377
+ if (current.state === "committing") {
378
+ const { priorBytes: _ignored, ...record } = current
379
+ return record
380
+ }
381
+ const { priorBytes: _ignored, digest: _digest, ...record } = current
382
+ const committing = writeJournal({ ...record, state: "committing" }, state)
383
+ options.checkpoint?.("after-template-committing")
384
+ return committing
385
+ }
386
+
387
+ export function rollbackDockerManTemplateTransaction(options = {}) {
388
+ const state = paths(options)
389
+ validateParents(state)
390
+ const current = readJournal(state)
391
+ if (!current) return false
392
+ if (current.authority && current.authority.state !== "retired") throw new Error("authority epoch must be retired before template rollback")
393
+ const installedDigest = currentTargetDigest(state)
394
+ assertJellyfinUnchanged(current.jellyfin)
395
+ if (current.priorTemplate.present) {
396
+ if (installedDigest !== current.targetTemplateDigest && installedDigest !== current.priorTemplateDigest) throw new Error("installed DockerMan template cannot be safely restored")
397
+ if (installedDigest !== current.priorTemplateDigest) atomicWrite(state.targetPath, state.targetTemporary, current.priorBytes, current.priorTemplate.metadata)
398
+ } else if (installedDigest !== null) {
399
+ if (installedDigest !== current.targetTemplateDigest) throw new Error("installed DockerMan template cannot be safely removed")
400
+ deleteDurably(state.targetPath)
401
+ }
402
+ options.checkpoint?.("after-template-restore")
403
+ deleteDurably(state.journalPath)
404
+ return true
405
+ }
406
+
407
+ function validateFinalProof(proof, record, state) {
408
+ const root = object(proof, "final install proof")
409
+ const container = object(root.container, "final container proof")
410
+ const labels = object(container.labels, "final container labels")
411
+ if (container.name !== "/ouro-butler" || container.imageId !== record.targetImageId || container.imageReference !== record.canonicalVersionTag || container.running !== true || container.healthy !== true || container.autostart !== true) throw new Error("final container proof is invalid")
412
+ if (labels["net.unraid.docker.managed"] !== "dockerman" || labels["net.unraid.docker.icon"] !== record.target.icon || Object.prototype.hasOwnProperty.call(labels, "net.unraid.docker.webui")) throw new Error("final container install labels are invalid")
413
+ const bundle = object(root.bundle, "final bundle proof")
414
+ const bundleData = object(bundle.data, "final bundle proof data")
415
+ if (bundle.ok !== true || bundleData.parity !== "exact" || bundleData.journalState !== "absent" || bundleData.ready !== true) throw new Error("final bundle proof is invalid")
416
+ const dockerMan = object(root.dockerMan, "final DockerMan proof")
417
+ if (dockerMan.templatePath !== state.targetPath || dockerMan.name !== "ouro-butler" || dockerMan.repository !== record.canonicalVersionTag || dockerMan.templateUrl !== record.target.templateUrl || dockerMan.icon !== record.target.icon) throw new Error("final DockerMan proof is invalid")
418
+ const communityApps = object(root.communityApps, "final Community Apps proof")
419
+ const inlineImplementation = communityApps.stateModel === "previous-apps-inline-v1" && communityApps.entryPath === COMMUNITY_APPS_ENTRY_PATH && communityApps.entryFunction === "previous_apps" && communityApps.implementationPath === COMMUNITY_APPS_ENTRY_PATH && communityApps.implementationSymbol === "previous_apps"
420
+ const helperImplementation = communityApps.stateModel === "previous-apps-helper-v1" && communityApps.entryPath === COMMUNITY_APPS_ENTRY_PATH && communityApps.entryFunction === "previous_apps" && communityApps.implementationPath === COMMUNITY_APPS_HELPER_PATH && communityApps.implementationSymbol === "PreviousAppsHelpers::collectDockerApplications"
421
+ const recognizedImplementation = inlineImplementation || helperImplementation
422
+ if (communityApps.installed !== true || communityApps.name !== "ouro-butler" || communityApps.repository !== record.canonicalVersionTag || communityApps.templateUrl !== record.target.templateUrl || !recognizedImplementation) throw new Error("final Community Apps proof is invalid")
423
+ const jellyfin = validateJellyfinState(root.jellyfin, "final Jellyfin proof")
424
+ if (JSON.stringify(jellyfin) !== JSON.stringify(record.jellyfin)) throw new Error("final Jellyfin proof is invalid")
425
+ }
426
+
427
+ export function commitDockerManTemplateTransaction(proof, options = {}) {
428
+ const state = paths(options)
429
+ validateParents(state)
430
+ const current = readJournal(state)
431
+ if (!current) return false
432
+ requireActiveAuthority(current, state)
433
+ if (current.state !== "committing" || currentTargetDigest(state) !== current.targetTemplateDigest) throw new Error("template transaction is not ready to commit")
434
+ validateFinalProof(proof, current, state)
435
+ assertJellyfinUnchanged(current.jellyfin)
436
+ deleteDurably(state.journalPath)
437
+ return true
438
+ }
439
+
440
+ function requireActiveAuthority(current, state) {
441
+ const document = xmlValidator.parseDockerManTemplateXml(readFileSync(state.targetPath))
442
+ const gateway = document?.children.some((child) => child.name === "Config" && child.attributes.Target === "/run/ouro-authority")
443
+ if ((gateway || current.authority) && current.authority?.state !== "active") throw new Error("authority handoff is not verified")
444
+ }
445
+
446
+ function validateAuthorityPlan(raw) {
447
+ const plan = object(raw, "authority handoff plan")
448
+ exactKeys(plan, ["schemaVersion", "epochId", "packageDigest", "publicKeyDigest", "botId", "ownerUserId", "ownerChatId", "predecessorContract", "targetContract"], "authority handoff plan")
449
+ if (plan.schemaVersion !== 1 || !/^[A-Za-z0-9_-]{1,128}$/u.test(plan.epochId) || !IMAGE_ID.test(plan.packageDigest) || (plan.publicKeyDigest !== null && !IMAGE_ID.test(plan.publicKeyDigest))
450
+ || !["botId", "ownerUserId", "ownerChatId"].every((key) => typeof plan[key] === "string" && /^[1-9][0-9]*$/u.test(plan[key]))
451
+ || plan.ownerUserId !== plan.ownerChatId || !["canonical-pre-gateway", "none"].includes(plan.predecessorContract) || plan.targetContract !== "canonical-gateway") throw new Error("authority handoff plan is invalid")
452
+ return plan
453
+ }
454
+
455
+ function authorityEffectIdentity(record, effect) {
456
+ return digest(Buffer.from(JSON.stringify([record.targetImageId, record.rollbackImageId, record.reviewedManifestDigest, record.authority.plan, effect.direction, effect.step, effect.beforeDigest, effect.afterDigest])))
457
+ }
458
+
459
+ function validateAuthorityHandoff(raw, record) {
460
+ const handoff = object(raw, "authority handoff")
461
+ exactKeys(handoff, ["plan", "state", "completed", "rollbackCompleted", "pending", "cancelled"], "authority handoff")
462
+ validateAuthorityPlan(handoff.plan)
463
+ // Only a fresh install (no predecessor) may lack a rollback image, and it must lack one.
464
+ if ((handoff.plan.predecessorContract === "none") !== (record.rollbackImageId === null)) throw new Error("authority handoff plan does not match the rollback image")
465
+ if (!["installing", "active", "retiring", "retired"].includes(handoff.state) || !Array.isArray(handoff.completed) || !Array.isArray(handoff.rollbackCompleted) || !Array.isArray(handoff.cancelled)) throw new Error("authority handoff state is invalid")
466
+ const validateEffect = (effect, direction, step) => {
467
+ object(effect, "authority effect")
468
+ exactKeys(effect, ["direction", "step", "effectId", "beforeDigest", "afterDigest"], "authority effect")
469
+ if (effect.direction !== direction || effect.step !== step || !IMAGE_ID.test(effect.beforeDigest) || !IMAGE_ID.test(effect.afterDigest) || effect.beforeDigest === effect.afterDigest
470
+ || effect.effectId !== authorityEffectIdentity(record, effect)) throw new Error("authority effect identity is invalid")
471
+ }
472
+ for (const [direction, entries, steps] of [["install", handoff.completed, AUTHORITY_INSTALL_STEPS], ["rollback", handoff.rollbackCompleted, AUTHORITY_ROLLBACK_STEPS]]) {
473
+ if (entries.length > steps.length) throw new Error("authority effect order is invalid")
474
+ entries.forEach((effect, index) => validateEffect(effect, direction, steps[index]))
475
+ }
476
+ const rollback = handoff.state === "retiring" || handoff.state === "retired"
477
+ if (handoff.cancelled.length > 1 || (handoff.cancelled.length !== 0 && !rollback)) throw new Error("authority cancelled effect is invalid")
478
+ for (const effect of handoff.cancelled) validateEffect(effect, "install", AUTHORITY_INSTALL_STEPS[handoff.completed.length])
479
+ if ((!rollback && handoff.rollbackCompleted.length !== 0) || (handoff.state === "active" && handoff.completed.length !== AUTHORITY_INSTALL_STEPS.length)
480
+ || (handoff.state === "retired" && handoff.rollbackCompleted.length !== AUTHORITY_ROLLBACK_STEPS.length)) throw new Error("authority terminal handoff is invalid")
481
+ if (handoff.pending !== null) {
482
+ if (handoff.state === "active" || handoff.state === "retired") throw new Error("authority terminal handoff has a pending effect")
483
+ const steps = rollback ? AUTHORITY_ROLLBACK_STEPS : AUTHORITY_INSTALL_STEPS
484
+ const completed = rollback ? handoff.rollbackCompleted : handoff.completed
485
+ validateEffect(handoff.pending, rollback ? "rollback" : "install", steps[completed.length])
486
+ }
487
+ return handoff
488
+ }
489
+
490
+ function writeAuthorityHandoff(record, authority, state) {
491
+ const { priorBytes: _bytes, digest: _digest, ...unsigned } = record
492
+ const next = { ...unsigned, authority }
493
+ validateAuthorityHandoff(authority, next)
494
+ return writeJournal(next, state)
495
+ }
496
+
497
+ export function beginDockerManAuthorityHandoff(plan, options = {}) {
498
+ validateAuthorityPlan(plan)
499
+ const state = paths(options)
500
+ validateParents(state)
501
+ const current = readJournal(state)
502
+ if (!current || (!current.authority && current.state !== "rollback")) throw new Error("authority handoff requires the prepared deployment transaction")
503
+ assertJellyfinUnchanged(current.jellyfin)
504
+ if (current.authority) {
505
+ if (JSON.stringify(current.authority.plan) !== JSON.stringify(plan)) throw new Error("authority handoff plan changed")
506
+ return current.authority
507
+ }
508
+ return writeAuthorityHandoff(current, { plan, state: "installing", completed: [], rollbackCompleted: [], cancelled: [], pending: null }, state).authority
509
+ }
510
+
511
+ async function withAuthorityLease(options, work) {
512
+ const state = paths(options)
513
+ validateParents(state)
514
+ const withLease = options.withLease ?? (await import("../../dist/mind/session-transaction.js")).withSessionTurnLease
515
+ return withLease(state.journalPath, () => work(state), { timeoutMs: 0, confinementRoot: state.journalParent })
516
+ }
517
+
518
+ export async function runDockerManAuthorityEffect(name, effect, options = {}) {
519
+ return withAuthorityLease(options, (state) => runAuthorityEffectLocked(name, effect, state))
520
+ }
521
+
522
+ export async function beginDockerManAuthorityRollback(readback, options = {}) {
523
+ return withAuthorityLease(options, (state) => beginAuthorityRollbackLocked(readback, state))
524
+ }
525
+
526
+ async function beginAuthorityRollbackLocked(readback, state) {
527
+ const current = readJournal(state)
528
+ if (!current?.authority) throw new Error("authority handoff is absent")
529
+ const handoff = current.authority
530
+ if (handoff.state === "retired" || handoff.state === "retiring") return handoff
531
+ const pending = handoff.pending
532
+ const observed = pending ? await readback() : null
533
+ if (pending && observed !== pending.beforeDigest && observed !== pending.afterDigest) throw new Error("authority pending effect is ambiguous")
534
+ if (readJournal(state).digest !== current.digest) throw new Error("authority transaction changed during recovery")
535
+ assertJellyfinUnchanged(current.jellyfin)
536
+ return writeAuthorityHandoff(current, {
537
+ ...handoff, state: "retiring", pending: null,
538
+ completed: pending && observed === pending.afterDigest ? [...handoff.completed, pending] : handoff.completed,
539
+ cancelled: pending && observed === pending.beforeDigest ? [...handoff.cancelled, pending] : handoff.cancelled,
540
+ }, state).authority
541
+ }
542
+
543
+ async function runAuthorityEffectLocked(name, effect, state) {
544
+ let current = readJournal(state)
545
+ if (!current?.authority) throw new Error("authority handoff is absent")
546
+ assertJellyfinUnchanged(current.jellyfin)
547
+ const direction = name.startsWith("rollback:") ? "rollback" : "install"
548
+ const step = direction === "rollback" ? name.slice("rollback:".length) : name
549
+ const steps = direction === "rollback" ? AUTHORITY_ROLLBACK_STEPS : AUTHORITY_INSTALL_STEPS
550
+ if (!steps.includes(step)) throw new Error("authority handoff step is invalid")
551
+ if (!IMAGE_ID.test(effect.beforeDigest) || !IMAGE_ID.test(effect.afterDigest) || effect.beforeDigest === effect.afterDigest) throw new Error("authority effect identity is invalid")
552
+ let handoff = current.authority
553
+ if (direction === "install" && ["retiring", "retired"].includes(handoff.state)) throw new Error("authority handoff is retiring")
554
+ const expected = { direction, step, beforeDigest: effect.beforeDigest, afterDigest: effect.afterDigest }
555
+ const intent = { ...expected, effectId: authorityEffectIdentity(current, expected) }
556
+ const completedKey = direction === "install" ? "completed" : "rollbackCompleted"
557
+ const completed = handoff[completedKey]
558
+ const prior = completed.find((entry) => entry.step === step)
559
+ if (prior) {
560
+ if (prior.effectId !== intent.effectId || await effect.readback() !== prior.afterDigest) throw new Error("authority completed effect changed")
561
+ return prior
562
+ }
563
+ if (steps[completed.length] !== step) throw new Error("authority handoff effect order is invalid")
564
+ if (handoff.pending && handoff.pending.effectId !== intent.effectId) throw new Error("authority pending effect changed; recovery is required before rollback")
565
+ if (!handoff.pending) {
566
+ handoff = { ...handoff, state: direction === "rollback" ? "retiring" : "installing", pending: intent }
567
+ current = writeAuthorityHandoff(current, handoff, state)
568
+ }
569
+ const observed = await effect.readback()
570
+ if (observed !== intent.beforeDigest && observed !== intent.afterDigest) throw new Error("authority handoff effect is ambiguous")
571
+ if (observed === intent.beforeDigest) await effect.apply(intent.effectId)
572
+ if (await effect.readback() !== intent.afterDigest) throw new Error("authority handoff effect readback failed")
573
+ const latest = readJournal(state)
574
+ if (latest.digest !== current.digest) throw new Error("authority transaction changed during effect")
575
+ assertJellyfinUnchanged(latest.jellyfin)
576
+ const entries = [...completed, intent]
577
+ handoff = { ...handoff, [completedKey]: entries, pending: null, state: entries.length === steps.length ? direction === "install" ? "active" : "retired" : direction === "install" ? "installing" : "retiring" }
578
+ writeAuthorityHandoff(latest, handoff, state)
579
+ return intent
580
+ }
581
+
582
+ export function verifyDockerManTemplateTransactionJellyfin(options = {}) {
583
+ const state = paths(options)
584
+ validateParents(state)
585
+ const current = readJournal(state)
586
+ if (!current) throw new Error("template transaction journal is absent")
587
+ assertJellyfinUnchanged(current.jellyfin)
588
+ return true
589
+ }
590
+
591
+ export function decideDockerManTemplateRecovery(record, evidence) {
592
+ const state = record?.state
593
+ const bundle = evidence?.bundleJournalState
594
+ const production = evidence?.production
595
+ if (!["rollback", "committing"].includes(state) || !["absent", "rollback", "committing"].includes(bundle) || !["rollback-exact", "target-exact-committing", "target-exact-ready", "adoption-source-exact", "adoption-target-exact-ready", "adoption-evidence-exact-stopped"].includes(production)) throw new Error("template transaction recovery evidence is invalid")
596
+ if (bundle === "rollback" && production === "rollback-exact") return "rollback-both"
597
+ if (state === "committing" && bundle === "committing" && production === "target-exact-committing") {
598
+ const inspection = evidence?.inspection
599
+ if (inspection?.ok !== true || inspection?.data?.parity !== "exact" || inspection?.data?.journalState !== "committing" || inspection?.data?.ready !== false) throw new Error("template transaction recovery inspection is invalid")
600
+ return "finish-bundle-commit"
601
+ }
602
+ if (state === "committing" && bundle === "absent" && production === "target-exact-ready") {
603
+ const inspection = evidence?.inspection
604
+ if (inspection?.ok !== true || inspection?.data?.parity !== "exact" || inspection?.data?.journalState !== "absent" || inspection?.data?.ready !== true) throw new Error("template transaction recovery inspection is invalid")
605
+ return "finish-template-commit"
606
+ }
607
+ if (bundle === "absent" && production === "rollback-exact") return "restore-prior-template"
608
+ if (state === "rollback" && bundle === "absent" && production === "adoption-source-exact") return "restore-prior-template"
609
+ if (state === "rollback" && bundle === "absent" && production === "adoption-target-exact-ready") {
610
+ const inspection = evidence?.inspection
611
+ if (inspection?.ok !== true || inspection?.data?.parity !== "exact" || inspection?.data?.journalState !== "absent" || inspection?.data?.ready !== true) throw new Error("template transaction recovery inspection is invalid")
612
+ return "roll-forward-adoption"
613
+ }
614
+ if (state === "rollback" && bundle === "absent" && production === "adoption-evidence-exact-stopped") return "quarantine-adoption"
615
+ throw new Error("template and bundle transaction topology is incompatible")
616
+ }
617
+
618
+ function parseArguments(argv) {
619
+ const [operation, ...rest] = argv
620
+ const values = new Map()
621
+ for (let index = 0; index < rest.length; index += 2) {
622
+ const flag = rest[index]
623
+ const value = rest[index + 1]
624
+ if (!flag?.startsWith("--") || !value || values.has(flag)) throw new Error("template transaction arguments are invalid")
625
+ values.set(flag, value)
626
+ }
627
+ return { operation, values }
628
+ }
629
+
630
+ function readRootJson(path, label) {
631
+ validateRegularFile(path, 0, 0, label)
632
+ try { return JSON.parse(readFileSync(path, "utf8")) } catch { throw new Error(`${label} JSON is invalid`) }
633
+ }
634
+
635
+ export function runDockerManTemplateTransactionCli(argv, options = {}, write = (text) => process.stdout.write(text)) {
636
+ const { operation, values } = parseArguments(argv)
637
+ if (operation?.startsWith("authority-") && values.size === 0) {
638
+ return runRootLifecycle(operation, options).then((result) => write(`${JSON.stringify(result)}\n`))
639
+ }
640
+ let result
641
+ if (operation === "prepare" && values.size === 5) {
642
+ result = prepareDockerManTemplateTransaction({
643
+ sourceTemplatePath: values.get("--source-template"),
644
+ canonicalVersionTag: values.get("--version-tag"),
645
+ reviewedManifestDigest: values.get("--manifest-digest"),
646
+ rollbackImageId: values.get("--rollback-image-id") === "none" ? null : values.get("--rollback-image-id"),
647
+ targetImageId: values.get("--target-image-id"),
648
+ }, options)
649
+ } else if (operation === "mark-committing" && values.size === 0) {
650
+ ensureJournalParent(paths(options))
651
+ result = markDockerManTemplateTransactionCommitting(options)
652
+ } else if (operation === "rollback" && values.size === 0) {
653
+ ensureJournalParent(paths(options))
654
+ result = { rolledBack: rollbackDockerManTemplateTransaction(options) }
655
+ } else if (operation === "commit" && values.size === 1 && values.get("--proof")) {
656
+ ensureJournalParent(paths(options))
657
+ result = { committed: commitDockerManTemplateTransaction(readRootJson(values.get("--proof"), "final install proof"), options) }
658
+ } else if (operation === "status" && values.size === 0) {
659
+ ensureJournalParent(paths(options))
660
+ result = inspectDockerManTemplateTransaction(options)
661
+ } else if (operation === "recover-status" && values.size === 0) {
662
+ const state = paths(options)
663
+ if (!metadataIfPresent(state.journalParent)) {
664
+ inspectCurrentJellyfinState()
665
+ ensureJournalParent(state)
666
+ }
667
+ result = inspectDockerManTemplateTransactionForRecovery(options)
668
+ } else if (operation === "recovery-identity" && values.size === 0) {
669
+ const state = paths(options)
670
+ if (!metadataIfPresent(state.journalParent)) {
671
+ inspectCurrentJellyfinState()
672
+ result = null
673
+ } else {
674
+ result = inspectDockerManTemplateTransactionRecoveryIdentity(options)
675
+ }
676
+ } else if (operation === "recovery-action" && values.size === 1 && values.get("--evidence")) {
677
+ ensureJournalParent(paths(options))
678
+ const record = inspectDockerManTemplateTransaction(options)
679
+ if (!record) throw new Error("template transaction journal is absent")
680
+ result = { action: decideDockerManTemplateRecovery(record, readRootJson(values.get("--evidence"), "template recovery evidence")) }
681
+ } else if (operation === "verify-jellyfin" && values.size === 0) {
682
+ result = { unchanged: verifyDockerManTemplateTransactionJellyfin(options) }
683
+ } else if (operation === "jellyfin-status" && values.size === 0) {
684
+ result = inspectCurrentJellyfinState()
685
+ } else {
686
+ throw new Error("Usage: docker-man-template-transaction.mjs <prepare|mark-committing|rollback|commit|status|recover-status|recovery-identity|recovery-action|verify-jellyfin|jellyfin-status> [fixed operation arguments]")
687
+ }
688
+ write(`${JSON.stringify(result)}\n`)
689
+ }
690
+
691
+ async function runRootLifecycle(operation, options) {
692
+ const selected = {
693
+ "authority-install": AUTHORITY_INSTALL_STEPS.slice(0, 6),
694
+ "authority-activate": AUTHORITY_INSTALL_STEPS.slice(6),
695
+ "authority-retire": AUTHORITY_ROLLBACK_STEPS.slice(0, 6).map((step) => `rollback:${step}`),
696
+ "authority-restore": AUTHORITY_ROLLBACK_STEPS.slice(6).map((step) => `rollback:${step}`),
697
+ }[operation]
698
+ if (!selected) throw new Error("authority lifecycle operation is invalid")
699
+ return withAuthorityLease(options, async (state) => {
700
+ let record = readJournal(state)
701
+ if (!record) throw new Error("authority lifecycle requires the prepared deployment transaction")
702
+ const RootLifecycle = options.RootLifecycle ?? (await import("../../dist/heart/daemon/sanctuary-authority-root-lifecycle.js")).SanctuaryAuthorityRootLifecycle
703
+ const lifecycle = new RootLifecycle(record, options.rootLifecycleOptions)
704
+ beginDockerManAuthorityHandoff(lifecycle.plan(), options)
705
+ record = readJournal(state)
706
+ if (["authority-install", "authority-activate"].includes(operation) && ["retiring", "retired"].includes(record.authority.state)) throw new Error("authority epoch is retiring or retired")
707
+ if (operation === "authority-activate" && record.authority.completed.length < 6) throw new Error("gateway installation must finish before resident activation")
708
+ if (operation === "authority-restore" && record.authority.rollbackCompleted.length < 6) throw new Error("authority retirement must finish before resident restoration")
709
+ if (operation === "authority-retire") {
710
+ const pending = record.authority.pending
711
+ await beginAuthorityRollbackLocked(pending ? lifecycle.effect(pending.step).readback : null, state)
712
+ }
713
+ for (const name of selected) {
714
+ const rollback = name.startsWith("rollback:")
715
+ const step = rollback ? name.slice("rollback:".length) : name
716
+ const current = readJournal(state).authority
717
+ // Completed historical effects may have intentionally changed at later
718
+ // steps (a frozen resident becomes running). Recheck the next real boundary,
719
+ // never replay earlier physical work merely to reproduce a historical hash.
720
+ if (current[rollback ? "rollbackCompleted" : "completed"].some((entry) => entry.step === step)) continue
721
+ await runAuthorityEffectLocked(name, lifecycle.effect(name), state)
722
+ }
723
+ return readJournal(state).authority
724
+ })
725
+ }
726
+
727
+ if (process.argv[1] === fileURLToPath(import.meta.url)) await runDockerManTemplateTransactionCli(process.argv.slice(2))