@ouro.bot/cli 0.1.0-alpha.92 → 0.1.0-alpha.921

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 (717) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +154 -23
  3. package/RepairGuide.ouro/agent.json +5 -0
  4. package/RepairGuide.ouro/psyche/IDENTITY.md +19 -0
  5. package/RepairGuide.ouro/psyche/SOUL.md +55 -0
  6. package/RepairGuide.ouro/skills/diagnose-broken-remote.md +63 -0
  7. package/RepairGuide.ouro/skills/diagnose-stacked-typed-issues.md +35 -0
  8. package/RepairGuide.ouro/skills/diagnose-sync-blocked.md +54 -0
  9. package/RepairGuide.ouro/skills/diagnose-vault-expired.md +60 -0
  10. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +4 -2
  11. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +2 -2
  12. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +1 -1
  13. package/assets/bluebubbles-host +264 -0
  14. package/assets/sanctuary-host-launcher.sh +16 -0
  15. package/changelog.json +5826 -16
  16. package/deploy/unraid/Dockerfile +58 -0
  17. package/deploy/unraid/README.txt +2997 -0
  18. package/deploy/unraid/audit-container-spec.sh +4 -0
  19. package/deploy/unraid/container-runtime.json +1 -0
  20. package/deploy/unraid/docker-man-template-transaction.mjs +711 -0
  21. package/deploy/unraid/docker-man-template-xml.cjs +105 -0
  22. package/deploy/unraid/migrate-sanctuary-bundle.mjs +60 -0
  23. package/deploy/unraid/ouro-events/bootstrap-spool.sh +92 -0
  24. package/deploy/unraid/ouro-events/emit-event.mjs +310 -0
  25. package/deploy/unraid/ouro-events/emit-usenet-event.sh +54 -0
  26. package/deploy/unraid/ouro-events/install-usenet-guard.sh +435 -0
  27. package/deploy/unraid/ouro-events/usenet-health.sh +176 -0
  28. package/deploy/unraid/sanctuary-acceptance-adapter.sh +43 -0
  29. package/deploy/unraid/sanctuary-acceptance-contract.json +198 -0
  30. package/deploy/unraid/sanctuary-acceptance-harness.sh +23 -0
  31. package/deploy/unraid/sanctuary-authority-installation.json +36 -0
  32. package/deploy/unraid/sanctuary-authority-service.sh +30 -0
  33. package/deploy/unraid/sanctuary-butler-upgrade.mjs +1254 -0
  34. package/deploy/unraid/sanctuary-deployment-target.mjs +710 -0
  35. package/deploy/unraid/sanctuary-replay-gate.mjs +1564 -0
  36. package/deploy/unraid/sanctuary-safe-rename.py +24 -0
  37. package/deploy/unraid/sanctuary-unit16-host-broker.mjs +2086 -0
  38. package/deploy/unraid/sanctuary-unit16-run.sh +560 -0
  39. package/deploy/unraid/sanctuary-unit18-target-audit.sh +27 -0
  40. package/deploy/unraid/sanctuary.ouro/agent.json +66 -0
  41. package/deploy/unraid/sanctuary.ouro/arc/README.md +3 -0
  42. package/deploy/unraid/sanctuary.ouro/awaiting/house-care-sweep.md +17 -0
  43. package/deploy/unraid/sanctuary.ouro/bundle-meta.json +5 -0
  44. package/deploy/unraid/sanctuary.ouro/habits/sanctuary-health.md +8 -0
  45. package/deploy/unraid/sanctuary.ouro/mcp/media-mcp.mjs +2231 -0
  46. package/deploy/unraid/sanctuary.ouro/provider-readiness.json +11 -0
  47. package/deploy/unraid/sanctuary.ouro/psyche/ASPIRATIONS.md +5 -0
  48. package/deploy/unraid/sanctuary.ouro/psyche/IDENTITY.md +5 -0
  49. package/deploy/unraid/sanctuary.ouro/psyche/LORE.md +33 -0
  50. package/deploy/unraid/sanctuary.ouro/psyche/SOUL.md +27 -0
  51. package/deploy/unraid/sanctuary.ouro/psyche/TACIT.md +22 -0
  52. package/deploy/unraid/sanctuary.ouro/state/policy/steward.json +7 -0
  53. package/deploy/unraid/sanctuary.ouro/tool-profiles.json +232 -0
  54. package/deploy/unraid/sanctuary.xml +30 -0
  55. package/dist/a2a/card.js +57 -0
  56. package/dist/a2a/client.js +250 -0
  57. package/dist/a2a/config.js +50 -0
  58. package/dist/a2a/delegated-command-grants.js +93 -0
  59. package/dist/a2a/delegated-command.js +197 -0
  60. package/dist/a2a/delegation-stores.js +56 -0
  61. package/dist/a2a/did-resolution.js +93 -0
  62. package/dist/a2a/escalation-grants.js +84 -0
  63. package/dist/a2a/escalation-wiring.js +60 -0
  64. package/dist/a2a/identity.js +258 -0
  65. package/dist/a2a/inbound-share.js +110 -0
  66. package/dist/a2a/mission-result-wire.js +196 -0
  67. package/dist/a2a/onboarding.js +133 -0
  68. package/dist/a2a/operator-trust.js +382 -0
  69. package/dist/a2a/outbox-client.js +57 -0
  70. package/dist/a2a/outbox-store.js +188 -0
  71. package/dist/a2a/outbox-wire.js +95 -0
  72. package/dist/a2a/pin-store.js +132 -0
  73. package/dist/a2a/replay-grant-holders.js +33 -0
  74. package/dist/a2a/replay-harness.js +188 -0
  75. package/dist/a2a/resolution-proof.js +53 -0
  76. package/dist/a2a/sealed-chat.js +113 -0
  77. package/dist/a2a/seen-ledger.js +120 -0
  78. package/dist/a2a/server.js +771 -0
  79. package/dist/a2a/task-store.js +69 -0
  80. package/dist/a2a/transport.js +45 -0
  81. package/dist/a2a/trust-commands.js +203 -0
  82. package/dist/a2a/trusted-files.js +185 -0
  83. package/dist/a2a/types.js +3 -0
  84. package/dist/arc/attention-types.js +8 -0
  85. package/dist/arc/cares.js +360 -0
  86. package/dist/arc/episodes.js +118 -0
  87. package/dist/arc/evolution.js +490 -0
  88. package/dist/arc/flight-recorder.js +1034 -0
  89. package/dist/arc/intentions.js +134 -0
  90. package/dist/arc/json-store.js +117 -0
  91. package/dist/arc/obligations.js +386 -0
  92. package/dist/arc/packets.js +336 -0
  93. package/dist/arc/presence.js +185 -0
  94. package/dist/arc/task-lifecycle.js +57 -0
  95. package/dist/commerce/store.js +755 -0
  96. package/dist/commerce/types.js +3 -0
  97. package/dist/heart/active-work.js +798 -40
  98. package/dist/heart/agent-entry.js +252 -3
  99. package/dist/heart/approval-files.js +113 -0
  100. package/dist/heart/approval-store.js +960 -0
  101. package/dist/heart/attachments/image-normalize.js +194 -0
  102. package/dist/heart/attachments/materialize.js +97 -0
  103. package/dist/heart/attachments/originals.js +88 -0
  104. package/dist/heart/attachments/render.js +29 -0
  105. package/dist/heart/attachments/sources/bluebubbles.js +156 -0
  106. package/dist/heart/attachments/sources/cli-local-file.js +78 -0
  107. package/dist/heart/attachments/sources/index.js +18 -0
  108. package/dist/heart/attachments/sources/telegram.js +71 -0
  109. package/dist/heart/attachments/store.js +132 -0
  110. package/dist/heart/attachments/types.js +93 -0
  111. package/dist/heart/auth/auth-flow.js +495 -0
  112. package/dist/heart/autonomy-budget.js +478 -0
  113. package/dist/heart/awaiting/a2a-await-delivery.js +109 -0
  114. package/dist/heart/awaiting/await-alert.js +152 -0
  115. package/dist/heart/awaiting/await-expiry.js +143 -0
  116. package/dist/heart/awaiting/await-loader.js +91 -0
  117. package/dist/heart/awaiting/await-parser.js +152 -0
  118. package/dist/heart/awaiting/await-runtime-state.js +111 -0
  119. package/dist/heart/awaiting/await-scheduler.js +391 -0
  120. package/dist/heart/background-operations.js +281 -0
  121. package/dist/heart/bluebubbles-host-account.js +78 -0
  122. package/dist/heart/bridges/manager.js +137 -17
  123. package/dist/heart/bridges/store.js +14 -2
  124. package/dist/heart/bundle-state.js +168 -0
  125. package/dist/heart/commitments.js +44 -18
  126. package/dist/heart/config-registry.js +331 -0
  127. package/dist/heart/config.js +174 -131
  128. package/dist/heart/context-loss-gauntlet.js +387 -0
  129. package/dist/heart/context-loss-sentinel.js +1159 -0
  130. package/dist/heart/core.js +2364 -578
  131. package/dist/heart/cross-chat-delivery.js +9 -22
  132. package/dist/heart/daemon/agent-config-check.js +451 -0
  133. package/dist/heart/daemon/agent-discovery.js +283 -3
  134. package/dist/heart/daemon/agent-home.js +319 -0
  135. package/dist/heart/daemon/agent-service.js +542 -0
  136. package/dist/heart/daemon/agentic-repair.js +547 -0
  137. package/dist/heart/daemon/await-private-wake.js +59 -0
  138. package/dist/heart/daemon/bluebubbles-cli-integration.js +71 -0
  139. package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
  140. package/dist/heart/daemon/bluebubbles-host-protocol.js +433 -0
  141. package/dist/heart/daemon/bluebubbles-host.js +273 -0
  142. package/dist/heart/daemon/boot-sync-probe.js +197 -0
  143. package/dist/heart/daemon/cadence.js +180 -0
  144. package/dist/heart/daemon/cli-arg-redaction.js +26 -0
  145. package/dist/heart/daemon/cli-defaults.js +876 -0
  146. package/dist/heart/daemon/cli-desk.js +322 -0
  147. package/dist/heart/daemon/cli-exec.js +9199 -0
  148. package/dist/heart/daemon/cli-help.js +735 -0
  149. package/dist/heart/daemon/cli-parse.js +3211 -0
  150. package/dist/heart/daemon/cli-render-doctor.js +57 -0
  151. package/dist/heart/daemon/cli-render.js +819 -0
  152. package/dist/heart/daemon/cli-types.js +8 -0
  153. package/dist/heart/daemon/connect-bay.js +323 -0
  154. package/dist/heart/daemon/container-credential-bootstrap.js +273 -0
  155. package/dist/heart/daemon/container-healthcheck.js +112 -0
  156. package/dist/heart/daemon/container-runtime.js +97 -0
  157. package/dist/heart/daemon/container-spec-auditor-main.js +171 -0
  158. package/dist/heart/daemon/container-spec-auditor.js +235 -0
  159. package/dist/heart/daemon/daemon-bootstrap-startup.js +115 -0
  160. package/dist/heart/daemon/daemon-cli.js +29 -2350
  161. package/dist/heart/daemon/daemon-entry.js +953 -11
  162. package/dist/heart/daemon/daemon-health.js +186 -0
  163. package/dist/heart/daemon/daemon-rollup.js +57 -0
  164. package/dist/heart/daemon/daemon-runtime-sync.js +88 -13
  165. package/dist/heart/daemon/daemon-tombstone.js +236 -0
  166. package/dist/heart/daemon/daemon.js +2084 -105
  167. package/dist/heart/daemon/dns-workflow.js +394 -0
  168. package/dist/heart/daemon/doctor-types.js +8 -0
  169. package/dist/heart/daemon/doctor.js +1766 -0
  170. package/dist/heart/daemon/freshness.js +345 -0
  171. package/dist/heart/daemon/habit-private-wake.js +61 -0
  172. package/dist/heart/daemon/health-monitor.js +154 -11
  173. package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
  174. package/dist/heart/daemon/hooks/bundle-meta.js +135 -1
  175. package/dist/heart/daemon/http-health-probe.js +80 -0
  176. package/dist/heart/daemon/human-command-screens.js +234 -0
  177. package/dist/heart/daemon/human-readiness.js +114 -0
  178. package/dist/heart/daemon/inner-status.js +78 -0
  179. package/dist/heart/daemon/interactive-repair.js +394 -0
  180. package/dist/heart/daemon/launchd.js +37 -8
  181. package/dist/heart/daemon/log-tailer.js +79 -10
  182. package/dist/heart/daemon/logs-prune.js +132 -0
  183. package/dist/heart/daemon/mcp-canary.js +524 -0
  184. package/dist/heart/daemon/message-router.js +65 -5
  185. package/dist/heart/daemon/migrate-to-desk.js +848 -0
  186. package/dist/heart/daemon/os-cron-deps.js +135 -0
  187. package/dist/heart/daemon/os-cron.js +22 -14
  188. package/dist/heart/daemon/ouro-bot-entry.js +6 -3
  189. package/dist/heart/daemon/ouro-entry.js +5 -2
  190. package/dist/heart/daemon/plugin-cli.js +432 -0
  191. package/dist/heart/daemon/process-manager.js +760 -61
  192. package/dist/heart/daemon/provider-discovery.js +137 -0
  193. package/dist/heart/daemon/provider-ping-progress.js +83 -0
  194. package/dist/heart/daemon/prunable-bundle.js +144 -0
  195. package/dist/heart/daemon/pulse.js +511 -0
  196. package/dist/heart/daemon/readiness-repair.js +365 -0
  197. package/dist/heart/daemon/run-hooks.js +2 -0
  198. package/dist/heart/daemon/runtime-logging.js +47 -14
  199. package/dist/heart/daemon/runtime-metadata.js +2 -30
  200. package/dist/heart/daemon/runtime-mode.js +13 -2
  201. package/dist/heart/daemon/safe-mode.js +161 -0
  202. package/dist/heart/daemon/sanctuary-acceptance-adapter.js +2654 -0
  203. package/dist/heart/daemon/sanctuary-acceptance-harness.js +1830 -0
  204. package/dist/heart/daemon/sanctuary-acceptance-marker.js +267 -0
  205. package/dist/heart/daemon/sanctuary-acceptance-scenarios.js +955 -0
  206. package/dist/heart/daemon/sanctuary-authority-codec.js +86 -0
  207. package/dist/heart/daemon/sanctuary-authority-epoch.js +281 -0
  208. package/dist/heart/daemon/sanctuary-authority-installation.js +161 -0
  209. package/dist/heart/daemon/sanctuary-authority-ledger.js +241 -0
  210. package/dist/heart/daemon/sanctuary-authority-root-lifecycle.js +1267 -0
  211. package/dist/heart/daemon/sanctuary-authority-vault-migration.js +79 -0
  212. package/dist/heart/daemon/sanctuary-bundle-migration.js +730 -0
  213. package/dist/heart/daemon/sanctuary-host-authority.js +1008 -0
  214. package/dist/heart/daemon/sanctuary-host-detached-supervisor.js +493 -0
  215. package/dist/heart/daemon/sanctuary-host-executor.js +670 -0
  216. package/dist/heart/daemon/sanctuary-host-linux-kernel.js +334 -0
  217. package/dist/heart/daemon/sanctuary-host-supervisor-entry.js +207 -0
  218. package/dist/heart/daemon/sanctuary-host-supervisor.js +95 -0
  219. package/dist/heart/daemon/sanctuary-package-management.js +103 -0
  220. package/dist/heart/daemon/sanctuary-scheduler-liveness.js +227 -0
  221. package/dist/heart/daemon/sanctuary-scheduler-origin.js +132 -0
  222. package/dist/heart/daemon/sanctuary-telegram-authority-entry.js +475 -0
  223. package/dist/heart/daemon/sanctuary-telegram-authority-gateway.js +585 -0
  224. package/dist/heart/daemon/sanctuary-telegram-authority-service.js +676 -0
  225. package/dist/heart/daemon/sense-manager.js +733 -48
  226. package/dist/heart/daemon/session-id-resolver.js +131 -0
  227. package/dist/heart/daemon/skill-management-installer.js +1 -1
  228. package/dist/heart/daemon/socket-client.js +180 -12
  229. package/dist/heart/daemon/stale-bundle-prune.js +113 -0
  230. package/dist/heart/daemon/startup-tui.js +339 -0
  231. package/dist/heart/daemon/supercronic-supervisor.js +282 -0
  232. package/dist/heart/daemon/task-scheduler.js +117 -39
  233. package/dist/heart/daemon/terminal-ui.js +503 -0
  234. package/dist/heart/daemon/thoughts.js +138 -54
  235. package/dist/heart/daemon/up-progress.js +366 -0
  236. package/dist/heart/daemon/vault-items.js +56 -0
  237. package/dist/heart/delegation.js +1 -4
  238. package/dist/heart/external-events/router.js +1516 -0
  239. package/dist/heart/failure-reports.js +336 -0
  240. package/dist/heart/file-lock.js +109 -0
  241. package/dist/heart/frontend-approval-runtime.js +506 -0
  242. package/dist/heart/frontend-journal.js +215 -0
  243. package/dist/heart/frontend-session-service.js +237 -0
  244. package/dist/heart/frontend-socket-client.js +164 -0
  245. package/dist/heart/frontend-socket.js +400 -0
  246. package/dist/heart/habits/habit-cancel.js +810 -0
  247. package/dist/heart/habits/habit-lifecycle.js +1327 -0
  248. package/dist/heart/habits/habit-migration.js +194 -0
  249. package/dist/heart/habits/habit-parser.js +278 -0
  250. package/dist/heart/habits/habit-runtime-state.js +93 -0
  251. package/dist/heart/habits/habit-scheduler.js +653 -0
  252. package/dist/heart/habits/habit-session-summary.js +318 -0
  253. package/dist/heart/habits/habit-session.js +636 -0
  254. package/dist/heart/{daemon → hatch}/hatch-flow.js +55 -62
  255. package/dist/heart/{daemon → hatch}/hatch-specialist.js +6 -8
  256. package/dist/heart/{daemon → hatch}/specialist-prompt.js +12 -9
  257. package/dist/heart/{daemon → hatch}/specialist-tools.js +53 -18
  258. package/dist/heart/identity.js +230 -59
  259. package/dist/heart/kept-notes.js +289 -0
  260. package/dist/heart/kicks.js +1 -1
  261. package/dist/heart/machine-identity.js +175 -0
  262. package/dist/heart/mail-import-discovery.js +356 -0
  263. package/dist/heart/mailbox/mailbox-http-hooks.js +98 -0
  264. package/dist/heart/mailbox/mailbox-http-response.js +7 -0
  265. package/dist/heart/mailbox/mailbox-http-routes.js +398 -0
  266. package/dist/heart/mailbox/mailbox-http-static.js +103 -0
  267. package/dist/heart/mailbox/mailbox-http-transport.js +272 -0
  268. package/dist/heart/mailbox/mailbox-http.js +99 -0
  269. package/dist/heart/mailbox/mailbox-read.js +39 -0
  270. package/dist/heart/mailbox/mailbox-types.js +27 -0
  271. package/dist/heart/mailbox/mailbox-view.js +197 -0
  272. package/dist/heart/mailbox/readers/agent-machine.js +428 -0
  273. package/dist/heart/mailbox/readers/continuity-readers.js +329 -0
  274. package/dist/heart/mailbox/readers/mail.js +375 -0
  275. package/dist/heart/mailbox/readers/runtime-readers.js +823 -0
  276. package/dist/heart/mailbox/readers/sessions.js +231 -0
  277. package/dist/heart/mailbox/readers/shared.js +111 -0
  278. package/dist/heart/mcp/mcp-server.js +696 -0
  279. package/dist/heart/migrate-config.js +100 -0
  280. package/dist/heart/model-capabilities.js +83 -16
  281. package/dist/heart/orientation-frame.js +345 -0
  282. package/dist/heart/platform.js +81 -0
  283. package/dist/heart/primitives.js +1 -1
  284. package/dist/heart/private-runtime/decision-renderer.js +158 -0
  285. package/dist/heart/private-runtime/index.js +18 -0
  286. package/dist/heart/private-runtime/ledger.js +221 -0
  287. package/dist/heart/private-runtime/policy.js +260 -0
  288. package/dist/heart/private-runtime/types.js +2 -0
  289. package/dist/heart/progress-story.js +1 -1
  290. package/dist/heart/provider-attempt.js +137 -0
  291. package/dist/heart/provider-binding-resolver.js +273 -0
  292. package/dist/heart/provider-credentials.js +449 -0
  293. package/dist/heart/provider-failover.js +311 -0
  294. package/dist/heart/provider-models.js +96 -0
  295. package/dist/heart/provider-ping.js +265 -0
  296. package/dist/heart/provider-readiness-cache.js +119 -0
  297. package/dist/heart/provider-visibility.js +188 -0
  298. package/dist/heart/providers/anthropic-token.js +131 -0
  299. package/dist/heart/providers/anthropic.js +173 -60
  300. package/dist/heart/providers/azure.js +23 -11
  301. package/dist/heart/providers/error-classification.js +127 -0
  302. package/dist/heart/providers/github-copilot.js +38 -39
  303. package/dist/heart/providers/minimax-vlm.js +189 -0
  304. package/dist/heart/providers/minimax.js +26 -8
  305. package/dist/heart/providers/openai-codex-token.js +349 -0
  306. package/dist/heart/providers/openai-codex.js +55 -40
  307. package/dist/heart/providers/openai-compatible.js +177 -0
  308. package/dist/heart/reply-style.js +52 -0
  309. package/dist/heart/run-ledger.js +247 -0
  310. package/dist/heart/runtime-capability-check.js +170 -0
  311. package/dist/heart/runtime-credentials.js +607 -0
  312. package/dist/heart/runtime-cwd.js +87 -0
  313. package/dist/heart/sense-truth.js +24 -4
  314. package/dist/heart/session-activity.js +92 -24
  315. package/dist/heart/session-events.js +1504 -0
  316. package/dist/heart/session-playback-cli-main.js +5 -0
  317. package/dist/heart/session-playback-cli.js +36 -0
  318. package/dist/heart/session-playback.js +231 -0
  319. package/dist/heart/session-redaction-repair-cli-main.js +31 -0
  320. package/dist/heart/session-redaction-repair.js +552 -0
  321. package/dist/heart/session-stats-cli-main.js +5 -0
  322. package/dist/heart/session-stats.js +183 -0
  323. package/dist/heart/session-transcript.js +133 -0
  324. package/dist/heart/shelf-claims.js +78 -0
  325. package/dist/heart/shepherd-config.js +113 -0
  326. package/dist/heart/source-grounding.js +297 -0
  327. package/dist/heart/start-of-turn-packet.js +372 -0
  328. package/dist/heart/steward-policy.js +656 -0
  329. package/dist/heart/streaming.js +609 -207
  330. package/dist/heart/structured-output.js +196 -0
  331. package/dist/heart/sync-classification.js +176 -0
  332. package/dist/heart/sync-health.js +152 -0
  333. package/dist/heart/sync.js +726 -0
  334. package/dist/heart/target-resolution.js +11 -7
  335. package/dist/heart/tempo.js +93 -0
  336. package/dist/heart/temporal-view.js +41 -0
  337. package/dist/heart/timeouts.js +101 -0
  338. package/dist/heart/tool-activity-callbacks.js +59 -0
  339. package/dist/heart/tool-approval.js +382 -0
  340. package/dist/heart/tool-description.js +155 -0
  341. package/dist/heart/tool-friction.js +55 -0
  342. package/dist/heart/tool-loop.js +206 -0
  343. package/dist/heart/turn-context.js +496 -0
  344. package/dist/heart/turn-execution-lease.js +30 -0
  345. package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +6 -5
  346. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +3 -2
  347. package/dist/heart/versioning/ouro-path-installer.js +432 -0
  348. package/dist/heart/versioning/ouro-recovery-launcher.js +76 -0
  349. package/dist/heart/versioning/ouro-version-manager.js +432 -0
  350. package/dist/heart/versioning/release-sources.js +173 -0
  351. package/dist/heart/versioning/self-update-defaults.js +294 -0
  352. package/dist/heart/versioning/self-update.js +196 -0
  353. package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
  354. package/dist/heart/{daemon → versioning}/update-checker.js +6 -1
  355. package/dist/heart/versioning/update-hooks.js +154 -0
  356. package/dist/heart/versioning/update-state.js +126 -0
  357. package/dist/heart/versioning/updater-launchd.js +150 -0
  358. package/dist/heart/versioning/version-intent.js +114 -0
  359. package/dist/heart/work-card.js +380 -0
  360. package/dist/mailbox-ui/assets/index-Cyv4mw3Y.js +1 -0
  361. package/dist/mailbox-ui/assets/index-Du_9G9WO.css +1 -0
  362. package/dist/mailbox-ui/assets/vendor-CcN1XpQ9.js +61 -0
  363. package/dist/mailbox-ui/index.html +16 -0
  364. package/dist/mailroom/attention.js +167 -0
  365. package/dist/mailroom/autonomy.js +209 -0
  366. package/dist/mailroom/blob-store.js +889 -0
  367. package/dist/mailroom/body-cache.js +61 -0
  368. package/dist/mailroom/cache-sync-cli.js +58 -0
  369. package/dist/mailroom/core.js +803 -0
  370. package/dist/mailroom/entry.js +160 -0
  371. package/dist/mailroom/file-store.js +568 -0
  372. package/dist/mailroom/hosted-cache-sync.js +384 -0
  373. package/dist/mailroom/mbox-import.js +393 -0
  374. package/dist/mailroom/migration.js +164 -0
  375. package/dist/mailroom/outbound.js +380 -0
  376. package/dist/mailroom/policy.js +263 -0
  377. package/dist/mailroom/reader.js +292 -0
  378. package/dist/mailroom/search-cache.js +513 -0
  379. package/dist/mailroom/search-relevance.js +319 -0
  380. package/dist/mailroom/smtp-ingress.js +176 -0
  381. package/dist/mailroom/source-state.js +176 -0
  382. package/dist/mailroom/thread.js +109 -0
  383. package/dist/mailroom/travel-extract.js +89 -0
  384. package/dist/mind/bundle-manifest.js +24 -2
  385. package/dist/mind/context.js +307 -139
  386. package/dist/mind/desk-section.js +362 -0
  387. package/dist/mind/diary-integrity.js +60 -0
  388. package/dist/mind/{memory.js → diary.js} +68 -77
  389. package/dist/mind/embedding-provider.js +60 -0
  390. package/dist/mind/file-state.js +179 -0
  391. package/dist/mind/friend-link-codes.js +209 -0
  392. package/dist/mind/friend-merge.js +330 -0
  393. package/dist/mind/{associative-recall.js → note-search.js} +47 -58
  394. package/dist/mind/obligation-steering.js +199 -3
  395. package/dist/mind/pending.js +36 -10
  396. package/dist/mind/prompt-budget.js +479 -0
  397. package/dist/mind/prompt-refresh.js +3 -2
  398. package/dist/mind/prompt.js +1085 -198
  399. package/dist/mind/provenance-trust.js +26 -0
  400. package/dist/mind/record-paths.js +312 -0
  401. package/dist/mind/scrutiny.js +173 -0
  402. package/dist/mind/session-transaction.js +564 -0
  403. package/dist/nerves/cli-logging.js +7 -1
  404. package/dist/nerves/coverage/audit-rules.js +15 -6
  405. package/dist/nerves/coverage/audit.js +28 -2
  406. package/dist/nerves/coverage/cli.js +1 -1
  407. package/dist/nerves/coverage/contract.js +5 -5
  408. package/dist/nerves/coverage/file-completeness.js +139 -5
  409. package/dist/nerves/coverage/run-artifacts.js +55 -35
  410. package/dist/nerves/coverage/source-scanner.js +4 -4
  411. package/dist/nerves/event-buffer.js +111 -0
  412. package/dist/nerves/index.js +293 -9
  413. package/dist/nerves/observation.js +20 -0
  414. package/dist/nerves/redact.js +79 -0
  415. package/dist/nerves/review/cli-main.js +5 -0
  416. package/dist/nerves/review/cli.js +156 -0
  417. package/dist/nerves/review/core.js +159 -0
  418. package/dist/nerves/runtime.js +17 -1
  419. package/dist/repertoire/ado-client.js +15 -56
  420. package/dist/repertoire/ado-semantic.js +16 -10
  421. package/dist/repertoire/api-client.js +97 -0
  422. package/dist/repertoire/bitwarden-store.js +1201 -0
  423. package/dist/repertoire/bundle-templates.js +71 -0
  424. package/dist/repertoire/bw-installer.js +231 -0
  425. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  426. package/dist/repertoire/coding/context-pack.js +331 -0
  427. package/dist/repertoire/coding/feedback.js +174 -32
  428. package/dist/repertoire/coding/index.js +15 -3
  429. package/dist/repertoire/coding/manager.js +159 -10
  430. package/dist/repertoire/coding/spawner.js +40 -11
  431. package/dist/repertoire/coding/tools.js +182 -18
  432. package/dist/repertoire/coding/workbench-client.js +272 -0
  433. package/dist/repertoire/coding/workbench-manager.js +346 -0
  434. package/dist/repertoire/commerce-errors.js +109 -0
  435. package/dist/repertoire/commerce-self-test.js +156 -0
  436. package/dist/repertoire/credential-access.js +183 -0
  437. package/dist/repertoire/desk/classifier.js +362 -0
  438. package/dist/repertoire/duffel-client.js +185 -0
  439. package/dist/repertoire/github-client.js +14 -55
  440. package/dist/repertoire/graph-client.js +11 -52
  441. package/dist/repertoire/guardrails.js +968 -20
  442. package/dist/repertoire/house-sweep.js +469 -0
  443. package/dist/repertoire/mcp-client.js +58 -5
  444. package/dist/repertoire/mcp-manager.js +427 -139
  445. package/dist/repertoire/mcp-tools.js +132 -0
  446. package/dist/repertoire/mcp-write-guard.js +46 -0
  447. package/dist/repertoire/plugin-mcp.js +175 -0
  448. package/dist/repertoire/plugins.js +253 -0
  449. package/dist/repertoire/relationship-authorization.js +322 -0
  450. package/dist/repertoire/sanctuary-container-policy.js +46 -0
  451. package/dist/repertoire/shell-sessions.js +134 -0
  452. package/dist/repertoire/skills.js +48 -4
  453. package/dist/repertoire/stripe-client.js +131 -0
  454. package/dist/repertoire/tool-arguments.js +182 -0
  455. package/dist/repertoire/tool-results.js +29 -0
  456. package/dist/repertoire/tools-a2a.js +699 -0
  457. package/dist/repertoire/tools-attachments.js +322 -0
  458. package/dist/repertoire/tools-awaiting.js +872 -0
  459. package/dist/repertoire/tools-base.js +83 -1168
  460. package/dist/repertoire/tools-bluebubbles.js +2 -0
  461. package/dist/repertoire/tools-bridge.js +144 -0
  462. package/dist/repertoire/tools-bundle.js +995 -0
  463. package/dist/repertoire/tools-commerce.js +253 -0
  464. package/dist/repertoire/tools-config.js +186 -0
  465. package/dist/repertoire/tools-continuity.js +575 -0
  466. package/dist/repertoire/tools-credential.js +383 -0
  467. package/dist/repertoire/tools-escalation.js +84 -0
  468. package/dist/repertoire/tools-evolution.js +527 -0
  469. package/dist/repertoire/tools-files.js +344 -0
  470. package/dist/repertoire/tools-flight.js +290 -0
  471. package/dist/repertoire/tools-flow.js +119 -0
  472. package/dist/repertoire/tools-friend-link.js +57 -0
  473. package/dist/repertoire/tools-github.js +3 -8
  474. package/dist/repertoire/tools-habits.js +103 -0
  475. package/dist/repertoire/tools-house-care.js +174 -0
  476. package/dist/repertoire/tools-mail.js +1828 -0
  477. package/dist/repertoire/tools-notes.js +496 -0
  478. package/dist/repertoire/tools-obligations.js +182 -0
  479. package/dist/repertoire/tools-orientation.js +31 -0
  480. package/dist/repertoire/tools-record.js +482 -0
  481. package/dist/repertoire/tools-rsvp.js +139 -0
  482. package/dist/repertoire/tools-runtime.js +238 -0
  483. package/dist/repertoire/tools-sanctuary-host.js +202 -0
  484. package/dist/repertoire/tools-session.js +1133 -0
  485. package/dist/repertoire/tools-shell.js +149 -0
  486. package/dist/repertoire/tools-shepherd.js +262 -0
  487. package/dist/repertoire/tools-steward-policy.js +147 -0
  488. package/dist/repertoire/tools-stripe.js +224 -0
  489. package/dist/repertoire/tools-surface.js +369 -0
  490. package/dist/repertoire/tools-teams.js +12 -39
  491. package/dist/repertoire/tools-telegram-contacts.js +37 -0
  492. package/dist/repertoire/tools-travel.js +125 -0
  493. package/dist/repertoire/tools-trip.js +982 -0
  494. package/dist/repertoire/tools-unraid.js +556 -0
  495. package/dist/repertoire/tools-user-profile.js +146 -0
  496. package/dist/repertoire/tools-vault.js +40 -0
  497. package/dist/repertoire/tools-voice-local.js +198 -0
  498. package/dist/repertoire/tools-voice.js +145 -0
  499. package/dist/repertoire/tools.js +514 -156
  500. package/dist/repertoire/travel-api-client.js +360 -0
  501. package/dist/repertoire/unraid-client.js +155 -0
  502. package/dist/repertoire/unraid-restart.js +339 -0
  503. package/dist/repertoire/user-profile.js +131 -0
  504. package/dist/repertoire/vault-setup.js +246 -0
  505. package/dist/repertoire/vault-unlock.js +686 -0
  506. package/dist/rsvp/aisleplanner-client.js +354 -0
  507. package/dist/rsvp/cli.js +936 -0
  508. package/dist/rsvp/config.js +327 -0
  509. package/dist/rsvp/cutover.js +433 -0
  510. package/dist/rsvp/diagnostics.js +232 -0
  511. package/dist/rsvp/diff-renderer.js +176 -0
  512. package/dist/rsvp/habit-policy.js +110 -0
  513. package/dist/rsvp/habit-stage.js +123 -0
  514. package/dist/rsvp/incident-bundle.js +97 -0
  515. package/dist/rsvp/migration.js +204 -0
  516. package/dist/rsvp/native-habit-runner.js +630 -0
  517. package/dist/rsvp/outbound-state.js +628 -0
  518. package/dist/rsvp/query.js +118 -0
  519. package/dist/rsvp/replay.js +233 -0
  520. package/dist/rsvp/snapshot.js +232 -0
  521. package/dist/rsvp/spend-ledger.js +175 -0
  522. package/dist/scripts/claude-code-hook.js +41 -0
  523. package/dist/scripts/claude-code-stop-hook.js +47 -0
  524. package/dist/senses/a2a-entry.js +132 -0
  525. package/dist/senses/acp-server.js +386 -0
  526. package/dist/senses/attention-queue.js +186 -0
  527. package/dist/senses/await-turn-message.js +65 -0
  528. package/dist/senses/bluebubbles/active-turns.js +216 -0
  529. package/dist/senses/bluebubbles/attachment-cache.js +53 -0
  530. package/dist/senses/bluebubbles/attachment-download.js +137 -0
  531. package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +379 -41
  532. package/dist/senses/bluebubbles/context-packet.js +207 -0
  533. package/dist/senses/bluebubbles/context-smoke.js +144 -0
  534. package/dist/senses/bluebubbles/entry.js +90 -0
  535. package/dist/senses/bluebubbles/host-account-verifier.js +88 -0
  536. package/dist/senses/{bluebubbles-inbound-log.js → bluebubbles/inbound-log.js} +21 -3
  537. package/dist/senses/bluebubbles/index.js +3878 -0
  538. package/dist/senses/bluebubbles/latest-turn.js +311 -0
  539. package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -71
  540. package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +159 -28
  541. package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +24 -13
  542. package/dist/senses/bluebubbles/outbound-state.js +308 -0
  543. package/dist/senses/bluebubbles/processed-log.js +133 -0
  544. package/dist/senses/bluebubbles/reaction-policy.js +47 -0
  545. package/dist/senses/bluebubbles/replay.js +284 -0
  546. package/dist/senses/{bluebubbles-runtime-state.js → bluebubbles/runtime-state.js} +30 -2
  547. package/dist/senses/bluebubbles/semantic-receipts.js +1986 -0
  548. package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
  549. package/dist/senses/bluebubbles/webhook-registration.js +257 -0
  550. package/dist/senses/bluebubbles-meta-guard.js +39 -0
  551. package/dist/senses/cli/bracketed-paste.js +82 -0
  552. package/dist/senses/cli/image-paste.js +287 -0
  553. package/dist/senses/cli/image-ref-navigation.js +75 -0
  554. package/dist/senses/cli/ink-app.js +156 -0
  555. package/dist/senses/cli/inline-diff.js +64 -0
  556. package/dist/senses/cli/input-keys.js +174 -0
  557. package/dist/senses/cli/kill-ring.js +86 -0
  558. package/dist/senses/cli/message-list.js +51 -0
  559. package/dist/senses/cli/ouro-tui.js +607 -0
  560. package/dist/senses/cli/spinner-imperative.js +135 -0
  561. package/dist/senses/cli/spinner.js +101 -0
  562. package/dist/senses/cli/status-line.js +60 -0
  563. package/dist/senses/cli/streaming-markdown.js +526 -0
  564. package/dist/senses/cli/tool-display.js +85 -0
  565. package/dist/senses/cli/tool-render.js +85 -0
  566. package/dist/senses/cli/tui-store.js +241 -0
  567. package/dist/senses/cli/virtual-list.js +35 -0
  568. package/dist/senses/cli-entry.js +60 -8
  569. package/dist/senses/cli.js +601 -304
  570. package/dist/senses/commands.js +66 -3
  571. package/dist/senses/context-packet-ledger.js +371 -0
  572. package/dist/senses/context-packets.js +154 -0
  573. package/dist/senses/habit-lifecycle-guard.js +24 -0
  574. package/dist/senses/habit-turn-message.js +160 -0
  575. package/dist/senses/ingress-evidence.js +2 -0
  576. package/dist/senses/mail-entry.js +66 -0
  577. package/dist/senses/mail.js +524 -0
  578. package/dist/senses/pipeline.js +1158 -204
  579. package/dist/senses/private-runtime-worker.js +859 -0
  580. package/dist/senses/private-runtime.js +1627 -0
  581. package/dist/senses/proactive-content-guard.js +51 -0
  582. package/dist/senses/root-host-approval-port.js +345 -0
  583. package/dist/senses/root-host-approval-runtime.js +393 -0
  584. package/dist/senses/sanctuary-authority-resident.js +102 -0
  585. package/dist/senses/sanctuary-download-credit-presentation.js +31 -0
  586. package/dist/senses/sanctuary-full-visibility-contract.js +302 -0
  587. package/dist/senses/sanctuary-grounding.js +118 -0
  588. package/dist/senses/sanctuary-health-acceptance-probe-entry.js +12 -0
  589. package/dist/senses/sanctuary-health-acceptance-probe.js +774 -0
  590. package/dist/senses/sanctuary-health-runner.js +104 -0
  591. package/dist/senses/sanctuary-health.js +422 -0
  592. package/dist/senses/sanctuary-install-state-contract.js +169 -0
  593. package/dist/senses/sanctuary-interactive-control.js +410 -0
  594. package/dist/senses/sanctuary-media-catalog-contract.js +259 -0
  595. package/dist/senses/sanctuary-media-optimization.js +514 -0
  596. package/dist/senses/sanctuary-runtime.js +369 -0
  597. package/dist/senses/sanctuary-sab.js +85 -0
  598. package/dist/senses/sanctuary-storage-optimization-contract.js +32 -0
  599. package/dist/senses/shared-turn.js +793 -0
  600. package/dist/senses/shepherd/client.js +196 -0
  601. package/dist/senses/shepherd/cmux.js +352 -0
  602. package/dist/senses/shepherd/herdr.js +315 -0
  603. package/dist/senses/shepherd/host.js +14 -0
  604. package/dist/senses/shepherd/judge.js +137 -0
  605. package/dist/senses/shepherd/owner-notice.js +251 -0
  606. package/dist/senses/shepherd/redact.js +66 -0
  607. package/dist/senses/shepherd/returns.js +79 -0
  608. package/dist/senses/shepherd/sense.js +310 -0
  609. package/dist/senses/shepherd/terminal-process.js +99 -0
  610. package/dist/senses/shepherd-entry.js +78 -0
  611. package/dist/senses/surface-tool.js +109 -0
  612. package/dist/senses/teams-entry.js +68 -8
  613. package/dist/senses/teams.js +598 -233
  614. package/dist/senses/telegram-admission.js +718 -0
  615. package/dist/senses/telegram-approval-runtime.js +793 -0
  616. package/dist/senses/telegram-attachments.js +186 -0
  617. package/dist/senses/telegram-audit-ledger.js +220 -0
  618. package/dist/senses/telegram-authority-transport.js +231 -0
  619. package/dist/senses/telegram-client.js +1749 -0
  620. package/dist/senses/telegram-effect-adapter.js +900 -0
  621. package/dist/senses/telegram-entry.js +83 -0
  622. package/dist/senses/telegram-handoff.js +80 -0
  623. package/dist/senses/telegram.js +2405 -0
  624. package/dist/senses/trust-gate.js +104 -9
  625. package/dist/senses/voice/audio-playback.js +237 -0
  626. package/dist/senses/voice/audio-routing.js +119 -0
  627. package/dist/senses/voice/call-auth.js +250 -0
  628. package/dist/senses/voice/elevenlabs.js +202 -0
  629. package/dist/senses/voice/floor-control.js +431 -0
  630. package/dist/senses/voice/floor-controller.js +115 -0
  631. package/dist/senses/voice/golden-path.js +116 -0
  632. package/dist/senses/voice/index.js +29 -0
  633. package/dist/senses/voice/local-audio-control.js +344 -0
  634. package/dist/senses/voice/local-audio-devices.js +516 -0
  635. package/dist/senses/voice/local-audio-disclosure.js +174 -0
  636. package/dist/senses/voice/local-audio-identity.js +103 -0
  637. package/dist/senses/voice/local-audio-join.js +325 -0
  638. package/dist/senses/voice/local-audio-mute.js +199 -0
  639. package/dist/senses/voice/local-audio-notify.js +121 -0
  640. package/dist/senses/voice/local-audio-prompts.js +116 -0
  641. package/dist/senses/voice/local-audio-transport.js +427 -0
  642. package/dist/senses/voice/local-audio.js +281 -0
  643. package/dist/senses/voice/local-media-stream.js +145 -0
  644. package/dist/senses/voice/meeting.js +113 -0
  645. package/dist/senses/voice/outbound.js +190 -0
  646. package/dist/senses/voice/phone.js +33 -0
  647. package/dist/senses/voice/playback.js +139 -0
  648. package/dist/senses/voice/process-guards.js +51 -0
  649. package/dist/senses/voice/realtime-eval.js +496 -0
  650. package/dist/senses/voice/realtime-tool-schema.js +195 -0
  651. package/dist/senses/voice/realtime-trace.js +531 -0
  652. package/dist/senses/voice/transcript.js +70 -0
  653. package/dist/senses/voice/turn.js +191 -0
  654. package/dist/senses/voice/twilio-phone-runtime.js +836 -0
  655. package/dist/senses/voice/twilio-phone.js +5454 -0
  656. package/dist/senses/voice/types.js +2 -0
  657. package/dist/senses/voice/whisper.js +161 -0
  658. package/dist/senses/voice-entry.js +83 -0
  659. package/dist/senses/voice-realtime-eval-command.js +99 -0
  660. package/dist/senses/voice-realtime-eval-entry.js +21 -0
  661. package/dist/senses/voice-twilio-entry.js +87 -0
  662. package/dist/trips/core.js +138 -0
  663. package/dist/trips/store.js +265 -0
  664. package/dist/util/frontmatter.js +69 -0
  665. package/npm-shrinkwrap.json +8100 -0
  666. package/package.json +74 -13
  667. package/skills/agent-commerce.md +113 -0
  668. package/skills/browser-navigation.md +117 -0
  669. package/skills/commerce-setup-guide.md +116 -0
  670. package/skills/commerce-setup.md +84 -0
  671. package/skills/configure-dev-tools.md +99 -0
  672. package/skills/travel-planning.md +138 -0
  673. package/dist/heart/daemon/auth-flow.js +0 -430
  674. package/dist/heart/daemon/ouro-path-installer.js +0 -260
  675. package/dist/heart/daemon/ouro-version-manager.js +0 -171
  676. package/dist/heart/daemon/update-hooks.js +0 -138
  677. package/dist/heart/obligations.js +0 -191
  678. package/dist/heart/safe-workspace.js +0 -362
  679. package/dist/heart/session-recall.js +0 -116
  680. package/dist/mind/friends/channel.js +0 -84
  681. package/dist/mind/friends/group-context.js +0 -144
  682. package/dist/mind/friends/resolver.js +0 -84
  683. package/dist/mind/friends/store-file.js +0 -190
  684. package/dist/mind/friends/store.js +0 -4
  685. package/dist/mind/friends/tokens.js +0 -26
  686. package/dist/mind/friends/trust-explanation.js +0 -74
  687. package/dist/mind/friends/types.js +0 -29
  688. package/dist/repertoire/tasks/board.js +0 -134
  689. package/dist/repertoire/tasks/index.js +0 -224
  690. package/dist/repertoire/tasks/lifecycle.js +0 -80
  691. package/dist/repertoire/tasks/middleware.js +0 -65
  692. package/dist/repertoire/tasks/parser.js +0 -173
  693. package/dist/repertoire/tasks/scanner.js +0 -132
  694. package/dist/repertoire/tasks/transitions.js +0 -144
  695. package/dist/senses/bluebubbles-entry.js +0 -13
  696. package/dist/senses/bluebubbles.js +0 -1181
  697. package/dist/senses/debug-activity.js +0 -154
  698. package/dist/senses/inner-dialog-worker.js +0 -90
  699. package/dist/senses/inner-dialog.js +0 -525
  700. package/subagents/README.md +0 -7
  701. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  702. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  703. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  704. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  705. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  706. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  707. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  708. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  709. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  710. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  711. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  712. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  713. /package/dist/{repertoire/tasks/types.js → heart/attachments/sources/adapter.js} +0 -0
  714. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  715. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  716. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
  717. /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
@@ -0,0 +1,469 @@
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.sameUtcDay = exports.writeLedger = exports.readLastDigestAt = exports.readLedger = exports.recallSweep = exports.rememberSweep = exports.ledgerPath = exports.progressPath = exports.houseSweepDir = exports.safe = exports.clip = exports.LIST_CAP = exports.REMIND_DAYS = exports.GRANT_WARNING_DAYS = exports.FAILED_HISTORY_DAYS = exports.MISSING_GRACE_DAYS = exports.PARITY_MAX_AGE_DAYS = exports.TEMPERATURE_C = exports.CRITICAL_CAPACITY_PERCENT = exports.CAPACITY_PERCENT = exports.STALL_MIN_AGE_HOURS = void 0;
37
+ exports.queueProblem = queueProblem;
38
+ exports.runHouseSweep = runHouseSweep;
39
+ exports.draftDigest = draftDigest;
40
+ const fs = __importStar(require("node:fs"));
41
+ const path = __importStar(require("node:path"));
42
+ const node_crypto_1 = require("node:crypto");
43
+ const friends_1 = require("@ouro.bot/friends");
44
+ const runtime_1 = require("../nerves/runtime");
45
+ const steward_policy_1 = require("../heart/steward-policy");
46
+ const delegated_command_grants_1 = require("../a2a/delegated-command-grants");
47
+ const escalation_grants_1 = require("../a2a/escalation-grants");
48
+ /**
49
+ * The Butler's daily house-care sweep, gathered in one deterministic read so the model does not chain a dozen calls:
50
+ * stalled or failed downloads and missing episodes and movies (Sonarr and Radarr), failed imports, disk and parity,
51
+ * container state against the steward policy, and grants that expire or no longer work. It changes nothing in the
52
+ * house. Its only writes are its own bookkeeping under `state/house-sweep/`: when each queue item last moved, and
53
+ * which findings the owner has already been told about, so a repeat finding is not repeated unless it changed.
54
+ */
55
+ exports.STALL_MIN_AGE_HOURS = 6;
56
+ exports.CAPACITY_PERCENT = 90;
57
+ exports.CRITICAL_CAPACITY_PERCENT = 98;
58
+ exports.TEMPERATURE_C = 50;
59
+ exports.PARITY_MAX_AGE_DAYS = 45;
60
+ exports.MISSING_GRACE_DAYS = 3;
61
+ exports.FAILED_HISTORY_DAYS = 3;
62
+ exports.GRANT_WARNING_DAYS = 7;
63
+ exports.REMIND_DAYS = 7;
64
+ exports.LIST_CAP = 8;
65
+ const HOUR_MS = 3_600_000;
66
+ const DAY_MS = 86_400_000;
67
+ const dict = (value) => (value && typeof value === "object" && !Array.isArray(value) ? value : {});
68
+ const list = (value) => (Array.isArray(value) ? value : []);
69
+ const text = (value) => (typeof value === "string" ? value : "");
70
+ const num = (value) => (typeof value === "number" && Number.isFinite(value) ? value : null);
71
+ const lower = (value) => text(value).toLowerCase();
72
+ const clip = (value, max) => (value.length > max ? `${value.slice(0, max - 3)}...` : value);
73
+ exports.clip = clip;
74
+ /** Names and messages come from outside (release titles, friend names): strip control characters, newlines and links before they can reach a digest. */
75
+ const safe = (value) => value.replace(/mailto:\S*/giu, "").replace(/[\u200b-\u200f\u202a-\u202e\u2060-\u206f\ufeff]/gu, "").replace(/\b[a-z][a-z0-9+.-]*:\/\/\S*/giu, "").replace(/[\u0000-\u001f\u007f-\u009f]+/gu, " ").replace(/\s+/gu, " ").trim();
76
+ exports.safe = safe;
77
+ const fingerprintOf = (id, detail) => (0, node_crypto_1.createHash)("sha256").update(`${id}\0${detail}`).digest("hex").slice(0, 12);
78
+ // ---- bookkeeping -------------------------------------------------------------------------------------------------
79
+ const houseSweepDir = (agentRoot) => path.join(agentRoot, "state", "house-sweep");
80
+ exports.houseSweepDir = houseSweepDir;
81
+ function readJson(file) {
82
+ try {
83
+ return dict(JSON.parse(fs.readFileSync(file, "utf8")));
84
+ }
85
+ catch {
86
+ return {};
87
+ }
88
+ }
89
+ function writeJson(file, value) {
90
+ fs.mkdirSync(path.dirname(file), { recursive: true });
91
+ const tmp = `${file}.${process.pid}.tmp`;
92
+ fs.writeFileSync(tmp, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
93
+ fs.renameSync(tmp, file);
94
+ }
95
+ const progressPath = (agentRoot) => path.join((0, exports.houseSweepDir)(agentRoot), "queue-progress.json");
96
+ exports.progressPath = progressPath;
97
+ const ledgerPath = (agentRoot) => path.join((0, exports.houseSweepDir)(agentRoot), "reported.json");
98
+ exports.ledgerPath = ledgerPath;
99
+ const lastSweeps = new Map();
100
+ const rememberSweep = (agentRoot, scope, findings) => { lastSweeps.set(`${path.resolve(agentRoot)}\0${scope}`, findings.map(({ id, fingerprint, summary }) => ({ id, fingerprint, summary }))); };
101
+ exports.rememberSweep = rememberSweep;
102
+ const recallSweep = (agentRoot, scope) => lastSweeps.get(`${path.resolve(agentRoot)}\0${scope}`);
103
+ exports.recallSweep = recallSweep;
104
+ const readLedger = (agentRoot) => dict(readJson((0, exports.ledgerPath)(agentRoot)).entries);
105
+ exports.readLedger = readLedger;
106
+ /** When the last live digest went out (ISO), or "" when none has. Kept beside the ledger so a retry or a second path cannot send two in a day. */
107
+ const readLastDigestAt = (agentRoot) => text(readJson((0, exports.ledgerPath)(agentRoot)).lastDigestAt);
108
+ exports.readLastDigestAt = readLastDigestAt;
109
+ const writeLedger = (agentRoot, entries, lastDigestAt = (0, exports.readLastDigestAt)(agentRoot)) => writeJson((0, exports.ledgerPath)(agentRoot), { schemaVersion: 1, entries, ...(lastDigestAt ? { lastDigestAt } : {}) });
110
+ exports.writeLedger = writeLedger;
111
+ const sameUtcDay = (a, b) => a.length > 0 && a.slice(0, 10) === b.slice(0, 10);
112
+ exports.sameUtcDay = sameUtcDay;
113
+ function readCredentials(file) {
114
+ const parsed = readJson(file);
115
+ const pick = (name) => {
116
+ const entry = dict(parsed[name]);
117
+ return text(entry.url) && text(entry.apiKey) ? { url: text(entry.url).replace(/\/+$/u, ""), apiKey: text(entry.apiKey) } : null;
118
+ };
119
+ return { sonarr: pick("sonarr"), radarr: pick("radarr") };
120
+ }
121
+ async function arrGet(service, creds, pathAndQuery, fetchImpl) {
122
+ const response = await fetchImpl(`${creds.url}/api/v3${pathAndQuery}`, { headers: { "X-Api-Key": creds.apiKey, Accept: "application/json" }, signal: AbortSignal.timeout(20_000) });
123
+ if (!response.ok)
124
+ throw new Error(`${service} answered HTTP ${response.status}`);
125
+ return response.json();
126
+ }
127
+ const records = (payload) => (Array.isArray(payload) ? payload : list(dict(payload).records)).map(dict);
128
+ /** The same notion of "stuck" the replay gate applies to a queue record: Sonarr flags it, or a partial download with no ETA has sat for hours. */
129
+ function queueProblem(item, nowMs) {
130
+ const flagged = (value) => ["warning", "error", "failed"].includes(lower(value));
131
+ const messages = list(item.statusMessages).flatMap((entry) => [text(dict(entry).title), ...list(dict(entry).messages).map(text)]).filter(Boolean);
132
+ const errorMessage = text(item.errorMessage);
133
+ if (flagged(item.trackedDownloadStatus) || flagged(item.status) || messages.length > 0) {
134
+ return (0, exports.clip)(errorMessage || messages[0] || `status ${text(item.trackedDownloadStatus) || text(item.status)}`, 140);
135
+ }
136
+ const size = num(item.size) ?? 0;
137
+ const left = num(item.sizeleft) ?? 0;
138
+ const noEta = item.timeleft === undefined || item.timeleft === null || item.timeleft === "00:00:00";
139
+ const addedAt = Date.parse(text(item.added));
140
+ if (size > 0 && left > 0 && noEta && Number.isFinite(addedAt) && nowMs - addedAt >= exports.STALL_MIN_AGE_HOURS * HOUR_MS)
141
+ return "partial download with no ETA";
142
+ return null;
143
+ }
144
+ const nameOf = (service, item) => text(service === "sonarr" ? dict(item.series).title : dict(item.movie).title) || text(item.title) || "an unknown title";
145
+ function summarizeArr(service, queuePayload, missingPayload, historyPayload, nowMs, progress, nextProgress) {
146
+ const label = service === "sonarr" ? "Sonarr" : "Radarr";
147
+ const items = records(queuePayload);
148
+ const findings = [];
149
+ let stalled = 0;
150
+ let importProblems = 0;
151
+ const inFlight = new Set();
152
+ for (const item of items) {
153
+ const queueId = num(item.id);
154
+ if (queueId === null)
155
+ continue;
156
+ const subjectId = num(service === "sonarr" ? item.seriesId : item.movieId);
157
+ if (subjectId !== null)
158
+ inFlight.add(subjectId);
159
+ const key = `${service}:${queueId}`;
160
+ const left = num(item.sizeleft) ?? 0;
161
+ const previous = dict(progress[key]);
162
+ const since = previous.sizeleft === left && Number.isFinite(Date.parse(text(previous.since))) ? text(previous.since) : new Date(nowMs).toISOString();
163
+ nextProgress[key] = { sizeleft: left, since };
164
+ const frozen = left > 0 && nowMs - Date.parse(since) >= exports.STALL_MIN_AGE_HOURS * HOUR_MS;
165
+ const problem = queueProblem(item, nowMs) ?? (frozen ? `no progress for ${Math.floor((nowMs - Date.parse(since)) / HOUR_MS)}h` : null);
166
+ const importState = lower(item.trackedDownloadState);
167
+ const importBlocked = importState === "importfailed" || importState === "importblocked";
168
+ if (problem === null && !importBlocked)
169
+ continue;
170
+ const size = num(item.size) ?? 0;
171
+ const percent = size > 0 ? Math.round(((size - left) / size) * 100) : 0;
172
+ const name = nameOf(service, item);
173
+ const release = (0, exports.clip)(text(item.title), 80);
174
+ const refs = { service, queueId };
175
+ if (importBlocked) {
176
+ importProblems += 1;
177
+ const id = `imports:${service}:${queueId}`;
178
+ findings.push({ id, area: "imports", severity: "warn", next: "owner", refs, summary: `${label} cannot import ${name} (${release}): ${problem ?? importState}`, fingerprint: fingerprintOf(id, importState) });
179
+ continue;
180
+ }
181
+ stalled += 1;
182
+ const id = `downloads:${service}:${queueId}`;
183
+ const series = service === "sonarr" && subjectId !== null;
184
+ findings.push({
185
+ id, area: "downloads", severity: "warn", refs,
186
+ next: series ? "fix" : "owner",
187
+ summary: `${label} download of ${name} is stuck at ${percent}% (${release}): ${problem}`,
188
+ fingerprint: fingerprintOf(id, problem.startsWith("no progress") ? "frozen" : problem),
189
+ ...(series ? { fix: { tool: "media_fill_missing", args: { service_id: subjectId }, note: "keeps the partial download; replaces it only when a seeded alternative is grabbed" } } : {}),
190
+ });
191
+ }
192
+ // Failed downloads Sonarr or Radarr recorded recently (the queue row is gone, the episode or movie may still be missing).
193
+ for (const event of records(historyPayload)) {
194
+ const when = Date.parse(text(event.date));
195
+ const eventId = num(event.id);
196
+ if (eventId === null || !Number.isFinite(when) || nowMs - when > exports.FAILED_HISTORY_DAYS * DAY_MS)
197
+ continue;
198
+ const subjectId = num(service === "sonarr" ? event.seriesId : event.movieId);
199
+ if (subjectId !== null && inFlight.has(subjectId))
200
+ continue;
201
+ const id = `downloads:${service}:failed:${eventId}`;
202
+ const series = service === "sonarr" && subjectId !== null;
203
+ findings.push({
204
+ id, area: "downloads", severity: "warn", next: series ? "fix" : "owner",
205
+ summary: `${label} recorded a failed download: ${(0, exports.clip)(text(event.sourceTitle), 90)}`,
206
+ fingerprint: fingerprintOf(id, "failed"),
207
+ ...(series ? { fix: { tool: "media_fill_missing", args: { service_id: subjectId }, note: "looks for another release" } } : {}),
208
+ });
209
+ }
210
+ // Monitored but missing, and not already on its way.
211
+ const cutoff = nowMs - exports.MISSING_GRACE_DAYS * DAY_MS;
212
+ const missingBySubject = new Map();
213
+ for (const row of records(missingPayload)) {
214
+ if (service === "radarr" && row.isAvailable === false)
215
+ continue;
216
+ const subjectId = service === "sonarr" ? num(row.seriesId) : num(row.id);
217
+ const stamp = Date.parse(text(service === "sonarr" ? row.airDateUtc : row.added));
218
+ if (subjectId === null || inFlight.has(subjectId) || row.monitored === false || !Number.isFinite(stamp) || stamp > cutoff)
219
+ continue;
220
+ const name = (service === "sonarr" ? text(dict(row.series).title) : text(row.title)) || "an unknown title";
221
+ const seen = missingBySubject.get(subjectId);
222
+ missingBySubject.set(subjectId, { name, count: (seen?.count ?? 0) + 1, oldest: Math.min(seen?.oldest ?? stamp, stamp) });
223
+ }
224
+ for (const [subjectId, entry] of [...missingBySubject].sort((a, b) => a[1].oldest - b[1].oldest)) {
225
+ const id = `missing:${service}:${subjectId}`;
226
+ const series = service === "sonarr";
227
+ const days = Math.floor((nowMs - entry.oldest) / DAY_MS);
228
+ findings.push({
229
+ id, area: "missing", severity: "info", next: "fix",
230
+ summary: series ? `${entry.name}: ${entry.count} monitored episode${entry.count === 1 ? "" : "s"} missing, the oldest aired ${days} days ago` : `${entry.name}: monitored movie still missing after ${days} days`,
231
+ fingerprint: fingerprintOf(id, String(entry.count)),
232
+ fix: series
233
+ ? { tool: "media_fill_missing", args: { service_id: subjectId }, note: "searches, grabs a seeded release, imports what finished" }
234
+ : { tool: "media_search_now", args: { kind: "movie", service_id: subjectId }, note: "triggers a Radarr search" },
235
+ });
236
+ }
237
+ return { queue: { total: items.length, stalled, importProblems }, findings };
238
+ }
239
+ // ---- the host, the policy and the grants -------------------------------------------------------------------------
240
+ function okData(result) {
241
+ const value = dict(result);
242
+ return value.ok === true ? dict(value.data) : null;
243
+ }
244
+ async function sweepHost(sanctuary, policyStates, nowMs, findings) {
245
+ const [containersResult, storageResult, disksResult] = await Promise.all([sanctuary.listContainers(), sanctuary.getStorage(), sanctuary.getDisks()]);
246
+ const containers = okData(containersResult);
247
+ const storage = okData(storageResult);
248
+ const disks = okData(disksResult);
249
+ const unavailable = [containers ? "" : "containers", storage ? "" : "storage", disks ? "" : "disks"].filter(Boolean);
250
+ const summary = { array: null, arrayUsedPercent: null, parity: null, parityAgeDays: null, containers: 0 };
251
+ for (const container of list(containers?.containers).map(dict)) {
252
+ summary.containers += 1;
253
+ const name = text(container.name);
254
+ const state = text(container.state);
255
+ const desired = policyStates ? dict(policyStates[`container:${name}`]) : {};
256
+ const expires = Date.parse(text(desired.expiresAt));
257
+ const value = Object.keys(desired).length > 0 && !(Number.isFinite(expires) && expires <= nowMs) ? lower(desired.value) : "";
258
+ const wantedOn = ["on", "always_on", "expected_on"].includes(value);
259
+ const wantedOff = /^(?:off|disabled|paused|intentionally_off|intentionally_paused)$/u.test(value);
260
+ const running = state === "running";
261
+ const id = `containers:${name}`;
262
+ if (wantedOn && !running)
263
+ findings.push({ id, area: "containers", severity: "critical", next: "owner", summary: `${name} should be on by the steward policy but is ${state}`, fingerprint: fingerprintOf(id, `down:${state}`) });
264
+ else if (wantedOff && running)
265
+ findings.push({ id, area: "containers", severity: "warn", next: "owner", summary: `${name} should be off by the steward policy but is running`, fingerprint: fingerprintOf(id, "up") });
266
+ else if (!value && container.autostart === true && !running)
267
+ findings.push({ id, area: "containers", severity: "warn", next: "owner", summary: `${name} starts with the array but is ${state}, and the policy says nothing about it`, fingerprint: fingerprintOf(id, `down:${state}`) });
268
+ }
269
+ const arrayInfo = dict(storage?.array);
270
+ summary.array = text(arrayInfo.state) || null;
271
+ summary.arrayUsedPercent = num(arrayInfo.usedPercent);
272
+ const capacity = (id, label, percent) => {
273
+ if (percent === null || percent < exports.CAPACITY_PERCENT)
274
+ return;
275
+ findings.push({ id, area: "disk", severity: percent >= exports.CRITICAL_CAPACITY_PERCENT ? "critical" : "warn", next: "owner", summary: `${label} is ${percent}% full`, fingerprint: fingerprintOf(id, String(Math.floor(percent))) });
276
+ };
277
+ capacity("disk:array:capacity", "the array", summary.arrayUsedPercent);
278
+ for (const share of list(storage?.shares).map(dict))
279
+ capacity(`disk:share:${text(share.name)}:capacity`, `share ${text(share.name)}`, num(share.usedPercent));
280
+ if (storage && summary.array && summary.array.toUpperCase() !== "STARTED")
281
+ findings.push({ id: "disk:array:state", area: "disk", severity: "critical", next: "owner", summary: `the array is ${summary.array}`, fingerprint: fingerprintOf("disk:array:state", summary.array) });
282
+ for (const disk of list(disks?.disks).map(dict)) {
283
+ const id = `disk:${text(disk.id)}`;
284
+ if (disk.smart === "failed")
285
+ findings.push({ id: `${id}:smart`, area: "disk", severity: "critical", next: "owner", summary: `${text(disk.name)} reports a failed SMART status`, fingerprint: fingerprintOf(`${id}:smart`, "failed") });
286
+ const temperature = num(disk.temperatureC);
287
+ if (temperature !== null && temperature >= exports.TEMPERATURE_C)
288
+ findings.push({ id: `${id}:temperature`, area: "disk", severity: "warn", next: "owner", summary: `${text(disk.name)} is ${temperature}C`, fingerprint: fingerprintOf(`${id}:temperature`, String(Math.floor(temperature / 5))) });
289
+ }
290
+ const parity = dict(disks?.parity);
291
+ summary.parity = text(parity.result) || null;
292
+ const ageHours = num(parity.ageHours);
293
+ summary.parityAgeDays = ageHours === null ? null : Math.floor(ageHours / 24);
294
+ if (disks) {
295
+ const result = summary.parity;
296
+ if (result === "failed")
297
+ findings.push({ id: "parity:result", area: "parity", severity: "critical", next: "owner", summary: `the last parity check failed (${num(parity.errors) ?? "unknown"} errors)`, fingerprint: fingerprintOf("parity:result", `failed:${num(parity.errors)}`) });
298
+ else if (result !== "in_progress" && (summary.parityAgeDays === null || summary.parityAgeDays >= exports.PARITY_MAX_AGE_DAYS))
299
+ findings.push({ id: "parity:age", area: "parity", severity: "warn", next: "owner", summary: summary.parityAgeDays === null ? "parity check age is unknown" : `the last parity check was ${summary.parityAgeDays} days ago`, fingerprint: fingerprintOf("parity:age", summary.parityAgeDays === null ? "unknown" : "old") });
300
+ }
301
+ return { summary, problem: unavailable.length > 0 ? `${unavailable.join(", ")} unreadable` : null };
302
+ }
303
+ async function sweepGrants(agentRoot, policy, nowMs, findings) {
304
+ const expiry = (id, label, expiresAt) => {
305
+ const at = Date.parse(text(expiresAt));
306
+ if (!Number.isFinite(at) || at - nowMs > exports.GRANT_WARNING_DAYS * DAY_MS)
307
+ return;
308
+ const days = Math.floor((at - nowMs) / DAY_MS);
309
+ findings.push({ id, area: "grants", severity: at <= nowMs ? "warn" : "info", next: "owner", summary: at <= nowMs ? `${label} expired` : `${label} expires in ${days} days`, fingerprint: fingerprintOf(id, at <= nowMs ? "expired" : "soon") });
310
+ };
311
+ for (const [key, grant] of Object.entries(policy?.routineActionGrants ?? {}))
312
+ expiry(`grants:routine:${key}`, `the standing permission ${key}`, grant.expiresAt);
313
+ for (const [key, entry] of Object.entries(policy?.desiredStates ?? {}))
314
+ expiry(`grants:desired:${key}`, `the standing expectation ${key}`, entry.expiresAt);
315
+ try {
316
+ const friends = await new friends_1.FileFriendStore(path.join(agentRoot, "friends")).listAll();
317
+ const escalation = (0, escalation_grants_1.readEscalationGrants)(agentRoot);
318
+ // Delegated-command authority is the operator's trusted grant, so that is what is warned about; the old record field grants nothing.
319
+ const delegated = (0, delegated_command_grants_1.viewDelegatedCommandGrants)(agentRoot).grants;
320
+ const active = (friend) => friend.trustLevel === "family" && friend.admissionState === "active";
321
+ for (const friend of friends) {
322
+ const delegation = delegated[friend.id];
323
+ if (delegation) {
324
+ expiry(`grants:delegation:${friend.id}`, `${friend.name}'s delegation grant`, delegation.expiresAt);
325
+ if (!active(friend))
326
+ findings.push({ id: `grants:delegation:${friend.id}:inert`, area: "grants", severity: "info", next: "owner", summary: `${friend.name} holds a delegation grant but is no longer an active family friend, so it does nothing`, fingerprint: fingerprintOf(`grants:delegation:${friend.id}:inert`, "inert") });
327
+ }
328
+ const held = escalation[friend.id];
329
+ if (held) {
330
+ expiry(`grants:escalation:${friend.id}`, `${friend.name}'s escalation grant`, held.expiresAt);
331
+ if (!active(friend))
332
+ findings.push({ id: `grants:escalation:${friend.id}:inert`, area: "grants", severity: "info", next: "owner", summary: `${friend.name} holds an escalation grant but is no longer an active family friend, so reports cannot reach them`, fingerprint: fingerprintOf(`grants:escalation:${friend.id}:inert`, "inert") });
333
+ }
334
+ }
335
+ return "ok";
336
+ }
337
+ catch (error) {
338
+ return `friends unreadable: ${error instanceof Error ? error.message : String(error)}`;
339
+ }
340
+ }
341
+ // ---- the sweep ---------------------------------------------------------------------------------------------------
342
+ const SEVERITY_ORDER = { critical: 0, warn: 1, info: 2 };
343
+ /** The sweep sources a ledger id depends on. */
344
+ function sourcesFor(id) {
345
+ const [area, second] = id.split(":");
346
+ if (area === "downloads" || area === "imports" || area === "missing")
347
+ return second === "radarr" ? ["radarr"] : ["sonarr"];
348
+ if (area === "containers")
349
+ return ["host", "policy"];
350
+ if (area === "disk" || area === "parity")
351
+ return ["host"];
352
+ if (area === "grants")
353
+ return ["grants", "policy"];
354
+ return [];
355
+ }
356
+ async function runHouseSweep(deps) {
357
+ const now = deps.now ?? Date.now;
358
+ const nowMs = now();
359
+ const fetchImpl = deps.fetch ?? fetch;
360
+ const scope = deps.replay ? "replay" : "live";
361
+ const findings = [];
362
+ const sources = { sonarr: "ok", radarr: "ok", host: "ok", policy: "ok", grants: "ok" };
363
+ const queue = { sonarr: { unavailable: "not read" }, radarr: { unavailable: "not read" } };
364
+ const unavailable = (source, reason) => {
365
+ sources[source] = reason;
366
+ findings.push({ id: `sweep:${source}`, area: "sweep", severity: "warn", next: "owner", summary: `the sweep could not read ${source}: ${(0, exports.clip)(reason, 120)}`, fingerprint: fingerprintOf(`sweep:${source}`, "unavailable") });
367
+ };
368
+ const credentials = readCredentials(deps.credentialsPath ?? path.join(deps.agentRoot, "mcp", "media-credentials.json"));
369
+ const progress = dict(readJson((0, exports.progressPath)(deps.agentRoot)).items);
370
+ const nextProgress = {};
371
+ for (const service of ["sonarr", "radarr"]) {
372
+ const creds = credentials[service];
373
+ if (!creds) {
374
+ queue[service] = { unavailable: "no credentials" };
375
+ unavailable(service, "no credentials in the media credential file");
376
+ continue;
377
+ }
378
+ try {
379
+ const sonarr = service === "sonarr";
380
+ const [queuePayload, missingPayload, historyPayload] = await Promise.all([
381
+ arrGet(service, creds, sonarr ? "/queue?pageSize=200&includeSeries=true" : "/queue?pageSize=200&includeMovie=true", fetchImpl),
382
+ arrGet(service, creds, sonarr ? "/wanted/missing?pageSize=500&monitored=true&includeSeries=true&sortKey=airDateUtc&sortDirection=ascending" : "/wanted/missing?pageSize=500&monitored=true", fetchImpl),
383
+ arrGet(service, creds, "/history?pageSize=50&eventType=4&sortKey=date&sortDirection=descending", fetchImpl),
384
+ ]);
385
+ const summary = summarizeArr(service, queuePayload, missingPayload, historyPayload, nowMs, progress, nextProgress);
386
+ queue[service] = summary.queue;
387
+ findings.push(...summary.findings);
388
+ }
389
+ catch (error) {
390
+ const reason = error instanceof Error ? error.message : String(error);
391
+ queue[service] = { unavailable: (0, exports.clip)(reason, 120) };
392
+ unavailable(service, reason);
393
+ }
394
+ }
395
+ // A source that could not be read keeps its last known progress, so one bad morning does not reset every stall clock.
396
+ for (const service of ["sonarr", "radarr"]) {
397
+ if ("unavailable" in queue[service])
398
+ for (const [key, value] of Object.entries(progress))
399
+ if (key.startsWith(`${service}:`))
400
+ nextProgress[key] = value;
401
+ }
402
+ writeJson((0, exports.progressPath)(deps.agentRoot), { schemaVersion: 1, items: nextProgress });
403
+ let policy = null;
404
+ try {
405
+ policy = (0, steward_policy_1.readStewardPolicy)(deps.agentRoot);
406
+ }
407
+ catch (error) {
408
+ unavailable("policy", error instanceof Error ? error.message : String(error));
409
+ }
410
+ let host;
411
+ if (!deps.sanctuary)
412
+ unavailable("host", "the Unraid read runtime is not attached to this turn");
413
+ else {
414
+ try {
415
+ const swept = await sweepHost(deps.sanctuary, policy ? policy.desiredStates : null, nowMs, findings);
416
+ host = swept.summary;
417
+ if (swept.problem)
418
+ unavailable("host", swept.problem);
419
+ }
420
+ catch (error) {
421
+ unavailable("host", error instanceof Error ? error.message : String(error));
422
+ }
423
+ }
424
+ const grantsStatus = await sweepGrants(deps.agentRoot, policy, nowMs, findings);
425
+ if (grantsStatus !== "ok")
426
+ unavailable("grants", grantsStatus);
427
+ // What the owner has already been told. A replay sweep reads and writes none of it.
428
+ const ledger = deps.replay ? {} : (0, exports.readLedger)(deps.agentRoot);
429
+ const ordered = findings
430
+ .map((finding) => {
431
+ const told = ledger[finding.id];
432
+ const recent = told !== undefined && told.fingerprint === finding.fingerprint && nowMs - Date.parse(told.reportedAt) < exports.REMIND_DAYS * DAY_MS;
433
+ return { ...finding, summary: (0, exports.clip)((0, exports.safe)(finding.summary), 220), alreadyReported: recent };
434
+ })
435
+ .sort((a, b) => SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity] || a.id.localeCompare(b.id));
436
+ if (!deps.replay) {
437
+ // A finding that is gone is forgotten, so it is told again if it comes back.
438
+ // ...but only when its source was readable: an outage must not make every finding look new on recovery.
439
+ const present = new Set(ordered.map((finding) => finding.id));
440
+ const kept = Object.fromEntries(Object.entries(ledger).filter(([id]) => present.has(id) || sourcesFor(id).some((source) => sources[source] !== "ok")));
441
+ if (Object.keys(kept).length !== Object.keys(ledger).length)
442
+ (0, exports.writeLedger)(deps.agentRoot, kept);
443
+ }
444
+ const sentToday = !deps.replay && (0, exports.sameUtcDay)((0, exports.readLastDigestAt)(deps.agentRoot), new Date(nowMs).toISOString());
445
+ const fresh = ordered.filter((finding) => !finding.alreadyReported);
446
+ const shown = ordered.slice(0, exports.LIST_CAP * 3);
447
+ const shownIds = new Set(shown.map((finding) => finding.id));
448
+ const report = {
449
+ checkedAt: new Date(nowMs).toISOString(),
450
+ scope,
451
+ queue,
452
+ sources,
453
+ ...(host ? { host } : {}),
454
+ findings: shown,
455
+ omitted: ordered.length - shown.length,
456
+ fresh: fresh.filter((finding) => shownIds.has(finding.id)).map((finding) => finding.id),
457
+ digest_due: fresh.length > 0 && !sentToday,
458
+ digest_draft: draftDigest(fresh.filter((finding) => shownIds.has(finding.id))),
459
+ guidance: `${sentToday ? "A digest already went out today, so none is due. " : ""}Try every finding with a fix first (the fix never deletes a partial download). Anything still unsettled, and every finding without a fix, goes in ONE short digest with house_digest_send using the finding ids. If digest_due is false, say nothing to the owner. Never remove a download or delete anything.`,
460
+ };
461
+ (0, exports.rememberSweep)(deps.agentRoot, scope, shown);
462
+ (0, runtime_1.emitNervesEvent)({ component: "repertoire", event: "repertoire.house_sweep_run", message: "house sweep gathered", meta: { scope, findings: ordered.length, fresh: fresh.length, digestDue: report.digest_due } });
463
+ return report;
464
+ }
465
+ function draftDigest(findings) {
466
+ if (findings.length === 0)
467
+ return "";
468
+ return findings.slice(0, exports.LIST_CAP).map((finding) => `- ${finding.summary}`).join("\n") + (findings.length > exports.LIST_CAP ? `\n- and ${findings.length - exports.LIST_CAP} more` : "");
469
+ }
@@ -1,11 +1,31 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.McpClient = void 0;
3
+ exports.McpClient = exports.MCP_TOOL_TIMEOUT_META_KEY = void 0;
4
+ exports.isMcpTransportError = isMcpTransportError;
4
5
  const child_process_1 = require("child_process");
5
6
  const readline_1 = require("readline");
6
7
  const runtime_1 = require("../nerves/runtime");
8
+ const runtime_cwd_1 = require("../heart/runtime-cwd");
9
+ /** Metadata key a server uses to declare how long one call to a tool may take. */
10
+ exports.MCP_TOOL_TIMEOUT_META_KEY = "ouro.bot/timeoutMs";
11
+ const MAX_TOOL_CALL_TIMEOUT = 600_000;
7
12
  const MCP_PROTOCOL_VERSION = "2024-11-05";
13
+ const DEFAULT_REQUEST_TIMEOUT = 10_000;
8
14
  const DEFAULT_TOOL_CALL_TIMEOUT = 30_000;
15
+ const MIN_TOOL_CALL_TIMEOUT = 1_000;
16
+ function isMcpTransportError(error) {
17
+ const message = error instanceof Error ? error.message : String(error);
18
+ const normalized = message.toLowerCase();
19
+ return normalized.includes("disconnected")
20
+ || normalized.includes("transport")
21
+ || normalized.includes("closed")
22
+ || normalized.includes("econnreset")
23
+ || normalized.includes("econnrefused")
24
+ || normalized.includes("enoent")
25
+ || normalized.includes("epipe")
26
+ || normalized.includes("broken pipe")
27
+ || normalized.includes("not writable");
28
+ }
9
29
  class McpClient {
10
30
  config;
11
31
  process = null;
@@ -18,6 +38,9 @@ class McpClient {
18
38
  this.config = config;
19
39
  }
20
40
  async connect() {
41
+ if (this.connected)
42
+ return;
43
+ this.shutdownProcessOnly();
21
44
  (0, runtime_1.emitNervesEvent)({
22
45
  event: "mcp.connect_start",
23
46
  component: "repertoire",
@@ -25,9 +48,11 @@ class McpClient {
25
48
  meta: { command: this.config.command },
26
49
  });
27
50
  const env = { ...process.env, ...this.config.env };
51
+ const spawnCwd = this.config.cwd ?? (0, runtime_cwd_1.recoverRuntimeCwd)();
28
52
  this.process = (0, child_process_1.spawn)(this.config.command, this.config.args ?? [], {
29
53
  env,
30
54
  stdio: ["pipe", "pipe", "pipe"],
55
+ cwd: spawnCwd,
31
56
  });
32
57
  this.setupLineReader();
33
58
  this.setupProcessHandlers();
@@ -43,6 +68,7 @@ class McpClient {
43
68
  }
44
69
  catch (error) {
45
70
  this.connected = false;
71
+ this.shutdownProcessOnly();
46
72
  (0, runtime_1.emitNervesEvent)({
47
73
  level: "error",
48
74
  event: "mcp.connect_error",
@@ -75,7 +101,19 @@ class McpClient {
75
101
  this.cachedTools = allTools;
76
102
  return allTools;
77
103
  }
78
- async callTool(name, args, timeout = DEFAULT_TOOL_CALL_TIMEOUT) {
104
+ /** A tool that searches slow services may declare a longer timeout; it is clamped to 1 s .. 10 min. */
105
+ declaredToolTimeout(name) {
106
+ const declared = this.cachedTools?.find((tool) => tool.name === name)?._meta?.[exports.MCP_TOOL_TIMEOUT_META_KEY];
107
+ if (typeof declared !== "number" || !Number.isFinite(declared))
108
+ return DEFAULT_TOOL_CALL_TIMEOUT;
109
+ return Math.min(Math.max(Math.round(declared), MIN_TOOL_CALL_TIMEOUT), MAX_TOOL_CALL_TIMEOUT);
110
+ }
111
+ async refreshTools() {
112
+ this.cachedTools = null;
113
+ return this.listTools();
114
+ }
115
+ async callTool(name, args, timeoutOverride) {
116
+ const timeout = timeoutOverride ?? this.declaredToolTimeout(name);
79
117
  (0, runtime_1.emitNervesEvent)({
80
118
  event: "mcp.tool_call_start",
81
119
  component: "repertoire",
@@ -138,7 +176,7 @@ class McpClient {
138
176
  });
139
177
  return result;
140
178
  }
141
- sendRequest(method, params, timeout) {
179
+ sendRequest(method, params, timeout = DEFAULT_REQUEST_TIMEOUT) {
142
180
  return new Promise((resolve, reject) => {
143
181
  if (!this.process || !this.connected && method !== "initialize") {
144
182
  reject(new Error("MCP client is disconnected"));
@@ -159,14 +197,21 @@ class McpClient {
159
197
  method,
160
198
  params,
161
199
  };
162
- this.writeMessage(request);
200
+ if (!this.writeMessage(request)) {
201
+ this.pending.delete(id);
202
+ if (pending.timer) {
203
+ clearTimeout(pending.timer);
204
+ }
205
+ reject(new Error(`MCP transport is not writable for request: ${method}`));
206
+ }
163
207
  });
164
208
  }
165
209
  writeMessage(message) {
166
- /* v8 ignore next -- defensive: stdin always writable during active connection @preserve */
167
210
  if (this.process?.stdin?.writable) {
168
211
  this.process.stdin.write(JSON.stringify(message) + "\n");
212
+ return true;
169
213
  }
214
+ return false;
170
215
  }
171
216
  setupLineReader() {
172
217
  /* v8 ignore next -- defensive: stdout always exists after spawn @preserve */
@@ -250,5 +295,13 @@ class McpClient {
250
295
  this.pending.delete(id);
251
296
  }
252
297
  }
298
+ shutdownProcessOnly() {
299
+ this.rejectAllPending(new Error("MCP transport closed during reconnect"));
300
+ /* v8 ignore next -- defensive: process may already be absent @preserve */
301
+ if (this.process && !this.process.killed) {
302
+ this.process.kill();
303
+ }
304
+ this.process = null;
305
+ }
253
306
  }
254
307
  exports.McpClient = McpClient;