@ouro.bot/cli 0.1.0-alpha.75 → 0.1.0-alpha.750

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 (602) 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/changelog.json +4812 -16
  15. package/deploy/unraid/Dockerfile +48 -0
  16. package/deploy/unraid/README.txt +2373 -0
  17. package/deploy/unraid/audit-container-spec.sh +4 -0
  18. package/deploy/unraid/container-runtime.json +1 -0
  19. package/deploy/unraid/migrate-sanctuary-bundle.mjs +21 -0
  20. package/deploy/unraid/ouro-events/bootstrap-spool.sh +92 -0
  21. package/deploy/unraid/ouro-events/emit-event.mjs +310 -0
  22. package/deploy/unraid/ouro-events/emit-usenet-event.sh +54 -0
  23. package/deploy/unraid/ouro-events/install-usenet-guard.sh +435 -0
  24. package/deploy/unraid/ouro-events/usenet-health.sh +178 -0
  25. package/deploy/unraid/sanctuary-acceptance-adapter.sh +37 -0
  26. package/deploy/unraid/sanctuary-acceptance-contract.json +201 -0
  27. package/deploy/unraid/sanctuary-acceptance-harness.sh +23 -0
  28. package/deploy/unraid/sanctuary-deployment-target.mjs +709 -0
  29. package/deploy/unraid/sanctuary-safe-rename.py +24 -0
  30. package/deploy/unraid/sanctuary-unit16-host-broker.mjs +1800 -0
  31. package/deploy/unraid/sanctuary-unit16-run.sh +520 -0
  32. package/deploy/unraid/sanctuary-unit18-target-audit.sh +27 -0
  33. package/deploy/unraid/sanctuary.ouro/agent.json +24 -0
  34. package/deploy/unraid/sanctuary.ouro/arc/README.md +3 -0
  35. package/deploy/unraid/sanctuary.ouro/bundle-meta.json +5 -0
  36. package/deploy/unraid/sanctuary.ouro/habits/sanctuary-health.md +8 -0
  37. package/deploy/unraid/sanctuary.ouro/provider-readiness.json +11 -0
  38. package/deploy/unraid/sanctuary.ouro/psyche/ASPIRATIONS.md +5 -0
  39. package/deploy/unraid/sanctuary.ouro/psyche/IDENTITY.md +5 -0
  40. package/deploy/unraid/sanctuary.ouro/psyche/LORE.md +9 -0
  41. package/deploy/unraid/sanctuary.ouro/psyche/SOUL.md +9 -0
  42. package/deploy/unraid/sanctuary.ouro/psyche/TACIT.md +9 -0
  43. package/deploy/unraid/sanctuary.ouro/state/policy/steward.json +7 -0
  44. package/deploy/unraid/sanctuary.ouro/tool-profiles.json +23 -0
  45. package/deploy/unraid/sanctuary.xml +28 -0
  46. package/dist/a2a/card.js +57 -0
  47. package/dist/a2a/client.js +180 -0
  48. package/dist/a2a/config.js +50 -0
  49. package/dist/a2a/delegation-stores.js +56 -0
  50. package/dist/a2a/did-resolution.js +93 -0
  51. package/dist/a2a/identity.js +170 -0
  52. package/dist/a2a/inbound-share.js +107 -0
  53. package/dist/a2a/mission-result-wire.js +196 -0
  54. package/dist/a2a/onboarding.js +115 -0
  55. package/dist/a2a/pin-store.js +132 -0
  56. package/dist/a2a/seen-ledger.js +120 -0
  57. package/dist/a2a/server.js +604 -0
  58. package/dist/a2a/task-store.js +69 -0
  59. package/dist/a2a/transport.js +45 -0
  60. package/dist/a2a/types.js +3 -0
  61. package/dist/arc/attention-types.js +8 -0
  62. package/dist/arc/cares.js +278 -0
  63. package/dist/arc/episodes.js +118 -0
  64. package/dist/arc/evolution.js +490 -0
  65. package/dist/arc/flight-recorder.js +1034 -0
  66. package/dist/arc/intentions.js +134 -0
  67. package/dist/arc/json-store.js +117 -0
  68. package/dist/arc/obligations.js +386 -0
  69. package/dist/arc/packets.js +336 -0
  70. package/dist/arc/presence.js +185 -0
  71. package/dist/arc/task-lifecycle.js +57 -0
  72. package/dist/commerce/store.js +755 -0
  73. package/dist/commerce/types.js +3 -0
  74. package/dist/heart/active-work.js +820 -28
  75. package/dist/heart/agent-entry.js +190 -3
  76. package/dist/heart/approval-files.js +113 -0
  77. package/dist/heart/approval-store.js +950 -0
  78. package/dist/heart/attachments/image-normalize.js +194 -0
  79. package/dist/heart/attachments/materialize.js +97 -0
  80. package/dist/heart/attachments/originals.js +88 -0
  81. package/dist/heart/attachments/render.js +29 -0
  82. package/dist/heart/attachments/sources/bluebubbles.js +156 -0
  83. package/dist/heart/attachments/sources/cli-local-file.js +78 -0
  84. package/dist/heart/attachments/sources/index.js +16 -0
  85. package/dist/heart/attachments/store.js +103 -0
  86. package/dist/heart/attachments/types.js +93 -0
  87. package/dist/heart/auth/auth-flow.js +495 -0
  88. package/dist/heart/autonomy-budget.js +477 -0
  89. package/dist/heart/awaiting/await-alert.js +148 -0
  90. package/dist/heart/awaiting/await-expiry.js +143 -0
  91. package/dist/heart/awaiting/await-loader.js +91 -0
  92. package/dist/heart/awaiting/await-parser.js +149 -0
  93. package/dist/heart/awaiting/await-runtime-state.js +100 -0
  94. package/dist/heart/awaiting/await-scheduler.js +382 -0
  95. package/dist/heart/background-operations.js +281 -0
  96. package/dist/heart/bridges/manager.js +137 -17
  97. package/dist/heart/bridges/store.js +14 -2
  98. package/dist/heart/bundle-state.js +168 -0
  99. package/dist/heart/commitments.js +68 -15
  100. package/dist/heart/config-registry.js +331 -0
  101. package/dist/heart/config.js +128 -129
  102. package/dist/heart/context-loss-gauntlet.js +383 -0
  103. package/dist/heart/context-loss-sentinel.js +1157 -0
  104. package/dist/heart/core.js +1761 -456
  105. package/dist/heart/cross-chat-delivery.js +9 -22
  106. package/dist/heart/daemon/agent-config-check.js +451 -0
  107. package/dist/heart/daemon/agent-discovery.js +185 -3
  108. package/dist/heart/daemon/agent-service.js +542 -0
  109. package/dist/heart/daemon/agentic-repair.js +547 -0
  110. package/dist/heart/daemon/await-private-wake.js +59 -0
  111. package/dist/heart/daemon/bluebubbles-cli-integration.js +71 -0
  112. package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
  113. package/dist/heart/daemon/bluebubbles-host-protocol.js +433 -0
  114. package/dist/heart/daemon/bluebubbles-host.js +273 -0
  115. package/dist/heart/daemon/boot-sync-probe.js +197 -0
  116. package/dist/heart/daemon/cadence.js +180 -0
  117. package/dist/heart/daemon/cli-defaults.js +832 -0
  118. package/dist/heart/daemon/cli-desk.js +322 -0
  119. package/dist/heart/daemon/cli-exec.js +8692 -0
  120. package/dist/heart/daemon/cli-help.js +710 -0
  121. package/dist/heart/daemon/cli-parse.js +2714 -0
  122. package/dist/heart/daemon/cli-render-doctor.js +57 -0
  123. package/dist/heart/daemon/cli-render.js +777 -0
  124. package/dist/heart/daemon/cli-types.js +8 -0
  125. package/dist/heart/daemon/connect-bay.js +323 -0
  126. package/dist/heart/daemon/container-credential-bootstrap.js +273 -0
  127. package/dist/heart/daemon/container-healthcheck.js +96 -0
  128. package/dist/heart/daemon/container-runtime.js +97 -0
  129. package/dist/heart/daemon/container-spec-auditor-main.js +160 -0
  130. package/dist/heart/daemon/container-spec-auditor.js +178 -0
  131. package/dist/heart/daemon/daemon-bootstrap-startup.js +41 -0
  132. package/dist/heart/daemon/daemon-cli.js +29 -2144
  133. package/dist/heart/daemon/daemon-entry.js +944 -10
  134. package/dist/heart/daemon/daemon-health.js +186 -0
  135. package/dist/heart/daemon/daemon-rollup.js +57 -0
  136. package/dist/heart/daemon/daemon-runtime-sync.js +88 -13
  137. package/dist/heart/daemon/daemon-tombstone.js +236 -0
  138. package/dist/heart/daemon/daemon.js +1830 -88
  139. package/dist/heart/daemon/dns-workflow.js +394 -0
  140. package/dist/heart/daemon/doctor-types.js +8 -0
  141. package/dist/heart/daemon/doctor.js +1755 -0
  142. package/dist/heart/daemon/freshness.js +345 -0
  143. package/dist/heart/daemon/habit-private-wake.js +61 -0
  144. package/dist/heart/daemon/health-monitor.js +142 -11
  145. package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
  146. package/dist/heart/daemon/hooks/bundle-meta.js +135 -1
  147. package/dist/heart/daemon/http-health-probe.js +80 -0
  148. package/dist/heart/daemon/human-command-screens.js +234 -0
  149. package/dist/heart/daemon/human-readiness.js +114 -0
  150. package/dist/heart/daemon/inner-status.js +78 -0
  151. package/dist/heart/daemon/interactive-repair.js +394 -0
  152. package/dist/heart/daemon/launchd.js +37 -8
  153. package/dist/heart/daemon/log-tailer.js +79 -10
  154. package/dist/heart/daemon/logs-prune.js +132 -0
  155. package/dist/heart/daemon/mcp-canary.js +524 -0
  156. package/dist/heart/daemon/message-router.js +45 -5
  157. package/dist/heart/daemon/migrate-to-desk.js +848 -0
  158. package/dist/heart/daemon/os-cron-deps.js +135 -0
  159. package/dist/heart/daemon/os-cron.js +22 -14
  160. package/dist/heart/daemon/ouro-bot-entry.js +4 -2
  161. package/dist/heart/daemon/ouro-entry.js +3 -1
  162. package/dist/heart/daemon/plugin-cli.js +432 -0
  163. package/dist/heart/daemon/process-manager.js +624 -61
  164. package/dist/heart/daemon/provider-discovery.js +137 -0
  165. package/dist/heart/daemon/provider-ping-progress.js +83 -0
  166. package/dist/heart/daemon/prunable-bundle.js +144 -0
  167. package/dist/heart/daemon/pulse.js +511 -0
  168. package/dist/heart/daemon/readiness-repair.js +365 -0
  169. package/dist/heart/daemon/run-hooks.js +2 -0
  170. package/dist/heart/daemon/runtime-logging.js +47 -14
  171. package/dist/heart/daemon/runtime-metadata.js +2 -30
  172. package/dist/heart/daemon/runtime-mode.js +13 -2
  173. package/dist/heart/daemon/safe-mode.js +161 -0
  174. package/dist/heart/daemon/sanctuary-acceptance-adapter.js +2426 -0
  175. package/dist/heart/daemon/sanctuary-acceptance-harness.js +1621 -0
  176. package/dist/heart/daemon/sanctuary-acceptance-marker.js +267 -0
  177. package/dist/heart/daemon/sanctuary-acceptance-scenarios.js +976 -0
  178. package/dist/heart/daemon/sanctuary-bundle-migration.js +398 -0
  179. package/dist/heart/daemon/sanctuary-scheduler-liveness.js +227 -0
  180. package/dist/heart/daemon/sanctuary-scheduler-origin.js +132 -0
  181. package/dist/heart/daemon/sense-manager.js +689 -48
  182. package/dist/heart/daemon/session-id-resolver.js +131 -0
  183. package/dist/heart/daemon/skill-management-installer.js +1 -1
  184. package/dist/heart/daemon/socket-client.js +180 -12
  185. package/dist/heart/daemon/stale-bundle-prune.js +113 -0
  186. package/dist/heart/daemon/startup-tui.js +338 -0
  187. package/dist/heart/daemon/supercronic-supervisor.js +282 -0
  188. package/dist/heart/daemon/task-scheduler.js +117 -39
  189. package/dist/heart/daemon/terminal-ui.js +499 -0
  190. package/dist/heart/daemon/thoughts.js +138 -54
  191. package/dist/heart/daemon/up-progress.js +366 -0
  192. package/dist/heart/daemon/vault-items.js +56 -0
  193. package/dist/heart/delegation.js +1 -4
  194. package/dist/heart/external-events/router.js +1103 -0
  195. package/dist/heart/habits/habit-cancel.js +810 -0
  196. package/dist/heart/habits/habit-lifecycle.js +1327 -0
  197. package/dist/heart/habits/habit-migration.js +194 -0
  198. package/dist/heart/habits/habit-parser.js +278 -0
  199. package/dist/heart/habits/habit-runtime-state.js +93 -0
  200. package/dist/heart/habits/habit-scheduler.js +653 -0
  201. package/dist/heart/habits/habit-session-summary.js +318 -0
  202. package/dist/heart/habits/habit-session.js +636 -0
  203. package/dist/heart/{daemon → hatch}/hatch-flow.js +55 -62
  204. package/dist/heart/{daemon → hatch}/hatch-specialist.js +6 -8
  205. package/dist/heart/{daemon → hatch}/specialist-prompt.js +12 -9
  206. package/dist/heart/{daemon → hatch}/specialist-tools.js +53 -18
  207. package/dist/heart/identity.js +184 -59
  208. package/dist/heart/kept-notes.js +289 -0
  209. package/dist/heart/kicks.js +1 -1
  210. package/dist/heart/machine-identity.js +161 -0
  211. package/dist/heart/mail-import-discovery.js +353 -0
  212. package/dist/heart/mailbox/mailbox-http-hooks.js +98 -0
  213. package/dist/heart/mailbox/mailbox-http-response.js +7 -0
  214. package/dist/heart/mailbox/mailbox-http-routes.js +398 -0
  215. package/dist/heart/mailbox/mailbox-http-static.js +103 -0
  216. package/dist/heart/mailbox/mailbox-http-transport.js +116 -0
  217. package/dist/heart/mailbox/mailbox-http.js +99 -0
  218. package/dist/heart/mailbox/mailbox-read.js +39 -0
  219. package/dist/heart/mailbox/mailbox-types.js +27 -0
  220. package/dist/heart/mailbox/mailbox-view.js +197 -0
  221. package/dist/heart/mailbox/readers/agent-machine.js +428 -0
  222. package/dist/heart/mailbox/readers/continuity-readers.js +329 -0
  223. package/dist/heart/mailbox/readers/mail.js +375 -0
  224. package/dist/heart/mailbox/readers/runtime-readers.js +823 -0
  225. package/dist/heart/mailbox/readers/sessions.js +232 -0
  226. package/dist/heart/mailbox/readers/shared.js +111 -0
  227. package/dist/heart/mcp/mcp-server.js +696 -0
  228. package/dist/heart/migrate-config.js +100 -0
  229. package/dist/heart/model-capabilities.js +11 -0
  230. package/dist/heart/orientation-frame.js +345 -0
  231. package/dist/heart/platform.js +81 -0
  232. package/dist/heart/primitives.js +1 -1
  233. package/dist/heart/private-runtime/decision-renderer.js +156 -0
  234. package/dist/heart/private-runtime/index.js +18 -0
  235. package/dist/heart/private-runtime/ledger.js +218 -0
  236. package/dist/heart/private-runtime/policy.js +214 -0
  237. package/dist/heart/private-runtime/types.js +2 -0
  238. package/dist/heart/progress-story.js +1 -1
  239. package/dist/heart/provider-attempt.js +137 -0
  240. package/dist/heart/provider-binding-resolver.js +273 -0
  241. package/dist/heart/provider-credentials.js +449 -0
  242. package/dist/heart/provider-failover.js +311 -0
  243. package/dist/heart/provider-models.js +96 -0
  244. package/dist/heart/provider-ping.js +265 -0
  245. package/dist/heart/provider-readiness-cache.js +119 -0
  246. package/dist/heart/provider-visibility.js +188 -0
  247. package/dist/heart/providers/anthropic-token.js +131 -0
  248. package/dist/heart/providers/anthropic.js +173 -60
  249. package/dist/heart/providers/azure.js +23 -11
  250. package/dist/heart/providers/error-classification.js +127 -0
  251. package/dist/heart/providers/github-copilot.js +34 -38
  252. package/dist/heart/providers/minimax-vlm.js +189 -0
  253. package/dist/heart/providers/minimax.js +26 -8
  254. package/dist/heart/providers/openai-codex-token.js +349 -0
  255. package/dist/heart/providers/openai-codex.js +55 -40
  256. package/dist/heart/providers/openai-compatible.js +177 -0
  257. package/dist/heart/run-ledger.js +247 -0
  258. package/dist/heart/runtime-capability-check.js +170 -0
  259. package/dist/heart/runtime-credentials.js +607 -0
  260. package/dist/heart/runtime-cwd.js +87 -0
  261. package/dist/heart/sense-truth.js +22 -4
  262. package/dist/heart/session-activity.js +90 -24
  263. package/dist/heart/session-events.js +1318 -0
  264. package/dist/heart/session-playback-cli-main.js +5 -0
  265. package/dist/heart/session-playback-cli.js +36 -0
  266. package/dist/heart/session-playback.js +231 -0
  267. package/dist/heart/session-stats-cli-main.js +5 -0
  268. package/dist/heart/session-stats.js +182 -0
  269. package/dist/heart/session-transcript.js +133 -0
  270. package/dist/heart/start-of-turn-packet.js +368 -0
  271. package/dist/heart/steward-policy.js +302 -0
  272. package/dist/heart/streaming.js +604 -207
  273. package/dist/heart/structured-output.js +196 -0
  274. package/dist/heart/sync-classification.js +176 -0
  275. package/dist/heart/sync.js +449 -0
  276. package/dist/heart/target-resolution.js +11 -7
  277. package/dist/heart/tempo.js +93 -0
  278. package/dist/heart/temporal-view.js +41 -0
  279. package/dist/heart/timeouts.js +101 -0
  280. package/dist/heart/tool-activity-callbacks.js +59 -0
  281. package/dist/heart/tool-approval.js +342 -0
  282. package/dist/heart/tool-description.js +155 -0
  283. package/dist/heart/tool-friction.js +55 -0
  284. package/dist/heart/tool-loop.js +200 -0
  285. package/dist/heart/turn-context.js +481 -0
  286. package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +6 -5
  287. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
  288. package/dist/heart/versioning/ouro-path-installer.js +432 -0
  289. package/dist/heart/versioning/ouro-recovery-launcher.js +76 -0
  290. package/dist/heart/versioning/ouro-version-manager.js +409 -0
  291. package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
  292. package/dist/heart/{daemon → versioning}/update-checker.js +6 -1
  293. package/dist/heart/versioning/update-hooks.js +154 -0
  294. package/dist/heart/versioning/version-intent.js +114 -0
  295. package/dist/heart/work-card.js +380 -0
  296. package/dist/mailbox-ui/assets/index-Du_9G9WO.css +1 -0
  297. package/dist/mailbox-ui/assets/index-J8zDwHmE.js +1 -0
  298. package/dist/mailbox-ui/assets/vendor-CcN1XpQ9.js +61 -0
  299. package/dist/mailbox-ui/index.html +16 -0
  300. package/dist/mailroom/attention.js +167 -0
  301. package/dist/mailroom/autonomy.js +209 -0
  302. package/dist/mailroom/blob-store.js +889 -0
  303. package/dist/mailroom/body-cache.js +61 -0
  304. package/dist/mailroom/cache-sync-cli.js +58 -0
  305. package/dist/mailroom/core.js +803 -0
  306. package/dist/mailroom/entry.js +160 -0
  307. package/dist/mailroom/file-store.js +568 -0
  308. package/dist/mailroom/hosted-cache-sync.js +384 -0
  309. package/dist/mailroom/mbox-import.js +393 -0
  310. package/dist/mailroom/migration.js +164 -0
  311. package/dist/mailroom/outbound.js +380 -0
  312. package/dist/mailroom/policy.js +263 -0
  313. package/dist/mailroom/reader.js +292 -0
  314. package/dist/mailroom/search-cache.js +513 -0
  315. package/dist/mailroom/search-relevance.js +319 -0
  316. package/dist/mailroom/smtp-ingress.js +176 -0
  317. package/dist/mailroom/source-state.js +176 -0
  318. package/dist/mailroom/thread.js +109 -0
  319. package/dist/mailroom/travel-extract.js +89 -0
  320. package/dist/mind/bundle-manifest.js +24 -2
  321. package/dist/mind/context.js +278 -111
  322. package/dist/mind/desk-section.js +362 -0
  323. package/dist/mind/diary-integrity.js +60 -0
  324. package/dist/mind/{memory.js → diary.js} +68 -77
  325. package/dist/mind/embedding-provider.js +60 -0
  326. package/dist/mind/file-state.js +179 -0
  327. package/dist/mind/{associative-recall.js → note-search.js} +47 -58
  328. package/dist/mind/obligation-steering.js +227 -0
  329. package/dist/mind/pending.js +34 -8
  330. package/dist/mind/prompt-budget.js +479 -0
  331. package/dist/mind/prompt-refresh.js +3 -2
  332. package/dist/mind/prompt.js +1050 -187
  333. package/dist/mind/provenance-trust.js +26 -0
  334. package/dist/mind/record-paths.js +312 -0
  335. package/dist/mind/scrutiny.js +173 -0
  336. package/dist/mind/session-transaction.js +379 -0
  337. package/dist/nerves/cli-logging.js +7 -1
  338. package/dist/nerves/coverage/audit-rules.js +15 -6
  339. package/dist/nerves/coverage/audit.js +28 -2
  340. package/dist/nerves/coverage/cli.js +1 -1
  341. package/dist/nerves/coverage/contract.js +5 -5
  342. package/dist/nerves/coverage/file-completeness.js +139 -5
  343. package/dist/nerves/coverage/run-artifacts.js +55 -35
  344. package/dist/nerves/coverage/source-scanner.js +4 -4
  345. package/dist/nerves/event-buffer.js +111 -0
  346. package/dist/nerves/index.js +293 -9
  347. package/dist/nerves/observation.js +20 -0
  348. package/dist/nerves/redact.js +79 -0
  349. package/dist/nerves/review/cli-main.js +5 -0
  350. package/dist/nerves/review/cli.js +156 -0
  351. package/dist/nerves/review/core.js +159 -0
  352. package/dist/nerves/runtime.js +17 -1
  353. package/dist/repertoire/ado-client.js +15 -56
  354. package/dist/repertoire/ado-semantic.js +16 -10
  355. package/dist/repertoire/api-client.js +97 -0
  356. package/dist/repertoire/bitwarden-store.js +1045 -0
  357. package/dist/repertoire/bundle-templates.js +71 -0
  358. package/dist/repertoire/bw-installer.js +180 -0
  359. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  360. package/dist/repertoire/coding/context-pack.js +331 -0
  361. package/dist/repertoire/coding/feedback.js +218 -30
  362. package/dist/repertoire/coding/index.js +15 -3
  363. package/dist/repertoire/coding/manager.js +166 -10
  364. package/dist/repertoire/coding/spawner.js +58 -11
  365. package/dist/repertoire/coding/tools.js +242 -14
  366. package/dist/repertoire/coding/workbench-client.js +272 -0
  367. package/dist/repertoire/coding/workbench-manager.js +346 -0
  368. package/dist/repertoire/commerce-errors.js +109 -0
  369. package/dist/repertoire/commerce-self-test.js +156 -0
  370. package/dist/repertoire/credential-access.js +178 -0
  371. package/dist/repertoire/desk/classifier.js +362 -0
  372. package/dist/repertoire/duffel-client.js +185 -0
  373. package/dist/repertoire/github-client.js +14 -55
  374. package/dist/repertoire/graph-client.js +11 -52
  375. package/dist/repertoire/guardrails.js +813 -18
  376. package/dist/repertoire/mcp-client.js +44 -3
  377. package/dist/repertoire/mcp-manager.js +259 -20
  378. package/dist/repertoire/mcp-tools.js +83 -0
  379. package/dist/repertoire/plugin-mcp.js +175 -0
  380. package/dist/repertoire/plugins.js +253 -0
  381. package/dist/repertoire/relationship-authorization.js +194 -0
  382. package/dist/repertoire/shell-sessions.js +133 -0
  383. package/dist/repertoire/skills.js +48 -4
  384. package/dist/repertoire/stripe-client.js +131 -0
  385. package/dist/repertoire/tool-arguments.js +143 -0
  386. package/dist/repertoire/tool-results.js +29 -0
  387. package/dist/repertoire/tools-a2a.js +699 -0
  388. package/dist/repertoire/tools-attachments.js +317 -0
  389. package/dist/repertoire/tools-awaiting.js +575 -0
  390. package/dist/repertoire/tools-base.js +82 -1124
  391. package/dist/repertoire/tools-bluebubbles.js +2 -0
  392. package/dist/repertoire/tools-bridge.js +144 -0
  393. package/dist/repertoire/tools-bundle.js +993 -0
  394. package/dist/repertoire/tools-commerce.js +253 -0
  395. package/dist/repertoire/tools-config.js +186 -0
  396. package/dist/repertoire/tools-continuity.js +507 -0
  397. package/dist/repertoire/tools-credential.js +383 -0
  398. package/dist/repertoire/tools-evolution.js +527 -0
  399. package/dist/repertoire/tools-files.js +344 -0
  400. package/dist/repertoire/tools-flight.js +290 -0
  401. package/dist/repertoire/tools-flow.js +119 -0
  402. package/dist/repertoire/tools-github.js +3 -8
  403. package/dist/repertoire/tools-habits.js +103 -0
  404. package/dist/repertoire/tools-mail.js +1828 -0
  405. package/dist/repertoire/tools-notes.js +485 -0
  406. package/dist/repertoire/tools-obligations.js +182 -0
  407. package/dist/repertoire/tools-orientation.js +31 -0
  408. package/dist/repertoire/tools-record.js +482 -0
  409. package/dist/repertoire/tools-rsvp.js +139 -0
  410. package/dist/repertoire/tools-runtime.js +150 -0
  411. package/dist/repertoire/tools-session.js +1096 -0
  412. package/dist/repertoire/tools-shell.js +133 -0
  413. package/dist/repertoire/tools-steward-policy.js +88 -0
  414. package/dist/repertoire/tools-stripe.js +224 -0
  415. package/dist/repertoire/tools-surface.js +355 -0
  416. package/dist/repertoire/tools-teams.js +12 -39
  417. package/dist/repertoire/tools-telegram-contacts.js +37 -0
  418. package/dist/repertoire/tools-travel.js +125 -0
  419. package/dist/repertoire/tools-trip.js +982 -0
  420. package/dist/repertoire/tools-unraid.js +460 -0
  421. package/dist/repertoire/tools-user-profile.js +146 -0
  422. package/dist/repertoire/tools-vault.js +40 -0
  423. package/dist/repertoire/tools-voice.js +145 -0
  424. package/dist/repertoire/tools.js +371 -80
  425. package/dist/repertoire/travel-api-client.js +360 -0
  426. package/dist/repertoire/unraid-client.js +155 -0
  427. package/dist/repertoire/unraid-restart.js +212 -0
  428. package/dist/repertoire/user-profile.js +131 -0
  429. package/dist/repertoire/vault-setup.js +246 -0
  430. package/dist/repertoire/vault-unlock.js +628 -0
  431. package/dist/rsvp/aisleplanner-client.js +354 -0
  432. package/dist/rsvp/cli.js +926 -0
  433. package/dist/rsvp/config.js +327 -0
  434. package/dist/rsvp/cutover.js +433 -0
  435. package/dist/rsvp/diagnostics.js +232 -0
  436. package/dist/rsvp/diff-renderer.js +176 -0
  437. package/dist/rsvp/habit-policy.js +110 -0
  438. package/dist/rsvp/habit-stage.js +123 -0
  439. package/dist/rsvp/incident-bundle.js +97 -0
  440. package/dist/rsvp/migration.js +204 -0
  441. package/dist/rsvp/native-habit-runner.js +630 -0
  442. package/dist/rsvp/outbound-state.js +628 -0
  443. package/dist/rsvp/query.js +118 -0
  444. package/dist/rsvp/replay.js +233 -0
  445. package/dist/rsvp/snapshot.js +232 -0
  446. package/dist/rsvp/spend-ledger.js +175 -0
  447. package/dist/scripts/claude-code-hook.js +41 -0
  448. package/dist/scripts/claude-code-stop-hook.js +47 -0
  449. package/dist/senses/a2a-entry.js +115 -0
  450. package/dist/senses/attention-queue.js +186 -0
  451. package/dist/senses/await-turn-message.js +58 -0
  452. package/dist/senses/bluebubbles/active-turns.js +216 -0
  453. package/dist/senses/bluebubbles/attachment-cache.js +53 -0
  454. package/dist/senses/bluebubbles/attachment-download.js +137 -0
  455. package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +355 -40
  456. package/dist/senses/bluebubbles/context-packet.js +207 -0
  457. package/dist/senses/bluebubbles/context-smoke.js +144 -0
  458. package/dist/senses/bluebubbles/entry.js +90 -0
  459. package/dist/senses/{bluebubbles-inbound-log.js → bluebubbles/inbound-log.js} +21 -3
  460. package/dist/senses/bluebubbles/index.js +3839 -0
  461. package/dist/senses/bluebubbles/latest-turn.js +311 -0
  462. package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -71
  463. package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +159 -28
  464. package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +24 -13
  465. package/dist/senses/bluebubbles/outbound-state.js +308 -0
  466. package/dist/senses/bluebubbles/processed-log.js +133 -0
  467. package/dist/senses/bluebubbles/reaction-policy.js +47 -0
  468. package/dist/senses/bluebubbles/replay.js +284 -0
  469. package/dist/senses/{bluebubbles-runtime-state.js → bluebubbles/runtime-state.js} +30 -2
  470. package/dist/senses/bluebubbles/semantic-receipts.js +1986 -0
  471. package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
  472. package/dist/senses/bluebubbles/webhook-registration.js +228 -0
  473. package/dist/senses/bluebubbles-meta-guard.js +39 -0
  474. package/dist/senses/cli/bracketed-paste.js +82 -0
  475. package/dist/senses/cli/image-paste.js +287 -0
  476. package/dist/senses/cli/image-ref-navigation.js +75 -0
  477. package/dist/senses/cli/ink-app.js +156 -0
  478. package/dist/senses/cli/inline-diff.js +64 -0
  479. package/dist/senses/cli/input-keys.js +174 -0
  480. package/dist/senses/cli/kill-ring.js +86 -0
  481. package/dist/senses/cli/message-list.js +51 -0
  482. package/dist/senses/cli/ouro-tui.js +607 -0
  483. package/dist/senses/cli/spinner-imperative.js +135 -0
  484. package/dist/senses/cli/spinner.js +101 -0
  485. package/dist/senses/cli/status-line.js +60 -0
  486. package/dist/senses/cli/streaming-markdown.js +526 -0
  487. package/dist/senses/cli/tool-display.js +85 -0
  488. package/dist/senses/cli/tool-render.js +85 -0
  489. package/dist/senses/cli/tui-store.js +241 -0
  490. package/dist/senses/cli/virtual-list.js +35 -0
  491. package/dist/senses/cli-entry.js +60 -8
  492. package/dist/senses/cli-layout.js +100 -0
  493. package/dist/senses/cli.js +621 -321
  494. package/dist/senses/commands.js +66 -3
  495. package/dist/senses/context-packet-ledger.js +371 -0
  496. package/dist/senses/context-packets.js +154 -0
  497. package/dist/senses/habit-lifecycle-guard.js +24 -0
  498. package/dist/senses/habit-turn-message.js +160 -0
  499. package/dist/senses/ingress-evidence.js +2 -0
  500. package/dist/senses/mail-entry.js +66 -0
  501. package/dist/senses/mail.js +524 -0
  502. package/dist/senses/pipeline.js +1074 -193
  503. package/dist/senses/private-runtime-worker.js +774 -0
  504. package/dist/senses/private-runtime.js +1459 -0
  505. package/dist/senses/proactive-content-guard.js +51 -0
  506. package/dist/senses/sanctuary-grounding.js +118 -0
  507. package/dist/senses/sanctuary-health-acceptance-probe-entry.js +12 -0
  508. package/dist/senses/sanctuary-health-acceptance-probe.js +774 -0
  509. package/dist/senses/sanctuary-health-runner.js +101 -0
  510. package/dist/senses/sanctuary-health.js +408 -0
  511. package/dist/senses/sanctuary-interactive-control.js +306 -0
  512. package/dist/senses/sanctuary-runtime.js +240 -0
  513. package/dist/senses/sanctuary-sab.js +80 -0
  514. package/dist/senses/shared-turn.js +487 -0
  515. package/dist/senses/surface-tool.js +109 -0
  516. package/dist/senses/teams-entry.js +60 -8
  517. package/dist/senses/teams.js +515 -217
  518. package/dist/senses/telegram-admission.js +695 -0
  519. package/dist/senses/telegram-approval-runtime.js +566 -0
  520. package/dist/senses/telegram-audit-ledger.js +220 -0
  521. package/dist/senses/telegram-client.js +1635 -0
  522. package/dist/senses/telegram-effect-adapter.js +787 -0
  523. package/dist/senses/telegram-entry.js +83 -0
  524. package/dist/senses/telegram.js +1914 -0
  525. package/dist/senses/trust-gate.js +104 -9
  526. package/dist/senses/voice/audio-playback.js +237 -0
  527. package/dist/senses/voice/audio-routing.js +119 -0
  528. package/dist/senses/voice/elevenlabs.js +202 -0
  529. package/dist/senses/voice/floor-control.js +431 -0
  530. package/dist/senses/voice/floor-controller.js +115 -0
  531. package/dist/senses/voice/golden-path.js +116 -0
  532. package/dist/senses/voice/index.js +29 -0
  533. package/dist/senses/voice/meeting.js +113 -0
  534. package/dist/senses/voice/outbound.js +190 -0
  535. package/dist/senses/voice/phone.js +33 -0
  536. package/dist/senses/voice/playback.js +139 -0
  537. package/dist/senses/voice/realtime-eval.js +496 -0
  538. package/dist/senses/voice/realtime-trace.js +531 -0
  539. package/dist/senses/voice/transcript.js +70 -0
  540. package/dist/senses/voice/turn.js +191 -0
  541. package/dist/senses/voice/twilio-phone-runtime.js +807 -0
  542. package/dist/senses/voice/twilio-phone.js +5081 -0
  543. package/dist/senses/voice/types.js +2 -0
  544. package/dist/senses/voice/whisper.js +161 -0
  545. package/dist/senses/voice-entry.js +81 -0
  546. package/dist/senses/voice-realtime-eval-command.js +99 -0
  547. package/dist/senses/voice-realtime-eval-entry.js +21 -0
  548. package/dist/senses/voice-twilio-entry.js +87 -0
  549. package/dist/trips/core.js +138 -0
  550. package/dist/trips/store.js +265 -0
  551. package/dist/util/frontmatter.js +69 -0
  552. package/npm-shrinkwrap.json +8129 -0
  553. package/package.json +71 -12
  554. package/skills/agent-commerce.md +113 -0
  555. package/skills/browser-navigation.md +117 -0
  556. package/skills/commerce-setup-guide.md +116 -0
  557. package/skills/commerce-setup.md +84 -0
  558. package/skills/configure-dev-tools.md +99 -0
  559. package/skills/travel-planning.md +138 -0
  560. package/dist/heart/daemon/auth-flow.js +0 -430
  561. package/dist/heart/daemon/ouro-path-installer.js +0 -178
  562. package/dist/heart/daemon/update-hooks.js +0 -138
  563. package/dist/heart/safe-workspace.js +0 -228
  564. package/dist/heart/session-recall.js +0 -116
  565. package/dist/mind/friends/channel.js +0 -84
  566. package/dist/mind/friends/group-context.js +0 -144
  567. package/dist/mind/friends/resolver.js +0 -84
  568. package/dist/mind/friends/store-file.js +0 -190
  569. package/dist/mind/friends/store.js +0 -4
  570. package/dist/mind/friends/tokens.js +0 -26
  571. package/dist/mind/friends/trust-explanation.js +0 -74
  572. package/dist/mind/friends/types.js +0 -29
  573. package/dist/repertoire/tasks/board.js +0 -134
  574. package/dist/repertoire/tasks/index.js +0 -224
  575. package/dist/repertoire/tasks/lifecycle.js +0 -80
  576. package/dist/repertoire/tasks/middleware.js +0 -65
  577. package/dist/repertoire/tasks/parser.js +0 -173
  578. package/dist/repertoire/tasks/scanner.js +0 -132
  579. package/dist/repertoire/tasks/transitions.js +0 -144
  580. package/dist/senses/bluebubbles-entry.js +0 -13
  581. package/dist/senses/bluebubbles.js +0 -1180
  582. package/dist/senses/debug-activity.js +0 -148
  583. package/dist/senses/inner-dialog-worker.js +0 -90
  584. package/dist/senses/inner-dialog.js +0 -509
  585. package/subagents/README.md +0 -7
  586. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  587. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  588. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  589. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  590. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  591. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  592. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  593. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  594. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  595. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  596. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  597. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  598. /package/dist/{repertoire/tasks/types.js → heart/attachments/sources/adapter.js} +0 -0
  599. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  600. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  601. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
  602. /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
@@ -0,0 +1,306 @@
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.proveSanctuaryAttemptedRecoveryWithoutRetry = proveSanctuaryAttemptedRecoveryWithoutRetry;
37
+ exports.executeSanctuaryInteractiveEngine = executeSanctuaryInteractiveEngine;
38
+ exports.createSanctuaryInteractiveControl = createSanctuaryInteractiveControl;
39
+ exports.sanctuaryInteractiveControlReady = sanctuaryInteractiveControlReady;
40
+ const node_crypto_1 = require("node:crypto");
41
+ const node_fs_1 = require("node:fs");
42
+ const node_net_1 = require("node:net");
43
+ const path = __importStar(require("node:path"));
44
+ const approval_store_1 = require("../heart/approval-store");
45
+ const tool_approval_1 = require("../heart/tool-approval");
46
+ const runtime_1 = require("../nerves/runtime");
47
+ const telegram_client_1 = require("./telegram-client");
48
+ const SHA256 = /^[0-9a-f]{64}$/u;
49
+ const MAX_CONTROL_REQUEST = 16 * 1024;
50
+ function object(value, label) {
51
+ if (!value || typeof value !== "object" || Array.isArray(value))
52
+ throw new Error(`${label} must be an object`);
53
+ return value;
54
+ }
55
+ function exactKeys(value, keys, label) {
56
+ if (JSON.stringify(Object.keys(value).sort()) !== JSON.stringify([...keys].sort()))
57
+ throw new Error(`${label} shape is invalid`);
58
+ }
59
+ function sha256(value) {
60
+ return (0, node_crypto_1.createHash)("sha256").update(value, "utf8").digest("hex");
61
+ }
62
+ function exactApproval(projections) {
63
+ const matches = projections.filter(({ approval }) => approval.toolName === "unraid_restart_container" && approval.arguments.container === "calibre-web");
64
+ if (matches.length !== 1)
65
+ throw new Error("interactive production approval is absent or ambiguous");
66
+ return matches[0];
67
+ }
68
+ function exactPending(records, approvalId) {
69
+ const validCallback = (value) => typeof value === "string" && Buffer.byteLength(value) >= 1 && Buffer.byteLength(value) <= 64;
70
+ const matches = records.filter((record) => record.approvalId === approvalId && record.deliveryState === "bound" && record.messageId !== null
71
+ && validCallback(record.approveCallbackData) && validCallback(record.denyCallbackData));
72
+ if (matches.length !== 1)
73
+ throw new Error("interactive production pending approval is absent or ambiguous");
74
+ return matches[0];
75
+ }
76
+ function exactTerminalTombstone(records, approvalId) {
77
+ const validCallback = (value) => typeof value === "string" && Buffer.byteLength(value) >= 1 && Buffer.byteLength(value) <= 64;
78
+ const matches = records.filter((record) => record.approvalId === approvalId && record.deliveryState === "terminal_tombstone" && record.messageId !== null
79
+ && validCallback(record.approveCallbackData) && validCallback(record.denyCallbackData)
80
+ && record.expiryObservation?.schemaVersion === "telegram-approval-expiry-observation-v1"
81
+ && record.expiryObservation.deadlineAt === record.expiresAt && Number.isSafeInteger(record.expiryObservation.observedAt)
82
+ && record.expiryObservation.observedAt >= record.expiresAt && typeof record.expiryObservation.evidenceMac === "string" && SHA256.test(record.expiryObservation.evidenceMac)
83
+ && Number.isSafeInteger(record.terminalizedAt) && Number(record.terminalizedAt) >= record.expiryObservation.observedAt);
84
+ if (matches.length !== 1)
85
+ throw new Error("interactive production terminal tombstone is absent or ambiguous");
86
+ return matches[0];
87
+ }
88
+ function pendingDigest(record) {
89
+ return sha256(JSON.stringify({ approvalId: record.approvalId, messageId: record.messageId, deliveryState: record.deliveryState, approveCallbackData: record.approveCallbackData, denyCallbackData: record.denyCallbackData, expiresAt: record.expiresAt }));
90
+ }
91
+ function proveSanctuaryAttemptedRecoveryWithoutRetry(agentRoot, scenarioHandleDigest, approval) {
92
+ if (!SHA256.test(scenarioHandleDigest))
93
+ throw new Error("attempted recovery scenario is invalid");
94
+ const databaseRoot = path.join(agentRoot, "state", "acceptance", "attempted-recovery-probes");
95
+ (0, node_fs_1.mkdirSync)(databaseRoot, { recursive: true, mode: 0o700 });
96
+ const databasePath = path.join(databaseRoot, `${scenarioHandleDigest}.sqlite`);
97
+ if ((0, node_fs_1.existsSync)(databasePath))
98
+ throw new Error("attempted recovery proof requires inspect-before-retry");
99
+ const instant = new Date("2026-08-21T00:00:00.000Z");
100
+ const first = (0, approval_store_1.openApprovalStore)({ databasePath, now: () => instant });
101
+ const ownerId = "acceptance-attempted-recovery";
102
+ const toolCallId = `acceptance-${approval.toolCallId}`;
103
+ const frozenAssistantMessage = structuredClone(approval.frozenAssistantMessage);
104
+ const toolCalls = frozenAssistantMessage.tool_calls;
105
+ if (!Array.isArray(toolCalls) || toolCalls.length !== 1 || !toolCalls[0] || typeof toolCalls[0] !== "object" || Array.isArray(toolCalls[0])) {
106
+ first.close();
107
+ throw new Error("attempted recovery frozen tool call is invalid");
108
+ }
109
+ toolCalls[0].id = toolCallId;
110
+ const prepared = first.prepare({
111
+ toolCallId, toolName: approval.toolName, arguments: approval.arguments,
112
+ schemaDigest: approval.schemaDigest, toolDigest: approval.toolDigest, policyDigest: approval.policyDigest, policyId: approval.policyId,
113
+ sessionKey: "acceptance:attempted-recovery", sessionPath: path.join(databaseRoot, `${scenarioHandleDigest}.session.json`),
114
+ baseSessionRevision: approval.baseSessionRevision, checkpointDigest: approval.checkpointDigest,
115
+ requesterId: "acceptance-probe", transport: "telegram", transportUserId: "acceptance-probe", transportChatId: "acceptance-probe",
116
+ expiresAt: "2099-01-01T00:00:00.000Z", frozenAssistantMessage,
117
+ });
118
+ first.activate({ approvalId: prepared.record.approvalId, checkpointDigest: prepared.record.checkpointDigest, suspendedSessionRevision: approval.suspendedSessionRevision });
119
+ first.bindPrompt({ approvalId: prepared.record.approvalId, transport: "telegram", transportChatId: "acceptance-probe", transportMessageId: "acceptance-message" });
120
+ const claimed = first.decide({
121
+ approvalId: prepared.record.approvalId, decisionToken: prepared.decisionToken, decision: "approve", requesterId: "acceptance-probe",
122
+ transport: "telegram", transportUserId: "acceptance-probe", transportChatId: "acceptance-probe", transportMessageId: "acceptance-message",
123
+ sessionKey: "acceptance:attempted-recovery", ownerId,
124
+ });
125
+ const attempted = first.markAttempted({ approvalId: claimed.approvalId, ownerId, epoch: claimed.epoch });
126
+ first.close();
127
+ const attemptedRecordDigest = sha256(JSON.stringify(attempted));
128
+ const reopened = (0, approval_store_1.openApprovalStore)({ databasePath, now: () => instant });
129
+ const durableAttempted = reopened.read(attempted.approvalId);
130
+ if (!durableAttempted || durableAttempted.state !== "attempted") {
131
+ reopened.close();
132
+ throw new Error("attempted recovery record was not durable across reopen");
133
+ }
134
+ const recovered = (0, tool_approval_1.recoverAttemptedApproval)({ approvalStore: reopened, approvalId: durableAttempted.approvalId });
135
+ let retryCount = 0;
136
+ try {
137
+ (0, tool_approval_1.recoverAttemptedApproval)({ approvalStore: reopened, approvalId: durableAttempted.approvalId });
138
+ retryCount += 1;
139
+ }
140
+ catch { /* expected */ }
141
+ reopened.close();
142
+ return { observed: recovered.state === "attempted_indeterminate", retryCount, reopened: true, attemptedRecordDigest, recoveredRecordDigest: sha256(JSON.stringify(recovered)) };
143
+ }
144
+ async function executeSanctuaryInteractiveEngine(raw, deps) {
145
+ const payload = object(raw, "interactive runtime payload");
146
+ exactKeys(payload, ["operation", "label", "scenarioHandleDigest"], "interactive runtime payload");
147
+ const operation = payload.operation;
148
+ const label = payload.label;
149
+ const scenarioHandleDigest = payload.scenarioHandleDigest;
150
+ if (typeof scenarioHandleDigest !== "string" || !SHA256.test(scenarioHandleDigest)
151
+ || !["unit-16k-timeout-stale", "unit-16l-duplicate-callback", "unit-16m-restart-continuation"].includes(label)
152
+ || !["drive_timeout_stale", "drive_duplicate_callbacks", "prepare_restart_continuation", "reconcile_restart_continuation"].includes(String(operation))
153
+ || (operation === "drive_timeout_stale") !== (label === "unit-16k-timeout-stale")
154
+ || (operation === "drive_duplicate_callbacks") !== (label === "unit-16l-duplicate-callback"))
155
+ throw new Error("interactive runtime operation binding is invalid");
156
+ const before = exactApproval(deps.readApprovals(scenarioHandleDigest));
157
+ if (!before.approval.suspendedSessionRevision)
158
+ throw new Error("interactive production approval checkpoint is unavailable");
159
+ if (operation === "drive_timeout_stale") {
160
+ if (before.approval.state === "proposed") {
161
+ exactPending(deps.readPending(), before.approval.approvalId);
162
+ return { state: "waiting" };
163
+ }
164
+ if (before.approval.state !== "expired" || before.approval.epoch !== 0)
165
+ throw new Error("timeout stale approval is not expired without a claim");
166
+ const captured = exactTerminalTombstone(deps.readPending(), before.approval.approvalId);
167
+ const session = await deps.createSession();
168
+ try {
169
+ const queryId = (0, node_crypto_1.randomBytes)(18).toString("base64url");
170
+ const result = await session.handle({ callbackData: captured.approveCallbackData, queryId, messageId: captured.messageId });
171
+ const after = exactApproval(deps.readApprovals(scenarioHandleDigest));
172
+ const promptTerminal = !session.pendingApprovalIds().includes(before.approval.approvalId);
173
+ if (!result.handled || result.accepted || result.reason !== "stale_callback" || after.approval.state !== "expired" || after.approval.epoch !== 0 || !promptTerminal) {
174
+ throw new Error("timeout stale callback proof failed");
175
+ }
176
+ return {
177
+ schemaVersion: "sanctuary-timeout-stale-driver-receipt-v1", phase: "complete", label, scenarioHandleDigest,
178
+ approvalIdDigest: sha256(before.approval.approvalId), checkpointDigest: before.approval.checkpointDigest,
179
+ suspendedSessionRevisionDigest: sha256(before.approval.suspendedSessionRevision), approvalEpochBefore: before.approval.epoch,
180
+ callbackAttempts: 1, distinctQueryCount: 1, callbackDataDigest: sha256(captured.approveCallbackData),
181
+ settledCount: 1, claimCount: 0, mutationCount: 0, staleAcknowledged: true, promptTerminal,
182
+ };
183
+ }
184
+ finally {
185
+ session.close();
186
+ }
187
+ }
188
+ if (before.approval.state !== "proposed")
189
+ throw new Error("interactive production approval is not currently proposed");
190
+ const pending = exactPending(deps.readPending(), before.approval.approvalId);
191
+ const common = { approvalIdDigest: sha256(before.approval.approvalId), checkpointDigest: before.approval.checkpointDigest, suspendedSessionRevisionDigest: sha256(before.approval.suspendedSessionRevision), approvalEpochBefore: before.approval.epoch };
192
+ if (operation === "prepare_restart_continuation") {
193
+ const recovery = deps.proveIndeterminateRecovery(before.approval, scenarioHandleDigest);
194
+ if (!recovery.observed || recovery.retryCount !== 0 || !recovery.reopened
195
+ || !SHA256.test(recovery.attemptedRecordDigest) || !SHA256.test(recovery.recoveredRecordDigest)
196
+ || recovery.attemptedRecordDigest === recovery.recoveredRecordDigest)
197
+ throw new Error("isolated attempted recovery proof failed");
198
+ return {
199
+ schemaVersion: "sanctuary-interactive-driver-receipt-v2", phase: "prepared", label, scenarioHandleDigest, ...common,
200
+ pendingDigestBefore: pendingDigest(pending), indeterminateRecoveryObserved: true,
201
+ attemptedRecoveryReopened: true, attemptedRecordDigest: recovery.attemptedRecordDigest, recoveredRecordDigest: recovery.recoveredRecordDigest,
202
+ };
203
+ }
204
+ const session = await deps.createSession();
205
+ try {
206
+ if (operation === "drive_duplicate_callbacks") {
207
+ const queryIds = [(0, node_crypto_1.randomBytes)(18).toString("base64url"), (0, node_crypto_1.randomBytes)(18).toString("base64url")];
208
+ let arrivals = 0;
209
+ let release;
210
+ const barrier = new Promise((resolve) => { release = resolve; });
211
+ const invoke = async (queryId) => { arrivals += 1; if (arrivals === 2)
212
+ release(); await barrier; return session.handle({ callbackData: pending.approveCallbackData, queryId, messageId: pending.messageId }); };
213
+ const results = await Promise.all(queryIds.map(invoke));
214
+ const after = exactApproval(deps.readApprovals(scenarioHandleDigest));
215
+ const stale = await session.handle({ callbackData: pending.approveCallbackData, queryId: (0, node_crypto_1.randomBytes)(18).toString("base64url"), messageId: pending.messageId });
216
+ return { schemaVersion: "sanctuary-interactive-driver-receipt-v2", phase: "complete", label, scenarioHandleDigest, ...common, callbackAttempts: 2, distinctQueryCount: new Set(queryIds).size, callbackDataDigest: sha256(pending.approveCallbackData), barrierObserved: arrivals === 2, settledCount: results.filter(({ handled }) => handled).length, claimCount: after.approval.epoch === before.approval.epoch + 1 ? 1 : 0, mutationCount: after.approval.state === "succeeded" ? 1 : 0, staleReplayAttempts: 1, staleReplaySettled: stale.handled, staleReplayMutationCount: 0, promptTerminal: !session.pendingApprovalIds().includes(before.approval.approvalId), writeCredentialObserved: deps.writeCredentialObserved() };
217
+ }
218
+ const result = await session.handle({ callbackData: pending.approveCallbackData, queryId: (0, node_crypto_1.randomBytes)(18).toString("base64url"), messageId: pending.messageId });
219
+ const after = exactApproval(deps.readApprovals(scenarioHandleDigest));
220
+ if (!after.continuation || after.approval.state !== "succeeded")
221
+ throw new Error("restart continuation did not complete");
222
+ return { approvalEpochAfterRestart: before.approval.epoch, continuationEpochAfter: after.continuation.continuationEpoch, pendingDigestAfter: pendingDigest(pending), pendingRestored: true, callbackAttempts: 1, mutationCount: result.accepted ? 1 : 0, indeterminateRetryCount: 0 };
223
+ }
224
+ finally {
225
+ session.close();
226
+ }
227
+ }
228
+ function createSanctuaryInteractiveControl(options) {
229
+ const socketPath = path.join(options.agentRoot, "state", "acceptance", "telegram-control.sock");
230
+ const runRequest = options.runRequest ?? (async (operation) => operation());
231
+ let server;
232
+ let updateId = 2_100_000_000;
233
+ return {
234
+ socketPath,
235
+ async start() {
236
+ if (server)
237
+ return;
238
+ (0, node_fs_1.mkdirSync)(path.dirname(socketPath), { recursive: true, mode: 0o700 });
239
+ if ((0, node_fs_1.existsSync)(socketPath))
240
+ (0, node_fs_1.unlinkSync)(socketPath);
241
+ server = (0, node_net_1.createServer)({ allowHalfOpen: true }, (connection) => {
242
+ let raw = "";
243
+ connection.setEncoding("utf8");
244
+ connection.on("error", () => undefined);
245
+ connection.on("data", (chunk) => { raw += chunk; if (Buffer.byteLength(raw) > MAX_CONTROL_REQUEST)
246
+ connection.destroy(); });
247
+ connection.on("end", () => {
248
+ void runRequest(async () => {
249
+ try {
250
+ (0, runtime_1.emitNervesEvent)({ component: "senses", event: "senses.sanctuary_interactive_control_request", message: "Sanctuary interactive control request received", meta: { bytes: Buffer.byteLength(raw) } });
251
+ const parsed = object(JSON.parse(raw), "interactive control request");
252
+ if (parsed.operation === "interactive_runtime_ready") {
253
+ exactKeys(parsed, ["operation", "label", "scenarioHandleDigest"], "interactive readiness request");
254
+ if (parsed.label !== "unit-16m-restart-continuation" || typeof parsed.scenarioHandleDigest !== "string" || !SHA256.test(parsed.scenarioHandleDigest))
255
+ throw new Error("interactive readiness binding is invalid");
256
+ connection.end(`${JSON.stringify({ ok: true, result: { ready: true } })}\n`);
257
+ return;
258
+ }
259
+ const result = await executeSanctuaryInteractiveEngine(parsed, {
260
+ agentRoot: options.agentRoot,
261
+ readApprovals: (digest) => (0, approval_store_1.readApprovalsByScenarioHandleDigest)(path.join(options.agentRoot, "state", "approvals", "approvals.sqlite"), digest),
262
+ readPending: () => new telegram_client_1.FileTelegramPendingApprovalStore(path.join(options.agentRoot, "state", "approvals", "telegram-pending.json")).load(),
263
+ createSession: async () => ({
264
+ handle: ({ callbackData, queryId, messageId }) => runRequest(() => options.transport.handleUpdate({
265
+ update_id: updateId++,
266
+ callback_query: {
267
+ id: queryId,
268
+ from: { id: Number(options.authorizedUserId) },
269
+ data: callbackData,
270
+ message: {
271
+ message_id: Number(messageId),
272
+ chat: { id: Number(options.authorizedChatId) },
273
+ },
274
+ },
275
+ })),
276
+ pendingApprovalIds: () => options.transport.listPendingDeliveries().map(({ approvalId }) => approvalId),
277
+ close: () => undefined,
278
+ }),
279
+ proveIndeterminateRecovery: (approval, digest) => proveSanctuaryAttemptedRecoveryWithoutRetry(options.agentRoot, digest, approval),
280
+ writeCredentialObserved: () => /credential|api[_-]?key|token|secret/iu.test(raw),
281
+ });
282
+ connection.end(`${JSON.stringify({ ok: true, result })}\n`);
283
+ }
284
+ catch {
285
+ connection.end(`${JSON.stringify({ ok: false, error: "interactive runtime operation failed" })}\n`);
286
+ }
287
+ }).catch(() => { if (!connection.destroyed)
288
+ connection.end(`${JSON.stringify({ ok: false, error: "interactive runtime operation failed" })}\n`); });
289
+ });
290
+ });
291
+ await new Promise((resolve, reject) => { server.once("error", reject); server.listen(socketPath, () => { server.off("error", reject); (0, node_fs_1.chmodSync)(socketPath, 0o600); resolve(); }); });
292
+ },
293
+ async stop() { const active = server; server = undefined; if (active)
294
+ await new Promise((resolve) => active.close(() => resolve())); if ((0, node_fs_1.existsSync)(socketPath))
295
+ (0, node_fs_1.unlinkSync)(socketPath); },
296
+ };
297
+ }
298
+ async function sanctuaryInteractiveControlReady(socketPath, timeoutMs = 1_000) {
299
+ return new Promise((resolve) => {
300
+ const socket = (0, node_net_1.createConnection)(socketPath);
301
+ const finish = (ready) => { socket.destroy(); resolve(ready); };
302
+ socket.setTimeout(timeoutMs, () => finish(false));
303
+ socket.once("connect", () => finish(true));
304
+ socket.once("error", () => finish(false));
305
+ });
306
+ }
@@ -0,0 +1,240 @@
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.runWithSanctuaryToolReceiptCollection = runWithSanctuaryToolReceiptCollection;
37
+ exports.createSanctuaryToolContext = createSanctuaryToolContext;
38
+ const fs = __importStar(require("node:fs"));
39
+ const path = __importStar(require("node:path"));
40
+ const node_crypto_1 = require("node:crypto");
41
+ const node_async_hooks_1 = require("node:async_hooks");
42
+ const identity_1 = require("../heart/identity");
43
+ const runtime_credentials_1 = require("../heart/runtime-credentials");
44
+ const unraid_restart_1 = require("../repertoire/unraid-restart");
45
+ const steward_policy_1 = require("../heart/steward-policy");
46
+ const unraid_client_1 = require("../repertoire/unraid-client");
47
+ const tools_unraid_1 = require("../repertoire/tools-unraid");
48
+ const runtime_1 = require("../nerves/runtime");
49
+ const sanctuary_acceptance_marker_1 = require("../heart/daemon/sanctuary-acceptance-marker");
50
+ const sanctuary_grounding_1 = require("./sanctuary-grounding");
51
+ const sanctuary_health_1 = require("./sanctuary-health");
52
+ const sanctuary_sab_1 = require("./sanctuary-sab");
53
+ const sanctuaryToolReceipts = new node_async_hooks_1.AsyncLocalStorage();
54
+ const sanctuaryToolGroundings = new node_async_hooks_1.AsyncLocalStorage();
55
+ const acceptanceLedgerTails = new Map();
56
+ async function runWithSanctuaryToolReceiptCollection(operation, observer) {
57
+ const digests = observer?.toolResultDigests ?? [];
58
+ const groundings = observer?.toolGroundings ?? [];
59
+ const result = await sanctuaryToolReceipts.run(digests, () => sanctuaryToolGroundings.run(groundings, operation));
60
+ return { result, toolResultDigests: [...digests], ...(groundings.length > 0 ? { toolGroundings: structuredClone(groundings) } : {}) };
61
+ }
62
+ function collectToolResult(result, toolName) {
63
+ const digest = (0, node_crypto_1.createHash)("sha256").update(JSON.stringify(result)).digest("hex");
64
+ sanctuaryToolReceipts.getStore()?.push(digest);
65
+ let facts = null;
66
+ try {
67
+ facts = toolName ? (0, sanctuary_grounding_1.projectSanctuaryGrounding)(toolName, result) : null;
68
+ }
69
+ catch { /* validated tool adapters own malformed-result failure */ }
70
+ if (!facts || (toolName !== "unraid_get_system" && toolName !== "unraid_get_storage"))
71
+ return { resultDigest: digest };
72
+ const groundingDigest = (0, sanctuary_grounding_1.sanctuaryGroundingDigest)(facts);
73
+ const envelope = result;
74
+ const sourceIdentityDigest = envelope?.data?.sourceIdentityDigest;
75
+ if (typeof sourceIdentityDigest !== "string" || !/^[0-9a-f]{64}$/u.test(sourceIdentityDigest))
76
+ throw new Error("Sanctuary grounding source identity is invalid");
77
+ const verifiedSourceIdentityDigest = sourceIdentityDigest;
78
+ const observedAt = new Date().toISOString();
79
+ sanctuaryToolGroundings.getStore()?.push({ toolName, resultDigest: digest, groundingDigest, sourceIdentityDigest: verifiedSourceIdentityDigest, observedAt, facts });
80
+ return { resultDigest: digest, groundingDigest, sourceIdentityDigest: verifiedSourceIdentityDigest, observedAt };
81
+ }
82
+ function machineConfig(agentName) {
83
+ const result = (0, runtime_credentials_1.readMachineRuntimeCredentialConfig)(agentName);
84
+ if (!result.ok)
85
+ throw new Error(`Sanctuary machine runtime config is ${result.reason}`);
86
+ return result.config;
87
+ }
88
+ function required(config, field) {
89
+ const value = config[field];
90
+ if (typeof value !== "string" || value.trim().length === 0)
91
+ throw new Error(`Sanctuary ${field} is missing`);
92
+ return value.trim();
93
+ }
94
+ function persistAttempt(filePath, attempt) {
95
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
96
+ const temporary = `${filePath}.tmp-${process.pid}-${(0, node_crypto_1.randomUUID)()}`;
97
+ fs.writeFileSync(temporary, `${JSON.stringify(attempt)}\n`, { mode: 0o600 });
98
+ fs.renameSync(temporary, filePath);
99
+ }
100
+ async function appendAcceptanceAttempt(agentName, attempt) {
101
+ if (!attempt.scenarioHandleDigest)
102
+ return;
103
+ const filePath = path.join((0, identity_1.getAgentRoot)(agentName), "state", "acceptance", "restart-attempts.ndjson");
104
+ const previous = acceptanceLedgerTails.get(filePath) ?? Promise.resolve();
105
+ const current = previous.catch(() => undefined).then(() => {
106
+ fs.mkdirSync(path.dirname(filePath), { recursive: true, mode: 0o700 });
107
+ let existing = [];
108
+ try {
109
+ if (fs.statSync(filePath).size > 4 * 1024 * 1024)
110
+ throw new Error("restart ledger exceeds its bound");
111
+ existing = fs.readFileSync(filePath, "utf8").split("\n").filter(Boolean);
112
+ if (existing.length > 500 || existing.some((line) => Buffer.byteLength(line) > 8 * 1024))
113
+ throw new Error("restart ledger rows exceed their bound");
114
+ for (const line of existing) {
115
+ const value = JSON.parse(line);
116
+ if (!value || typeof value !== "object" || !value.container || typeof value.container.id !== "string" || !value.container.id || value.container.id.length > 128
117
+ || typeof value.container.name !== "string" || !value.container.name || value.container.name.length > 128
118
+ || !["attempt_not_started", "attempting", "succeeded", "attempted_or_indeterminate"].includes(String(value.state))
119
+ || typeof value.observedAt !== "string" || !Number.isFinite(Date.parse(value.observedAt)) || new Date(Date.parse(value.observedAt)).toISOString() !== value.observedAt
120
+ || !/^[0-9a-f]{64}$/u.test(String(value.actionDigest)) || !/^[0-9a-f]{64}$/u.test(String(value.argumentDigest))
121
+ || !/^[0-9a-f]{64}$/u.test(String(value.scenarioHandleDigest)) || typeof value.approvalId !== "string" || !value.approvalId || value.approvalId.length > 128
122
+ || typeof value.attemptId !== "string" || !value.attemptId || value.attemptId.length > 128 || typeof value.mutationAcknowledged !== "boolean"
123
+ || (value.afterState !== null && (typeof value.afterState !== "string" || value.afterState.length > 64)))
124
+ throw new Error("restart ledger row is invalid");
125
+ }
126
+ }
127
+ catch (error) {
128
+ if (error.code !== "ENOENT")
129
+ throw new Error("Sanctuary acceptance restart ledger is corrupt", { cause: error });
130
+ }
131
+ const entries = [...existing, JSON.stringify(attempt)].slice(-500);
132
+ const temporary = `${filePath}.tmp-${process.pid}-${(0, node_crypto_1.randomUUID)()}`;
133
+ fs.writeFileSync(temporary, `${entries.join("\n")}\n`, { encoding: "utf8", mode: 0o600, flag: "wx" });
134
+ const handle = fs.openSync(temporary, "r");
135
+ try {
136
+ fs.fsyncSync(handle);
137
+ }
138
+ finally {
139
+ fs.closeSync(handle);
140
+ }
141
+ fs.renameSync(temporary, filePath);
142
+ fs.chmodSync(filePath, 0o600);
143
+ const directory = fs.openSync(path.dirname(filePath), "r");
144
+ try {
145
+ fs.fsyncSync(directory);
146
+ }
147
+ finally {
148
+ fs.closeSync(directory);
149
+ }
150
+ });
151
+ acceptanceLedgerTails.set(filePath, current);
152
+ try {
153
+ await current;
154
+ }
155
+ finally {
156
+ if (acceptanceLedgerTails.get(filePath) === current)
157
+ acceptanceLedgerTails.delete(filePath);
158
+ }
159
+ }
160
+ function createSanctuaryToolContext(agentName) {
161
+ (0, runtime_1.emitNervesEvent)({
162
+ component: "senses",
163
+ event: "senses.sanctuary_runtime_create",
164
+ message: "creating typed Sanctuary tool context",
165
+ meta: { agentName },
166
+ });
167
+ const initial = machineConfig(agentName);
168
+ const endpoint = required(initial, "unraidGraphqlUrl");
169
+ const readClient = new unraid_client_1.UnraidClient({ endpoint, apiKey: required(initial, "unraidReadApiKey") });
170
+ const reads = (0, tools_unraid_1.createUnraidReadTools)(readClient);
171
+ const sab = (0, sanctuary_sab_1.createSanctuarySabClient)();
172
+ const acceptanceRead = (toolName, read) => async (...args) => {
173
+ try {
174
+ const result = await read(...args);
175
+ const receipt = collectToolResult(result, toolName);
176
+ (0, runtime_1.emitNervesEvent)({ component: "senses", event: "senses.sanctuary_read_receipt", message: "Sanctuary live read completed", meta: { toolName, success: true, ...receipt, ...(0, sanctuary_acceptance_marker_1.sanctuaryAcceptanceEventMeta)(agentName) } });
177
+ return result;
178
+ }
179
+ catch (error) {
180
+ (0, runtime_1.emitNervesEvent)({ level: "error", component: "senses", event: "senses.sanctuary_read_receipt_error", message: "Sanctuary live read failed", meta: { toolName, success: false, category: error instanceof Error ? error.name : "unknown", ...(0, sanctuary_acceptance_marker_1.sanctuaryAcceptanceEventMeta)(agentName) } });
181
+ throw error;
182
+ }
183
+ };
184
+ const restart = (0, unraid_restart_1.createApprovedUnraidRestartExecutor)({
185
+ endpoint,
186
+ listContainers: reads.listContainers,
187
+ loadWriteApiKey: async () => required(machineConfig(agentName), "unraidWriteApiKey"),
188
+ persistAttempt: async (attempt) => {
189
+ persistAttempt(path.join((0, identity_1.getAgentRoot)(agentName), "state", "approvals", "unraid-restart-attempt.json"), attempt);
190
+ await appendAcceptanceAttempt(agentName, attempt);
191
+ },
192
+ acceptanceScenarioHandleDigest: () => (0, sanctuary_acceptance_marker_1.readSanctuaryAcceptanceMarker)(agentName)?.scenarioHandleDigest,
193
+ acceptanceApproval: sanctuary_acceptance_marker_1.readSanctuaryAcceptanceApproval,
194
+ reserveRoutineAction: (input) => (0, steward_policy_1.consumeRoutineActionGrant)((0, identity_1.getAgentRoot)(agentName), input),
195
+ transitionRoutineAction: (input) => (0, steward_policy_1.transitionRoutineActionReceipt)((0, identity_1.getAgentRoot)(agentName), input),
196
+ });
197
+ return {
198
+ sanctuary: {
199
+ listContainers: acceptanceRead("unraid_list_containers", reads.listContainers),
200
+ getContainerLogs: acceptanceRead("unraid_get_container_logs", reads.getContainerLogs),
201
+ getStorage: acceptanceRead("unraid_get_storage", reads.getStorage),
202
+ getDisks: acceptanceRead("unraid_get_disks", reads.getDisks),
203
+ getNotifications: acceptanceRead("unraid_get_notifications", reads.getNotifications),
204
+ getSystem: acceptanceRead("unraid_get_system", reads.getSystem),
205
+ checkServices: acceptanceRead("unraid_check_services", async () => {
206
+ const services = await Promise.all(sanctuary_health_1.SANCTUARY_PUBLIC_ENDPOINTS.map(async (url) => ({ name: new URL(url).hostname.split(".")[0], ...await (0, sanctuary_health_1.probeSanctuaryEndpoint)(url) })));
207
+ return { ok: true, data: { observedAt: new Date().toISOString(), services, degraded: services.some((service) => !service.ok) } };
208
+ }),
209
+ getDownloadQueue: acceptanceRead("sanctuary_get_download_queue", sab.readQueue),
210
+ resumeDownloadQueue: async () => {
211
+ try {
212
+ const result = await sab.resumeQueue();
213
+ collectToolResult(result);
214
+ (0, runtime_1.emitNervesEvent)({ component: "senses", event: "senses.sanctuary_download_resume_receipt", message: "Sanctuary download queue resume completed", meta: { changed: result.changed, verified: result.verified, receiptDigest: result.receiptDigest } });
215
+ return { ok: true, data: result };
216
+ }
217
+ catch (error) {
218
+ (0, runtime_1.emitNervesEvent)({ level: "error", component: "senses", event: "senses.sanctuary_download_resume_error", message: "Sanctuary download queue resume failed", meta: { category: error instanceof Error ? error.name : "unknown" } });
219
+ throw error;
220
+ }
221
+ },
222
+ restartContainer: async (args, execution) => {
223
+ const result = execution ? await restart(args, execution) : await restart(args);
224
+ collectToolResult(result);
225
+ return result;
226
+ },
227
+ recoverRoutineActions: () => (0, steward_policy_1.recoverRoutineActionReceipts)((0, identity_1.getAgentRoot)(agentName), {
228
+ observeTarget: async (target) => {
229
+ const observed = await reads.listContainers();
230
+ if (!observed.ok)
231
+ throw new Error(observed.error.message);
232
+ const matches = observed.data.containers.filter((container) => container.id === target.id && container.name === target.name);
233
+ if (matches.length !== 1)
234
+ return { ...target, state: "unknown" };
235
+ return { id: matches[0].id, name: matches[0].name, state: matches[0].state };
236
+ },
237
+ }),
238
+ },
239
+ };
240
+ }
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createSanctuarySabClient = createSanctuarySabClient;
4
+ const node_crypto_1 = require("node:crypto");
5
+ const node_fs_1 = require("node:fs");
6
+ const runtime_1 = require("../nerves/runtime");
7
+ const DEFAULT_INI_PATH = "/run/sanctuary/sabnzbd.ini";
8
+ const BASE_URL = "http://127.0.0.1:8090/api";
9
+ function apiKey(iniPath) {
10
+ let contents = "";
11
+ try {
12
+ contents = (0, node_fs_1.readFileSync)(iniPath, "utf8");
13
+ }
14
+ catch {
15
+ throw new Error("SAB queue verification credential is unavailable");
16
+ }
17
+ const value = contents.match(/^\s*api_key\s*=\s*(\S+)\s*$/mu)?.[1];
18
+ if (!value)
19
+ throw new Error("SAB queue verification credential is unavailable");
20
+ return value;
21
+ }
22
+ function queuedJobs(value) {
23
+ const parsed = typeof value === "string" && /^\d{1,7}$/u.test(value) ? Number(value) : value;
24
+ if (!Number.isSafeInteger(parsed) || Number(parsed) < 0 || Number(parsed) > 1_000_000)
25
+ throw new Error("SAB queue response is malformed");
26
+ return Number(parsed);
27
+ }
28
+ function createSanctuarySabClient(options = {}) {
29
+ const iniPath = options.iniPath ?? DEFAULT_INI_PATH;
30
+ const fetchImpl = options.fetch ?? fetch;
31
+ let cachedApiKey = null;
32
+ const request = async (mode) => {
33
+ cachedApiKey ??= apiKey(iniPath);
34
+ let response;
35
+ try {
36
+ response = await fetchImpl(`${BASE_URL}?mode=${mode}${mode === "queue" ? "&output=json" : ""}&apikey=${encodeURIComponent(cachedApiKey)}`, { signal: AbortSignal.timeout(15_000) });
37
+ }
38
+ catch {
39
+ throw new Error("SAB queue request failed");
40
+ }
41
+ if (!response.ok)
42
+ throw new Error("SAB queue request failed");
43
+ return response;
44
+ };
45
+ const readQueue = async () => {
46
+ const response = await request("queue");
47
+ const body = await response.json();
48
+ if (!body || typeof body !== "object" || Array.isArray(body))
49
+ throw new Error("SAB queue response is malformed");
50
+ const queue = body.queue;
51
+ if (!queue || typeof queue !== "object" || Array.isArray(queue))
52
+ throw new Error("SAB queue response is malformed");
53
+ const fields = queue;
54
+ if (typeof fields.paused !== "boolean" || (fields.status !== undefined && (typeof fields.status !== "string" || Buffer.byteLength(fields.status) > 64)))
55
+ throw new Error("SAB queue response is malformed");
56
+ const status = typeof fields.status === "string" ? fields.status : "unknown";
57
+ const jobs = fields.noofslots === undefined ? 0 : queuedJobs(fields.noofslots);
58
+ const observedAt = options.now?.() ?? new Date().toISOString();
59
+ const stateDigest = (0, node_crypto_1.createHash)("sha256").update(JSON.stringify({ paused: fields.paused, status, queuedJobs: jobs })).digest("hex");
60
+ return { paused: fields.paused, status, queuedJobs: jobs, observedAt, stateDigest };
61
+ };
62
+ return {
63
+ readQueue,
64
+ async resumeQueue() {
65
+ const before = await readQueue();
66
+ if (!before.paused) {
67
+ const receiptDigest = (0, node_crypto_1.createHash)("sha256").update(`sabnzbd.resume\0${before.stateDigest}\0${before.stateDigest}`).digest("hex");
68
+ return { changed: false, before, after: before, verified: true, receiptDigest };
69
+ }
70
+ const response = await request("resume");
71
+ await response.body?.cancel().catch(() => undefined);
72
+ const after = await readQueue();
73
+ if (after.paused)
74
+ throw new Error("SAB queue resume could not be verified");
75
+ const receiptDigest = (0, node_crypto_1.createHash)("sha256").update(`sabnzbd.resume\0${before.stateDigest}\0${after.stateDigest}`).digest("hex");
76
+ return { changed: true, before, after, verified: true, receiptDigest };
77
+ },
78
+ };
79
+ }
80
+ (0, runtime_1.emitNervesEvent)({ component: "senses", event: "senses.sanctuary_sab_loaded", message: "typed Sanctuary SAB queue client loaded", meta: { operations: 2 } });