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

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 (716) 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 +5819 -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 +344 -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 +245 -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 +309 -0
  609. package/dist/senses/shepherd-entry.js +78 -0
  610. package/dist/senses/surface-tool.js +109 -0
  611. package/dist/senses/teams-entry.js +68 -8
  612. package/dist/senses/teams.js +598 -233
  613. package/dist/senses/telegram-admission.js +718 -0
  614. package/dist/senses/telegram-approval-runtime.js +793 -0
  615. package/dist/senses/telegram-attachments.js +186 -0
  616. package/dist/senses/telegram-audit-ledger.js +220 -0
  617. package/dist/senses/telegram-authority-transport.js +231 -0
  618. package/dist/senses/telegram-client.js +1749 -0
  619. package/dist/senses/telegram-effect-adapter.js +900 -0
  620. package/dist/senses/telegram-entry.js +83 -0
  621. package/dist/senses/telegram-handoff.js +80 -0
  622. package/dist/senses/telegram.js +2405 -0
  623. package/dist/senses/trust-gate.js +104 -9
  624. package/dist/senses/voice/audio-playback.js +237 -0
  625. package/dist/senses/voice/audio-routing.js +119 -0
  626. package/dist/senses/voice/call-auth.js +250 -0
  627. package/dist/senses/voice/elevenlabs.js +202 -0
  628. package/dist/senses/voice/floor-control.js +431 -0
  629. package/dist/senses/voice/floor-controller.js +115 -0
  630. package/dist/senses/voice/golden-path.js +116 -0
  631. package/dist/senses/voice/index.js +29 -0
  632. package/dist/senses/voice/local-audio-control.js +344 -0
  633. package/dist/senses/voice/local-audio-devices.js +516 -0
  634. package/dist/senses/voice/local-audio-disclosure.js +174 -0
  635. package/dist/senses/voice/local-audio-identity.js +103 -0
  636. package/dist/senses/voice/local-audio-join.js +325 -0
  637. package/dist/senses/voice/local-audio-mute.js +199 -0
  638. package/dist/senses/voice/local-audio-notify.js +121 -0
  639. package/dist/senses/voice/local-audio-prompts.js +116 -0
  640. package/dist/senses/voice/local-audio-transport.js +427 -0
  641. package/dist/senses/voice/local-audio.js +281 -0
  642. package/dist/senses/voice/local-media-stream.js +145 -0
  643. package/dist/senses/voice/meeting.js +113 -0
  644. package/dist/senses/voice/outbound.js +190 -0
  645. package/dist/senses/voice/phone.js +33 -0
  646. package/dist/senses/voice/playback.js +139 -0
  647. package/dist/senses/voice/process-guards.js +51 -0
  648. package/dist/senses/voice/realtime-eval.js +496 -0
  649. package/dist/senses/voice/realtime-tool-schema.js +195 -0
  650. package/dist/senses/voice/realtime-trace.js +531 -0
  651. package/dist/senses/voice/transcript.js +70 -0
  652. package/dist/senses/voice/turn.js +191 -0
  653. package/dist/senses/voice/twilio-phone-runtime.js +836 -0
  654. package/dist/senses/voice/twilio-phone.js +5454 -0
  655. package/dist/senses/voice/types.js +2 -0
  656. package/dist/senses/voice/whisper.js +161 -0
  657. package/dist/senses/voice-entry.js +83 -0
  658. package/dist/senses/voice-realtime-eval-command.js +99 -0
  659. package/dist/senses/voice-realtime-eval-entry.js +21 -0
  660. package/dist/senses/voice-twilio-entry.js +87 -0
  661. package/dist/trips/core.js +138 -0
  662. package/dist/trips/store.js +265 -0
  663. package/dist/util/frontmatter.js +69 -0
  664. package/npm-shrinkwrap.json +8100 -0
  665. package/package.json +74 -13
  666. package/skills/agent-commerce.md +113 -0
  667. package/skills/browser-navigation.md +117 -0
  668. package/skills/commerce-setup-guide.md +116 -0
  669. package/skills/commerce-setup.md +84 -0
  670. package/skills/configure-dev-tools.md +99 -0
  671. package/skills/travel-planning.md +138 -0
  672. package/dist/heart/daemon/auth-flow.js +0 -430
  673. package/dist/heart/daemon/ouro-path-installer.js +0 -260
  674. package/dist/heart/daemon/ouro-version-manager.js +0 -171
  675. package/dist/heart/daemon/update-hooks.js +0 -138
  676. package/dist/heart/obligations.js +0 -191
  677. package/dist/heart/safe-workspace.js +0 -362
  678. package/dist/heart/session-recall.js +0 -116
  679. package/dist/mind/friends/channel.js +0 -84
  680. package/dist/mind/friends/group-context.js +0 -144
  681. package/dist/mind/friends/resolver.js +0 -84
  682. package/dist/mind/friends/store-file.js +0 -190
  683. package/dist/mind/friends/store.js +0 -4
  684. package/dist/mind/friends/tokens.js +0 -26
  685. package/dist/mind/friends/trust-explanation.js +0 -74
  686. package/dist/mind/friends/types.js +0 -29
  687. package/dist/repertoire/tasks/board.js +0 -134
  688. package/dist/repertoire/tasks/index.js +0 -224
  689. package/dist/repertoire/tasks/lifecycle.js +0 -80
  690. package/dist/repertoire/tasks/middleware.js +0 -65
  691. package/dist/repertoire/tasks/parser.js +0 -173
  692. package/dist/repertoire/tasks/scanner.js +0 -132
  693. package/dist/repertoire/tasks/transitions.js +0 -144
  694. package/dist/senses/bluebubbles-entry.js +0 -13
  695. package/dist/senses/bluebubbles.js +0 -1181
  696. package/dist/senses/debug-activity.js +0 -154
  697. package/dist/senses/inner-dialog-worker.js +0 -90
  698. package/dist/senses/inner-dialog.js +0 -525
  699. package/subagents/README.md +0 -7
  700. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  701. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  702. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  703. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  704. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  705. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  706. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  707. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  708. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  709. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  710. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  711. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  712. /package/dist/{repertoire/tasks/types.js → heart/attachments/sources/adapter.js} +0 -0
  713. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  714. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  715. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
  716. /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
@@ -33,16 +33,34 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.DaemonProcessManager = void 0;
36
+ exports.DaemonProcessManager = exports.PRIVATE_RUNTIME_DISPATCH_ACK_TIMEOUT_MS = exports.RESPAWN_GUARD_WINDOW_MS = exports.RESPAWN_GUARD_MAX_RESTARTS = void 0;
37
37
  const child_process_1 = require("child_process");
38
+ const crypto_1 = require("crypto");
38
39
  const path = __importStar(require("path"));
39
40
  const identity_1 = require("../identity");
40
41
  const runtime_1 = require("../../nerves/runtime");
41
42
  function startOfHour(ms) {
42
43
  return ms - 60 * 60 * 1000;
43
44
  }
45
+ /**
46
+ * Respawn-loop guard: refuse `restartAgent` if we've already orchestrated
47
+ * RESPAWN_GUARD_MAX_RESTARTS in the past RESPAWN_GUARD_WINDOW_MS.
48
+ *
49
+ * Calibrated for the 2026-05-11 BB sense incident: a misconfigured probe
50
+ * was triggering `restartAgent` every ~60s for hours. Five restarts in
51
+ * 10 minutes is well above the rate of legitimate operational restarts
52
+ * (a single human-initiated `ouro down && ouro up` produces one) and well
53
+ * below the rate of a death spiral (60/hr ⇒ 10/10min).
54
+ */
55
+ exports.RESPAWN_GUARD_MAX_RESTARTS = 5;
56
+ exports.RESPAWN_GUARD_WINDOW_MS = 10 * 60_000;
57
+ const MAX_PENDING_IPC_MESSAGES = 20;
58
+ exports.PRIVATE_RUNTIME_DISPATCH_ACK_TIMEOUT_MS = 8 * 60_000;
44
59
  class DaemonProcessManager {
45
60
  agents = new Map();
61
+ /** `stopAll()` is terminal for a manager instance. Once set, it fences
62
+ * detached restart/start continuations until the owning daemon exits. */
63
+ shutdownRequested = false;
46
64
  maxRestartsPerHour;
47
65
  stabilityThresholdMs;
48
66
  initialBackoffMs;
@@ -51,33 +69,130 @@ class DaemonProcessManager {
51
69
  now;
52
70
  setTimeoutFn;
53
71
  clearTimeoutFn;
72
+ cooldownRecoveryMs;
73
+ maxCooldownRetries;
74
+ startupStaleAfterMs;
75
+ stopTimeoutMs;
54
76
  existsSyncFn;
77
+ configCheckFn;
78
+ statusWriterFn;
79
+ onSnapshotChangeFn;
80
+ /**
81
+ * Notify the snapshot-change observer (if registered). Swallows any
82
+ * errors from the observer so process lifecycle code never fails
83
+ * because the observer threw.
84
+ */
85
+ notifySnapshotChange(snapshot) {
86
+ if (!this.onSnapshotChangeFn)
87
+ return;
88
+ try {
89
+ this.onSnapshotChangeFn(snapshot);
90
+ }
91
+ catch (error) {
92
+ (0, runtime_1.emitNervesEvent)({
93
+ level: "warn",
94
+ component: "daemon",
95
+ event: "daemon.snapshot_change_observer_error",
96
+ message: "snapshot-change observer threw",
97
+ meta: {
98
+ agent: snapshot.name,
99
+ error: error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error),
100
+ },
101
+ });
102
+ }
103
+ }
104
+ writeStatus(agent, text) {
105
+ try {
106
+ this.statusWriterFn(text);
107
+ }
108
+ catch (error) {
109
+ (0, runtime_1.emitNervesEvent)({
110
+ level: "warn",
111
+ component: "daemon",
112
+ event: "daemon.status_writer_error",
113
+ message: "daemon status writer threw",
114
+ meta: {
115
+ agent,
116
+ error: error instanceof Error ? error.message : String(error),
117
+ },
118
+ });
119
+ }
120
+ }
121
+ currentTimeMs() {
122
+ const value = this.now();
123
+ return Number.isFinite(value) ? value : Date.now();
124
+ }
125
+ isStartAttemptCurrent(state, attemptId) {
126
+ return state.startAttemptId === attemptId;
127
+ }
128
+ markConfigCheckFailed(state, errorReason, fixHint) {
129
+ const agent = state.config.name;
130
+ state.snapshot.status = "crashed";
131
+ state.snapshot.errorReason = errorReason;
132
+ state.snapshot.fixHint = fixHint;
133
+ (0, runtime_1.emitNervesEvent)({
134
+ level: "error",
135
+ component: "daemon",
136
+ event: "daemon.agent_config_invalid",
137
+ message: errorReason,
138
+ meta: { agent, fix: fixHint },
139
+ });
140
+ this.writeStatus(agent, `[daemon] ${agent}: ${errorReason}\n` +
141
+ (fixHint ? ` Fix: ${fixHint}\n` : ""));
142
+ this.notifySnapshotChange(state.snapshot);
143
+ }
55
144
  constructor(options) {
56
145
  this.maxRestartsPerHour = options.maxRestartsPerHour ?? 10;
57
146
  this.stabilityThresholdMs = options.stabilityThresholdMs ?? 60_000;
58
147
  this.initialBackoffMs = options.initialBackoffMs ?? 1_000;
59
148
  this.maxBackoffMs = options.maxBackoffMs ?? 60_000;
149
+ this.cooldownRecoveryMs = options.cooldownRecoveryMs ?? 5 * 60 * 1_000;
150
+ this.maxCooldownRetries = options.maxCooldownRetries ?? 3;
151
+ this.startupStaleAfterMs = options.startupStaleAfterMs ?? 45_000;
152
+ this.stopTimeoutMs = options.stopTimeoutMs ?? 4_000;
60
153
  this.spawnFn = options.spawn ?? ((command, args, spawnOptions) => (0, child_process_1.spawn)(command, args, spawnOptions));
61
154
  this.now = options.now ?? (() => Date.now());
62
155
  this.setTimeoutFn = options.setTimeoutFn ?? ((cb, delay) => setTimeout(cb, delay));
63
156
  this.clearTimeoutFn = options.clearTimeoutFn ?? ((timer) => clearTimeout(timer));
64
157
  this.existsSyncFn = options.existsSync ?? null;
158
+ this.configCheckFn = options.configCheck ?? null;
159
+ this.statusWriterFn = options.statusWriter ?? ((text) => {
160
+ process.stderr.write(text);
161
+ });
162
+ this.onSnapshotChangeFn = options.onSnapshotChange ?? null;
65
163
  for (const agent of options.agents) {
66
164
  this.agents.set(agent.name, {
67
165
  config: agent,
68
166
  process: null,
167
+ startInFlight: false,
168
+ startAttemptedAtMs: null,
169
+ startAttemptId: 0,
170
+ pendingIpcMessages: [],
171
+ pendingDispatches: new Map(),
69
172
  restartTimer: null,
70
173
  crashTimestamps: [],
174
+ orchestratedRestartTimestamps: [],
175
+ respawnLoopTripped: false,
71
176
  stopRequested: false,
177
+ cooldownTimer: null,
178
+ cooldownRetryCount: 0,
179
+ fastCrashCount: 0,
180
+ stopFailureActive: false,
181
+ displacedReason: null,
72
182
  snapshot: {
73
183
  name: agent.name,
74
184
  channel: agent.channel,
185
+ autoStart: agent.autoStart,
75
186
  status: "stopped",
76
187
  pid: null,
77
188
  restartCount: 0,
78
189
  startedAt: null,
79
190
  lastCrashAt: null,
80
191
  backoffMs: this.initialBackoffMs,
192
+ lastExitCode: null,
193
+ lastSignal: null,
194
+ errorReason: null,
195
+ fixHint: null,
81
196
  },
82
197
  });
83
198
  }
@@ -89,85 +204,508 @@ class DaemonProcessManager {
89
204
  }
90
205
  }
91
206
  }
92
- async startAgent(agent) {
207
+ triggerAutoStartAgents() {
208
+ for (const state of this.agents.values()) {
209
+ if (!state.config.autoStart)
210
+ continue;
211
+ void this.startAgent(state.config.name).catch((error) => {
212
+ const errorReason = error instanceof Error ? error.message : String(error);
213
+ this.markConfigCheckFailed(state, `agent startup threw before the worker could run: ${errorReason}`, "Run 'ouro doctor' for diagnostics, then retry 'ouro up'.");
214
+ });
215
+ }
216
+ }
217
+ async startAgent(agent, options = {}) {
93
218
  const state = this.requireAgent(agent);
94
- if (state.process)
219
+ if (this.shutdownRequested)
95
220
  return;
96
- this.clearRestartTimer(state);
97
- state.stopRequested = false;
98
- state.snapshot.status = "starting";
99
- const runCwd = (0, identity_1.getRepoRoot)();
100
- const entryScript = path.join((0, identity_1.getRepoRoot)(), "dist", state.config.entry);
101
- if (this.existsSyncFn && !this.existsSyncFn(entryScript)) {
102
- state.snapshot.status = "crashed";
221
+ if (state.displacedReason !== null)
222
+ return;
223
+ if (state.process || state.startInFlight)
224
+ return;
225
+ const attemptId = state.startAttemptId + 1;
226
+ state.startAttemptId = attemptId;
227
+ state.startInFlight = true;
228
+ state.startAttemptedAtMs = this.currentTimeMs();
229
+ try {
230
+ this.clearRestartTimer(state);
231
+ state.stopRequested = false;
232
+ state.snapshot.status = "starting";
233
+ if (this.configCheckFn && options.skipConfigCheck !== true) {
234
+ let result;
235
+ try {
236
+ result = await this.configCheckFn(agent);
237
+ }
238
+ catch (error) {
239
+ const errorReason = error instanceof Error ? error.message : String(error);
240
+ this.markConfigCheckFailed(state, `agent config validation threw: ${errorReason}`, "Run 'ouro doctor' for diagnostics, then retry 'ouro up'.");
241
+ return;
242
+ }
243
+ if (!this.isStartAttemptCurrent(state, attemptId))
244
+ return;
245
+ if (state.stopRequested) {
246
+ state.snapshot.status = "stopped";
247
+ state.snapshot.pid = null;
248
+ this.notifySnapshotChange(state.snapshot);
249
+ return;
250
+ }
251
+ if (result.skip) {
252
+ state.snapshot.status = "stopped";
253
+ state.snapshot.errorReason = null;
254
+ state.snapshot.fixHint = null;
255
+ (0, runtime_1.emitNervesEvent)({
256
+ component: "daemon",
257
+ event: "daemon.agent_config_skipped",
258
+ message: result.error ?? "agent start skipped by config check",
259
+ meta: { agent, fix: result.fix ?? null },
260
+ });
261
+ this.notifySnapshotChange(state.snapshot);
262
+ return;
263
+ }
264
+ if (!result.ok) {
265
+ this.markConfigCheckFailed(state, result.error ?? "agent config validation failed", result.fix ?? null);
266
+ return;
267
+ }
268
+ /* v8 ignore next -- defensive duplicate stale-start guard before spawn preparation @preserve */
269
+ if (!this.isStartAttemptCurrent(state, attemptId))
270
+ return;
271
+ // Config check passed — clear any prior error so the pulse stops
272
+ // reporting the broken state. This is the recovery path: the user
273
+ // fixed their secrets/config, the next startAgent attempt sees a
274
+ // valid config, and the pulse goes quiet.
275
+ state.snapshot.errorReason = null;
276
+ state.snapshot.fixHint = null;
277
+ }
278
+ if (options.skipConfigCheck === true) {
279
+ state.snapshot.errorReason = null;
280
+ state.snapshot.fixHint = null;
281
+ }
282
+ const runCwd = (0, identity_1.getRepoRoot)();
283
+ const entryScript = path.join((0, identity_1.getRepoRoot)(), "dist", state.config.entry);
284
+ if (this.existsSyncFn && !this.existsSyncFn(entryScript)) {
285
+ state.snapshot.status = "crashed";
286
+ (0, runtime_1.emitNervesEvent)({
287
+ level: "error",
288
+ component: "daemon",
289
+ event: "daemon.agent_entry_missing",
290
+ message: "agent entry script does not exist — cannot spawn. Run 'ouro daemon install' from the correct location.",
291
+ meta: { agent, entryScript },
292
+ });
293
+ this.notifySnapshotChange(state.snapshot);
294
+ return;
295
+ }
296
+ /* v8 ignore next -- defensive duplicate stale-start guard immediately before spawn @preserve */
297
+ if (!this.isStartAttemptCurrent(state, attemptId))
298
+ return;
299
+ if (state.stopRequested) {
300
+ state.snapshot.status = "stopped";
301
+ state.snapshot.pid = null;
302
+ this.notifySnapshotChange(state.snapshot);
303
+ return;
304
+ }
305
+ const agentArgs = state.config.getArgs?.() ?? state.config.args ?? [];
306
+ const args = [entryScript, "--agent", state.config.agentArg ?? agent, ...agentArgs];
307
+ const child = this.spawnFn("node", args, {
308
+ cwd: runCwd,
309
+ env: state.config.env ? { ...process.env, ...state.config.env } : process.env,
310
+ stdio: ["ignore", "ignore", "ignore", "ipc"],
311
+ });
312
+ /* v8 ignore next 7 -- defensive: spawn should always return a ChildProcess @preserve */
313
+ if (!child) {
314
+ state.snapshot.status = "crashed";
315
+ (0, runtime_1.emitNervesEvent)({ level: "error", component: "daemon", event: "daemon.agent_spawn_failed", message: "spawn returned null", meta: { agent } });
316
+ return;
317
+ }
318
+ if (!this.isStartAttemptCurrent(state, attemptId)) {
319
+ try {
320
+ child.kill("SIGTERM");
321
+ }
322
+ catch {
323
+ (0, runtime_1.emitNervesEvent)({
324
+ level: "warn",
325
+ component: "daemon",
326
+ event: "daemon.agent_stop_error",
327
+ message: "failed to stop stale managed agent startup",
328
+ meta: { agent },
329
+ });
330
+ }
331
+ return;
332
+ }
333
+ state.process = child;
334
+ state.snapshot.status = "running";
335
+ state.snapshot.pid = child.pid ?? null;
336
+ state.snapshot.startedAt = new Date(this.currentTimeMs()).toISOString();
337
+ child.on("message", (message) => {
338
+ if (state.process !== child || !message || typeof message !== "object")
339
+ return;
340
+ const result = message;
341
+ if (result.type !== "ouro.privateRuntimeDispatchResult" || typeof result.dispatchId !== "string")
342
+ return;
343
+ const pending = state.pendingDispatches.get(result.dispatchId);
344
+ if (!pending || pending.child !== child)
345
+ return;
346
+ state.pendingDispatches.delete(result.dispatchId);
347
+ this.clearTimeoutFn(pending.timer);
348
+ if (result.ok === true)
349
+ pending.resolve();
350
+ else
351
+ pending.reject(new Error(typeof result.error === "string" && result.error.trim() ? result.error.slice(0, 1_000) : "private-runtime dispatch failed"));
352
+ });
353
+ child.once("disconnect", () => {
354
+ if (state.process !== child)
355
+ return;
356
+ this.rejectPendingDispatchesForChild(state, child, new Error(`Managed agent '${agent}' disconnected before acknowledged dispatch completed.`));
357
+ });
358
+ const bootstrap = state.config.getRuntimeCredentialBootstrap?.() ?? null;
359
+ if (bootstrap) {
360
+ const message = {
361
+ type: "ouro.runtimeCredentialBootstrap",
362
+ agentName: bootstrap.agentName,
363
+ };
364
+ if (bootstrap.runtimeConfig)
365
+ message.runtimeConfig = bootstrap.runtimeConfig;
366
+ if (bootstrap.machineRuntimeConfig)
367
+ message.machineRuntimeConfig = bootstrap.machineRuntimeConfig;
368
+ if (bootstrap.machineId)
369
+ message.machineId = bootstrap.machineId;
370
+ if (bootstrap.providerCredentialRecords)
371
+ message.providerCredentialRecords = bootstrap.providerCredentialRecords;
372
+ try {
373
+ child.send?.(message);
374
+ (0, runtime_1.emitNervesEvent)({
375
+ component: "daemon",
376
+ event: "daemon.agent_runtime_credentials_bootstrap_sent",
377
+ message: "sent runtime credential bootstrap to managed agent process",
378
+ meta: {
379
+ agent,
380
+ runtimeConfig: !!bootstrap.runtimeConfig,
381
+ machineRuntimeConfig: !!bootstrap.machineRuntimeConfig,
382
+ providerCredentialRecords: bootstrap.providerCredentialRecords?.length ?? 0,
383
+ },
384
+ });
385
+ }
386
+ catch (error) {
387
+ (0, runtime_1.emitNervesEvent)({
388
+ level: "warn",
389
+ component: "daemon",
390
+ event: "daemon.agent_ipc_send_error",
391
+ message: "failed to send runtime credential bootstrap to managed agent process",
392
+ meta: { agent, error: error instanceof Error ? error.message : String(error) },
393
+ });
394
+ }
395
+ }
396
+ const pendingIpcMessages = state.pendingIpcMessages.splice(0);
397
+ for (const pendingMessage of pendingIpcMessages) {
398
+ const { message, dispatchId } = pendingMessage;
399
+ if (dispatchId) {
400
+ const pending = state.pendingDispatches.get(dispatchId);
401
+ if (!pending)
402
+ continue;
403
+ pending.child = child;
404
+ this.sendAcknowledgedMessage(state, child, dispatchId, message);
405
+ continue;
406
+ }
407
+ try {
408
+ child.send?.(message);
409
+ (0, runtime_1.emitNervesEvent)({
410
+ component: "daemon",
411
+ event: "daemon.agent_pending_ipc_flushed",
412
+ message: "flushed pending IPC message to managed agent",
413
+ meta: { agent },
414
+ });
415
+ }
416
+ catch (error) {
417
+ (0, runtime_1.emitNervesEvent)({
418
+ level: "warn",
419
+ component: "daemon",
420
+ event: "daemon.agent_ipc_send_error",
421
+ message: "failed to flush pending IPC message to managed agent",
422
+ meta: { agent, error: error instanceof Error ? error.message : String(error) },
423
+ });
424
+ }
425
+ }
103
426
  (0, runtime_1.emitNervesEvent)({
104
- level: "error",
105
427
  component: "daemon",
106
- event: "daemon.agent_entry_missing",
107
- message: "agent entry script does not exist — cannot spawn. Run 'ouro daemon install' from the correct location.",
108
- meta: { agent, entryScript },
428
+ event: "daemon.agent_started",
429
+ message: "daemon started managed agent process",
430
+ meta: { agent, pid: child.pid ?? null, cwd: runCwd },
431
+ });
432
+ this.notifySnapshotChange(state.snapshot);
433
+ /* v8 ignore start — child process error handler; requires real spawn to trigger */
434
+ child.on("error", (err) => {
435
+ (0, runtime_1.emitNervesEvent)({
436
+ level: "warn",
437
+ component: "daemon",
438
+ event: "daemon.agent_process_error",
439
+ message: "managed agent process emitted error",
440
+ meta: { agent, error: err.message },
441
+ });
442
+ });
443
+ /* v8 ignore stop */
444
+ child.once("exit", (code, signal) => {
445
+ this.onExit(state, child, code, signal);
109
446
  });
110
- return;
111
447
  }
112
- const args = [entryScript, "--agent", state.config.agentArg ?? agent, ...(state.config.args ?? [])];
113
- const child = this.spawnFn("node", args, {
114
- cwd: runCwd,
115
- env: state.config.env ? { ...process.env, ...state.config.env } : process.env,
116
- stdio: ["ignore", "ignore", "ignore", "ipc"],
117
- });
118
- state.process = child;
119
- state.snapshot.status = "running";
120
- state.snapshot.pid = child.pid ?? null;
121
- state.snapshot.startedAt = new Date(this.now()).toISOString();
122
- (0, runtime_1.emitNervesEvent)({
123
- component: "daemon",
124
- event: "daemon.agent_started",
125
- message: "daemon started managed agent process",
126
- meta: { agent, pid: child.pid ?? null, cwd: runCwd },
127
- });
128
- child.once("exit", (code, signal) => {
129
- this.onExit(state, code, signal);
130
- });
448
+ finally {
449
+ if (this.isStartAttemptCurrent(state, attemptId)) {
450
+ state.startInFlight = false;
451
+ state.startAttemptedAtMs = null;
452
+ if (!state.process) {
453
+ this.rejectPendingDispatchesForChild(state, null, new Error(`Managed agent '${agent}' startup ended before acknowledged dispatch completed.`));
454
+ }
455
+ }
456
+ }
131
457
  }
132
458
  async stopAgent(agent) {
133
459
  const state = this.requireAgent(agent);
134
460
  this.clearRestartTimer(state);
461
+ this.clearCooldownTimer(state);
135
462
  state.stopRequested = true;
463
+ for (const [dispatchId] of state.pendingDispatches) {
464
+ this.rejectPendingDispatch(state, dispatchId, new Error(`Managed agent '${agent}' stopped before acknowledged dispatch completed.`));
465
+ }
466
+ state.pendingIpcMessages = [];
467
+ // NOTE: do not touch state.respawnLoopTripped / orchestratedRestartTimestamps
468
+ // here. restartAgent calls stopAgent internally; clearing the guard here
469
+ // would reset the window every cycle and defeat the loop-detection. The
470
+ // guard self-clears when timestamps age out of the window (handled inside
471
+ // restartAgent at the prune step).
136
472
  if (!state.process) {
137
473
  state.snapshot.status = "stopped";
138
474
  state.snapshot.pid = null;
475
+ this.notifySnapshotChange(state.snapshot);
139
476
  return;
140
477
  }
141
478
  const child = state.process;
142
- state.process = null;
143
- state.snapshot.status = "stopped";
144
- state.snapshot.pid = null;
145
- try {
146
- child.kill("SIGTERM");
479
+ const childPid = child.pid ?? null;
480
+ const childPidLabel = childPid === null ? "unknown" : String(childPid);
481
+ await new Promise((resolve, reject) => {
482
+ let settled = false;
483
+ let timeout = null;
484
+ const finish = (error) => {
485
+ /* v8 ignore next -- defensive idempotence if an injected timer fires after cancellation @preserve */
486
+ if (settled)
487
+ return;
488
+ settled = true;
489
+ child.removeListener("exit", onExit);
490
+ if (timeout !== null)
491
+ this.clearTimeoutFn(timeout);
492
+ if (error)
493
+ reject(error);
494
+ else
495
+ resolve();
496
+ };
497
+ const onExit = () => finish();
498
+ child.once("exit", onExit);
499
+ let signalAccepted = false;
500
+ try {
501
+ signalAccepted = child.kill("SIGTERM");
502
+ }
503
+ catch {
504
+ const errorReason = "failed to send SIGTERM to managed agent; worker remains tracked";
505
+ state.stopFailureActive = true;
506
+ state.snapshot.errorReason = errorReason;
507
+ state.snapshot.fixHint = `Inspect managed worker PID ${childPidLabel} before retrying the stop or restart.`;
508
+ (0, runtime_1.emitNervesEvent)({
509
+ level: "error",
510
+ component: "daemon",
511
+ event: "daemon.agent_stop_error",
512
+ message: errorReason,
513
+ meta: { agent, pid: childPid },
514
+ });
515
+ this.notifySnapshotChange(state.snapshot);
516
+ finish(new Error(errorReason));
517
+ return;
518
+ }
519
+ if (!signalAccepted) {
520
+ const errorReason = "managed agent rejected SIGTERM; worker remains tracked";
521
+ state.stopFailureActive = true;
522
+ state.snapshot.errorReason = errorReason;
523
+ state.snapshot.fixHint = `Inspect managed worker PID ${childPidLabel} before retrying the stop or restart.`;
524
+ (0, runtime_1.emitNervesEvent)({
525
+ level: "error",
526
+ component: "daemon",
527
+ event: "daemon.agent_stop_error",
528
+ message: errorReason,
529
+ meta: { agent, pid: childPid },
530
+ });
531
+ this.notifySnapshotChange(state.snapshot);
532
+ finish(new Error(errorReason));
533
+ return;
534
+ }
535
+ if (settled)
536
+ return;
537
+ timeout = this.setTimeoutFn(() => {
538
+ if (settled)
539
+ return;
540
+ const errorReason = `managed agent did not exit within ${this.stopTimeoutMs}ms; replacement was not started`;
541
+ state.stopFailureActive = true;
542
+ state.snapshot.errorReason = errorReason;
543
+ state.snapshot.fixHint = `Inspect managed worker PID ${childPidLabel}; retry only after that worker has exited.`;
544
+ (0, runtime_1.emitNervesEvent)({
545
+ level: "error",
546
+ component: "daemon",
547
+ event: "daemon.agent_stop_timeout",
548
+ message: errorReason,
549
+ meta: { agent, pid: childPid, timeoutMs: this.stopTimeoutMs },
550
+ });
551
+ this.notifySnapshotChange(state.snapshot);
552
+ finish(new Error(errorReason));
553
+ }, this.stopTimeoutMs);
554
+ });
555
+ }
556
+ /**
557
+ * Stop an agent whose home moved to another machine and keep it stopped:
558
+ * it is parked (health checks no longer restart it) and no later start,
559
+ * wake or restart runs it again in this daemon. `ouro up` replaces the
560
+ * daemon with one that does not manage it at all.
561
+ */
562
+ async displaceAgent(agent, reason) {
563
+ const state = this.requireAgent(agent);
564
+ if (state.displacedReason !== null)
565
+ return;
566
+ state.displacedReason = reason;
567
+ state.snapshot.autoStart = false;
568
+ state.snapshot.errorReason = reason;
569
+ state.snapshot.fixHint = "Run 'ouro up' on this machine to stop managing it, or 'ouro move <agent> here' to take it back.";
570
+ (0, runtime_1.emitNervesEvent)({
571
+ level: "warn",
572
+ component: "daemon",
573
+ event: "daemon.agent_displaced",
574
+ message: "stopping agent homed on another machine",
575
+ meta: { agent, reason },
576
+ });
577
+ this.notifySnapshotChange(state.snapshot);
578
+ await this.stopAgent(agent);
579
+ }
580
+ async restartAgent(agent, options = {}) {
581
+ const state = this.requireAgent(agent);
582
+ if (this.shutdownRequested)
583
+ return;
584
+ // Respawn-loop guard: prune timestamps outside the window, then check
585
+ // whether we've already restarted this agent too many times in it.
586
+ const now = this.now();
587
+ const windowStart = now - exports.RESPAWN_GUARD_WINDOW_MS;
588
+ state.orchestratedRestartTimestamps = state.orchestratedRestartTimestamps.filter((ts) => ts >= windowStart);
589
+ // If the window is now empty, the trip naturally self-clears. That means
590
+ // after RESPAWN_GUARD_WINDOW_MS of no restart attempts, the daemon is
591
+ // willing to try again (e.g. for a fresh health probe failure that has
592
+ // nothing to do with the original loop).
593
+ if (state.respawnLoopTripped && state.orchestratedRestartTimestamps.length === 0) {
594
+ state.respawnLoopTripped = false;
595
+ state.snapshot.errorReason = null;
596
+ state.snapshot.fixHint = null;
597
+ (0, runtime_1.emitNervesEvent)({
598
+ component: "daemon",
599
+ event: "daemon.agent_respawn_loop_cleared",
600
+ message: "respawn-loop guard cleared by window-aging",
601
+ meta: { agent, windowMs: exports.RESPAWN_GUARD_WINDOW_MS },
602
+ });
603
+ this.notifySnapshotChange(state.snapshot);
147
604
  }
148
- catch {
605
+ if (state.respawnLoopTripped) {
606
+ (0, runtime_1.emitNervesEvent)({
607
+ level: "error",
608
+ component: "daemon",
609
+ event: "daemon.agent_respawn_loop_blocked",
610
+ message: "refused agent restart — respawn-loop guard tripped; manual intervention required",
611
+ meta: {
612
+ agent,
613
+ recentRestartCount: state.orchestratedRestartTimestamps.length,
614
+ windowMs: exports.RESPAWN_GUARD_WINDOW_MS,
615
+ },
616
+ });
617
+ return;
618
+ }
619
+ if (state.orchestratedRestartTimestamps.length >= exports.RESPAWN_GUARD_MAX_RESTARTS) {
620
+ state.respawnLoopTripped = true;
621
+ state.snapshot.errorReason = `respawn loop detected: ${exports.RESPAWN_GUARD_MAX_RESTARTS}+ restarts in ${Math.round(exports.RESPAWN_GUARD_WINDOW_MS / 60_000)}min — refusing further restarts`;
622
+ state.snapshot.fixHint = "investigate the root cause then run `ouro up` to resume";
623
+ (0, runtime_1.emitNervesEvent)({
624
+ level: "error",
625
+ component: "daemon",
626
+ event: "daemon.agent_respawn_loop_tripped",
627
+ message: "respawn-loop guard tripped; further restarts blocked",
628
+ meta: {
629
+ agent,
630
+ restartCount: state.orchestratedRestartTimestamps.length,
631
+ windowMs: exports.RESPAWN_GUARD_WINDOW_MS,
632
+ maxRestarts: exports.RESPAWN_GUARD_MAX_RESTARTS,
633
+ },
634
+ });
635
+ this.notifySnapshotChange(state.snapshot);
636
+ return;
637
+ }
638
+ state.orchestratedRestartTimestamps.push(now);
639
+ if (state.startInFlight && !state.process) {
640
+ const startedAt = state.startAttemptedAtMs;
641
+ /* v8 ignore next -- defensive: startInFlight always records a start timestamp @preserve */
642
+ const elapsedMs = startedAt === null ? 0 : this.currentTimeMs() - startedAt;
643
+ if (elapsedMs < this.startupStaleAfterMs) {
644
+ (0, runtime_1.emitNervesEvent)({
645
+ component: "daemon",
646
+ event: "daemon.agent_restart_deferred",
647
+ message: "managed agent restart skipped while startup is already in flight",
648
+ meta: { agent, elapsedMs, startupStaleAfterMs: this.startupStaleAfterMs },
649
+ });
650
+ return;
651
+ }
149
652
  (0, runtime_1.emitNervesEvent)({
150
653
  level: "warn",
151
654
  component: "daemon",
152
- event: "daemon.agent_stop_error",
153
- message: "failed to send SIGTERM to managed agent",
154
- meta: { agent },
655
+ event: "daemon.agent_startup_stale_recovered",
656
+ message: "managed agent startup was stale; clearing the pending attempt before restart",
657
+ meta: { agent, elapsedMs, startupStaleAfterMs: this.startupStaleAfterMs },
155
658
  });
659
+ state.startAttemptId += 1;
660
+ state.startInFlight = false;
661
+ state.startAttemptedAtMs = null;
156
662
  }
157
- }
158
- async restartAgent(agent) {
159
663
  await this.stopAgent(agent);
160
- await this.startAgent(agent);
664
+ await this.startAgent(agent, options);
161
665
  }
162
666
  async stopAll() {
163
- for (const state of this.agents.values()) {
164
- await this.stopAgent(state.config.name);
667
+ this.shutdownRequested = true;
668
+ const states = [...this.agents.values()];
669
+ const results = await Promise.allSettled(states.map((state) => this.stopAgent(state.config.name)));
670
+ const failures = results.flatMap((result, index) => result.status === "rejected"
671
+ ? [{ agent: states[index].config.name, reason: result.reason }]
672
+ : []);
673
+ if (failures.length > 0) {
674
+ throw new AggregateError(failures.map((failure) => failure.reason), `failed to stop ${failures.length} managed worker(s): ${failures.map((failure) => failure.agent).join(", ")}`);
165
675
  }
166
676
  }
677
+ resetAgentFailureState(agent) {
678
+ const state = this.requireAgent(agent);
679
+ this.clearRestartTimer(state);
680
+ this.clearCooldownTimer(state);
681
+ state.cooldownRetryCount = 0;
682
+ state.crashTimestamps = [];
683
+ state.fastCrashCount = 0;
684
+ state.respawnLoopTripped = false;
685
+ state.orchestratedRestartTimestamps = [];
686
+ state.snapshot.errorReason = null;
687
+ state.snapshot.fixHint = null;
688
+ this.notifySnapshotChange(state.snapshot);
689
+ }
167
690
  sendToAgent(agent, message) {
168
691
  const state = this.requireAgent(agent);
169
- if (!state.process)
692
+ if (!state.process) {
693
+ if (state.startInFlight) {
694
+ if (state.pendingIpcMessages.length >= MAX_PENDING_IPC_MESSAGES) {
695
+ const evicted = state.pendingIpcMessages.shift();
696
+ if (evicted?.dispatchId)
697
+ this.rejectPendingDispatch(state, evicted.dispatchId, new Error(`Managed agent '${agent}' startup IPC queue evicted acknowledged dispatch.`));
698
+ }
699
+ state.pendingIpcMessages.push({ message });
700
+ (0, runtime_1.emitNervesEvent)({
701
+ component: "daemon",
702
+ event: "daemon.agent_ipc_queued_during_startup",
703
+ message: "queued IPC message while managed agent startup is in flight",
704
+ meta: { agent, queuedCount: state.pendingIpcMessages.length },
705
+ });
706
+ }
170
707
  return;
708
+ }
171
709
  try {
172
710
  state.process.send(message);
173
711
  }
@@ -181,47 +719,168 @@ class DaemonProcessManager {
181
719
  });
182
720
  }
183
721
  }
722
+ dispatchToAgent(agent, message, options = {}) {
723
+ const state = this.requireAgent(agent);
724
+ const dispatchId = (0, crypto_1.randomUUID)();
725
+ const timeoutMs = options.timeoutMs ?? exports.PRIVATE_RUNTIME_DISPATCH_ACK_TIMEOUT_MS;
726
+ return new Promise((resolve, reject) => {
727
+ const timer = this.setTimeoutFn(() => {
728
+ const pending = state.pendingDispatches.get(dispatchId);
729
+ if (!pending)
730
+ return;
731
+ state.pendingDispatches.delete(dispatchId);
732
+ if (pending.child && state.process === pending.child) {
733
+ try {
734
+ pending.child.send?.({ type: "ouro.privateRuntimeDispatchCancel", dispatchId });
735
+ }
736
+ catch { /* cancellation is best effort */ }
737
+ }
738
+ reject(new Error(`Private-runtime dispatch timed out after ${timeoutMs}ms.`));
739
+ }, timeoutMs);
740
+ const child = state.process?.connected === false ? null : state.process;
741
+ state.pendingDispatches.set(dispatchId, { child, timer, resolve, reject });
742
+ if (child) {
743
+ this.sendAcknowledgedMessage(state, child, dispatchId, message);
744
+ return;
745
+ }
746
+ if (state.startInFlight) {
747
+ if (state.pendingIpcMessages.length >= MAX_PENDING_IPC_MESSAGES) {
748
+ const evicted = state.pendingIpcMessages.shift();
749
+ if (evicted?.dispatchId)
750
+ this.rejectPendingDispatch(state, evicted.dispatchId, new Error(`Managed agent '${agent}' startup IPC queue evicted acknowledged dispatch.`));
751
+ }
752
+ state.pendingIpcMessages.push({ message, dispatchId });
753
+ return;
754
+ }
755
+ this.rejectPendingDispatch(state, dispatchId, new Error(`Managed agent '${agent}' is unavailable for acknowledged dispatch.`));
756
+ });
757
+ }
758
+ rejectPendingDispatch(state, dispatchId, error) {
759
+ const pending = state.pendingDispatches.get(dispatchId);
760
+ if (!pending)
761
+ return;
762
+ state.pendingDispatches.delete(dispatchId);
763
+ this.clearTimeoutFn(pending.timer);
764
+ pending.reject(error);
765
+ }
766
+ rejectPendingDispatchesForChild(state, child, error) {
767
+ for (const [dispatchId, pending] of state.pendingDispatches) {
768
+ if (pending.child === child)
769
+ this.rejectPendingDispatch(state, dispatchId, error);
770
+ }
771
+ if (child === null)
772
+ state.pendingIpcMessages = state.pendingIpcMessages.filter((pending) => pending.dispatchId === undefined);
773
+ }
774
+ sendAcknowledgedMessage(state, child, dispatchId, message) {
775
+ try {
776
+ child.send?.({ ...message, dispatchId }, (error) => {
777
+ if (!error)
778
+ return;
779
+ const pending = state.pendingDispatches.get(dispatchId);
780
+ if (!pending || pending.child !== child)
781
+ return;
782
+ this.rejectPendingDispatch(state, dispatchId, error);
783
+ });
784
+ }
785
+ catch (error) {
786
+ this.rejectPendingDispatch(state, dispatchId, error instanceof Error ? error : new Error(String(error)));
787
+ }
788
+ }
184
789
  getAgentSnapshot(agent) {
185
790
  return this.agents.get(agent)?.snapshot;
186
791
  }
187
792
  listAgentSnapshots() {
188
793
  return [...this.agents.values()].map((state) => state.snapshot);
189
794
  }
190
- onExit(state, code, signal) {
191
- if (!state.process)
795
+ onExit(state, child, code, signal) {
796
+ /* v8 ignore next -- defensive: replacement cannot start before this child's one-shot exit listener drains @preserve */
797
+ if (state.process !== child)
192
798
  return;
799
+ this.rejectPendingDispatchesForChild(state, child, new Error(`Managed agent '${state.config.name}' exited before acknowledged dispatch completed.`));
193
800
  state.process = null;
801
+ state.startInFlight = false;
802
+ state.startAttemptedAtMs = null;
194
803
  state.snapshot.pid = null;
195
- const crashed = !state.stopRequested && code !== 0;
196
- const now = this.now();
804
+ state.snapshot.lastExitCode = code;
805
+ state.snapshot.lastSignal = signal;
806
+ if (state.stopFailureActive) {
807
+ state.stopFailureActive = false;
808
+ state.snapshot.errorReason = null;
809
+ state.snapshot.fixHint = null;
810
+ }
811
+ const unexpectedExit = !state.stopRequested;
812
+ const crashed = unexpectedExit && code !== 0;
813
+ const now = this.currentTimeMs();
197
814
  const startedAt = state.snapshot.startedAt ? Date.parse(state.snapshot.startedAt) : now;
198
815
  const runDuration = Math.max(0, now - startedAt);
199
816
  (0, runtime_1.emitNervesEvent)({
200
- level: crashed ? "warn" : "info",
817
+ level: unexpectedExit ? "warn" : "info",
201
818
  component: "daemon",
202
819
  event: "daemon.agent_exit",
203
820
  message: "managed agent process exited",
204
- meta: { agent: state.config.name, code, signal, crashed, runDurationMs: runDuration },
821
+ meta: { agent: state.config.name, code, signal, crashed, unexpectedExit, runDurationMs: runDuration },
205
822
  });
206
- if (!crashed) {
823
+ if (runDuration >= this.stabilityThresholdMs) {
824
+ state.snapshot.backoffMs = this.initialBackoffMs;
825
+ }
826
+ if (!unexpectedExit) {
207
827
  state.snapshot.status = "stopped";
208
- if (runDuration >= this.stabilityThresholdMs) {
209
- state.snapshot.backoffMs = this.initialBackoffMs;
210
- }
828
+ this.notifySnapshotChange(state.snapshot);
211
829
  return;
212
830
  }
213
- state.snapshot.lastCrashAt = new Date(now).toISOString();
831
+ if (crashed) {
832
+ state.snapshot.lastCrashAt = new Date(now).toISOString();
833
+ }
834
+ if (state.config.autoStart === false) {
835
+ state.snapshot.status = "stopped";
836
+ state.snapshot.errorReason = "non-autostart worker exited unexpectedly; not respawning";
837
+ state.snapshot.fixHint = "Start it explicitly after checking the exit cause.";
838
+ this.notifySnapshotChange(state.snapshot);
839
+ return;
840
+ }
841
+ // Fast-exit detection: repeated early exits, including clean exit-code-0
842
+ // terminations, indicate a startup/configuration or lifecycle fault rather
843
+ // than a stable long-lived worker. Bound them without claiming a crash.
844
+ const FAST_CRASH_THRESHOLD_MS = 5000;
845
+ const FAST_CRASH_MAX = 3;
846
+ if (runDuration < FAST_CRASH_THRESHOLD_MS) {
847
+ state.fastCrashCount = state.fastCrashCount + 1;
848
+ if (state.fastCrashCount >= FAST_CRASH_MAX) {
849
+ state.snapshot.status = "crashed";
850
+ // Capture the fast-exit diagnosis on the snapshot so it surfaces via
851
+ // the pulse with a prescriptive, cause-neutral repair path.
852
+ state.snapshot.errorReason = `agent exited unexpectedly ${FAST_CRASH_MAX} times within ${FAST_CRASH_THRESHOLD_MS}ms of startup — likely a startup/configuration or lifecycle issue.`;
853
+ state.snapshot.fixHint = `Inspect daemon logs and lifecycle/configuration, then run \`ouro up\` to restart.`;
854
+ (0, runtime_1.emitNervesEvent)({
855
+ level: "error",
856
+ component: "daemon",
857
+ event: "daemon.agent_config_failure",
858
+ message: `agent exited unexpectedly ${FAST_CRASH_MAX} times within ${FAST_CRASH_THRESHOLD_MS}ms of startup. Not retrying. Inspect lifecycle/configuration and run \`ouro up\` to restart.`,
859
+ meta: { agent: state.config.name, fastUnexpectedExitCount: state.fastCrashCount, avgRunDurationMs: runDuration },
860
+ });
861
+ this.notifySnapshotChange(state.snapshot);
862
+ return; // Don't schedule cooldown recovery — this needs human/agent intervention
863
+ }
864
+ }
865
+ else {
866
+ // Reset the fast unexpected-exit counter on a stable run.
867
+ state.fastCrashCount = 0;
868
+ }
214
869
  state.crashTimestamps = state.crashTimestamps.filter((crashTs) => crashTs >= startOfHour(now));
215
870
  state.crashTimestamps.push(now);
216
871
  if (state.crashTimestamps.length > this.maxRestartsPerHour) {
217
872
  state.snapshot.status = "crashed";
873
+ state.snapshot.errorReason = `agent exceeded unexpected-exit restart limit (${this.maxRestartsPerHour}/hr) — entering cooldown`;
874
+ state.snapshot.fixHint = "investigate why the agent keeps exiting unexpectedly; cooldown will retry shortly";
218
875
  (0, runtime_1.emitNervesEvent)({
219
876
  level: "error",
220
877
  component: "daemon",
221
878
  event: "daemon.agent_restart_exhausted",
222
- message: "managed agent exceeded restart limit and is marked crashed",
879
+ message: "managed agent exceeded unexpected-exit restart limit and is marked crashed",
223
880
  meta: { agent: state.config.name, maxRestartsPerHour: this.maxRestartsPerHour },
224
881
  });
882
+ this.notifySnapshotChange(state.snapshot);
883
+ this.scheduleCooldownRecovery(state);
225
884
  return;
226
885
  }
227
886
  state.snapshot.status = "starting";
@@ -232,6 +891,7 @@ class DaemonProcessManager {
232
891
  state.restartTimer = this.setTimeoutFn(() => {
233
892
  void this.startAgent(state.config.name);
234
893
  }, waitMs);
894
+ this.notifySnapshotChange(state.snapshot);
235
895
  }
236
896
  clearRestartTimer(state) {
237
897
  if (state.restartTimer === null)
@@ -239,6 +899,45 @@ class DaemonProcessManager {
239
899
  this.clearTimeoutFn(state.restartTimer);
240
900
  state.restartTimer = null;
241
901
  }
902
+ scheduleCooldownRecovery(state) {
903
+ if (state.cooldownRetryCount >= this.maxCooldownRetries) {
904
+ (0, runtime_1.emitNervesEvent)({
905
+ level: "error",
906
+ component: "daemon",
907
+ event: "daemon.agent_permanent_failure",
908
+ message: "managed agent exhausted all cooldown retries — permanently stopped",
909
+ meta: { agent: state.config.name, cooldownRetryCount: state.cooldownRetryCount, maxCooldownRetries: this.maxCooldownRetries },
910
+ });
911
+ return;
912
+ }
913
+ this.clearCooldownTimer(state);
914
+ state.cooldownTimer = this.setTimeoutFn(() => {
915
+ state.cooldownRetryCount += 1;
916
+ state.crashTimestamps = [];
917
+ state.snapshot.backoffMs = this.initialBackoffMs;
918
+ state.snapshot.status = "starting";
919
+ state.snapshot.restartCount += 1;
920
+ (0, runtime_1.emitNervesEvent)({
921
+ component: "daemon",
922
+ event: "daemon.agent_cooldown_recovery",
923
+ message: "attempting cooldown recovery for managed agent",
924
+ meta: { agent: state.config.name, cooldownRetryCount: state.cooldownRetryCount },
925
+ });
926
+ void this.startAgent(state.config.name);
927
+ }, this.cooldownRecoveryMs);
928
+ (0, runtime_1.emitNervesEvent)({
929
+ component: "daemon",
930
+ event: "daemon.agent_cooldown_scheduled",
931
+ message: `scheduled cooldown recovery in ${this.cooldownRecoveryMs}ms`,
932
+ meta: { agent: state.config.name, cooldownRecoveryMs: this.cooldownRecoveryMs, cooldownRetryCount: state.cooldownRetryCount },
933
+ });
934
+ }
935
+ clearCooldownTimer(state) {
936
+ if (state.cooldownTimer === null)
937
+ return;
938
+ this.clearTimeoutFn(state.cooldownTimer);
939
+ state.cooldownTimer = null;
940
+ }
242
941
  requireAgent(agent) {
243
942
  const state = this.agents.get(agent);
244
943
  if (!state) {