@ouro.bot/cli 0.1.0-alpha.61 → 0.1.0-alpha.611

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 (416) hide show
  1. package/README.md +127 -23
  2. package/RepairGuide.ouro/agent.json +5 -0
  3. package/RepairGuide.ouro/psyche/IDENTITY.md +19 -0
  4. package/RepairGuide.ouro/psyche/SOUL.md +55 -0
  5. package/RepairGuide.ouro/skills/diagnose-broken-remote.md +63 -0
  6. package/RepairGuide.ouro/skills/diagnose-stacked-typed-issues.md +35 -0
  7. package/RepairGuide.ouro/skills/diagnose-sync-blocked.md +54 -0
  8. package/RepairGuide.ouro/skills/diagnose-vault-expired.md +60 -0
  9. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +4 -2
  10. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +2 -2
  11. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +1 -1
  12. package/changelog.json +3902 -0
  13. package/dist/arc/attention-types.js +8 -0
  14. package/dist/arc/cares.js +140 -0
  15. package/dist/arc/episodes.js +117 -0
  16. package/dist/arc/intentions.js +133 -0
  17. package/dist/arc/json-store.js +117 -0
  18. package/dist/arc/obligations.js +254 -0
  19. package/dist/arc/packets.js +193 -0
  20. package/dist/arc/presence.js +185 -0
  21. package/dist/arc/task-lifecycle.js +65 -0
  22. package/dist/heart/active-work.js +837 -26
  23. package/dist/heart/agent-entry.js +69 -3
  24. package/dist/heart/attachments/image-normalize.js +194 -0
  25. package/dist/heart/attachments/materialize.js +97 -0
  26. package/dist/heart/attachments/originals.js +88 -0
  27. package/dist/heart/attachments/render.js +29 -0
  28. package/dist/heart/attachments/sources/adapter.js +2 -0
  29. package/dist/heart/attachments/sources/bluebubbles.js +156 -0
  30. package/dist/heart/attachments/sources/cli-local-file.js +78 -0
  31. package/dist/heart/attachments/sources/index.js +16 -0
  32. package/dist/heart/attachments/store.js +103 -0
  33. package/dist/heart/attachments/types.js +93 -0
  34. package/dist/heart/auth/auth-flow.js +479 -0
  35. package/dist/heart/awaiting/await-alert.js +146 -0
  36. package/dist/heart/awaiting/await-expiry.js +108 -0
  37. package/dist/heart/awaiting/await-loader.js +91 -0
  38. package/dist/heart/awaiting/await-parser.js +141 -0
  39. package/dist/heart/awaiting/await-runtime-state.js +97 -0
  40. package/dist/heart/awaiting/await-scheduler.js +377 -0
  41. package/dist/heart/background-operations.js +281 -0
  42. package/dist/heart/bundle-state.js +168 -0
  43. package/dist/heart/commitments.js +142 -0
  44. package/dist/heart/config-registry.js +322 -0
  45. package/dist/heart/config.js +114 -119
  46. package/dist/heart/core.js +909 -246
  47. package/dist/heart/cross-chat-delivery.js +3 -18
  48. package/dist/heart/daemon/agent-config-check.js +419 -0
  49. package/dist/heart/daemon/agent-discovery.js +102 -3
  50. package/dist/heart/daemon/agent-service.js +522 -0
  51. package/dist/heart/daemon/agentic-repair.js +547 -0
  52. package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
  53. package/dist/heart/daemon/boot-sync-probe.js +197 -0
  54. package/dist/heart/daemon/cadence.js +70 -0
  55. package/dist/heart/daemon/cli-defaults.js +776 -0
  56. package/dist/heart/daemon/cli-exec.js +7579 -0
  57. package/dist/heart/daemon/cli-help.js +498 -0
  58. package/dist/heart/daemon/cli-parse.js +1599 -0
  59. package/dist/heart/daemon/cli-render-doctor.js +57 -0
  60. package/dist/heart/daemon/cli-render.js +763 -0
  61. package/dist/heart/daemon/cli-types.js +8 -0
  62. package/dist/heart/daemon/connect-bay.js +323 -0
  63. package/dist/heart/daemon/daemon-cli.js +29 -1700
  64. package/dist/heart/daemon/daemon-entry.js +485 -2
  65. package/dist/heart/daemon/daemon-health.js +176 -0
  66. package/dist/heart/daemon/daemon-rollup.js +57 -0
  67. package/dist/heart/daemon/daemon-runtime-sync.js +88 -13
  68. package/dist/heart/daemon/daemon-tombstone.js +236 -0
  69. package/dist/heart/daemon/daemon.js +857 -70
  70. package/dist/heart/daemon/dns-workflow.js +394 -0
  71. package/dist/heart/daemon/doctor-types.js +8 -0
  72. package/dist/heart/daemon/doctor.js +873 -0
  73. package/dist/heart/daemon/health-monitor.js +122 -1
  74. package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
  75. package/dist/heart/daemon/hooks/bundle-meta.js +115 -1
  76. package/dist/heart/daemon/http-health-probe.js +80 -0
  77. package/dist/heart/daemon/human-command-screens.js +234 -0
  78. package/dist/heart/daemon/human-readiness.js +114 -0
  79. package/dist/heart/daemon/inner-status.js +89 -0
  80. package/dist/heart/daemon/interactive-repair.js +394 -0
  81. package/dist/heart/daemon/launchd.js +37 -8
  82. package/dist/heart/daemon/log-tailer.js +78 -9
  83. package/dist/heart/daemon/logs-prune.js +110 -0
  84. package/dist/heart/daemon/mcp-canary.js +297 -0
  85. package/dist/heart/daemon/os-cron-deps.js +135 -0
  86. package/dist/heart/daemon/os-cron.js +14 -12
  87. package/dist/heart/daemon/ouro-bot-entry.js +4 -2
  88. package/dist/heart/daemon/ouro-entry.js +3 -1
  89. package/dist/heart/daemon/process-manager.js +450 -34
  90. package/dist/heart/daemon/provider-discovery.js +137 -0
  91. package/dist/heart/daemon/provider-ping-progress.js +83 -0
  92. package/dist/heart/daemon/pulse.js +475 -0
  93. package/dist/heart/daemon/readiness-repair.js +365 -0
  94. package/dist/heart/daemon/run-hooks.js +2 -0
  95. package/dist/heart/daemon/runtime-logging.js +10 -2
  96. package/dist/heart/daemon/runtime-metadata.js +2 -30
  97. package/dist/heart/daemon/safe-mode.js +161 -0
  98. package/dist/heart/daemon/sense-manager.js +481 -38
  99. package/dist/heart/daemon/session-id-resolver.js +131 -0
  100. package/dist/heart/daemon/skill-management-installer.js +28 -7
  101. package/dist/heart/daemon/socket-client.js +158 -11
  102. package/dist/heart/daemon/stale-bundle-prune.js +96 -0
  103. package/dist/heart/daemon/startup-tui.js +330 -0
  104. package/dist/heart/daemon/task-scheduler.js +3 -25
  105. package/dist/heart/daemon/terminal-ui.js +499 -0
  106. package/dist/heart/daemon/thoughts.js +229 -17
  107. package/dist/heart/daemon/up-progress.js +366 -0
  108. package/dist/heart/daemon/vault-items.js +56 -0
  109. package/dist/heart/delegation.js +1 -1
  110. package/dist/heart/habits/habit-migration.js +189 -0
  111. package/dist/heart/habits/habit-parser.js +140 -0
  112. package/dist/heart/habits/habit-runtime-state.js +100 -0
  113. package/dist/heart/habits/habit-scheduler.js +372 -0
  114. package/dist/heart/{daemon → hatch}/hatch-flow.js +32 -56
  115. package/dist/heart/{daemon → hatch}/hatch-specialist.js +6 -8
  116. package/dist/heart/{daemon → hatch}/specialist-prompt.js +12 -9
  117. package/dist/heart/{daemon → hatch}/specialist-tools.js +35 -12
  118. package/dist/heart/identity.js +166 -55
  119. package/dist/heart/kept-notes.js +357 -0
  120. package/dist/heart/kicks.js +1 -1
  121. package/dist/heart/machine-identity.js +161 -0
  122. package/dist/heart/mail-import-discovery.js +353 -0
  123. package/dist/heart/mailbox/mailbox-http-hooks.js +66 -0
  124. package/dist/heart/mailbox/mailbox-http-response.js +7 -0
  125. package/dist/heart/mailbox/mailbox-http-routes.js +246 -0
  126. package/dist/heart/mailbox/mailbox-http-static.js +103 -0
  127. package/dist/heart/mailbox/mailbox-http-transport.js +116 -0
  128. package/dist/heart/mailbox/mailbox-http.js +99 -0
  129. package/dist/heart/mailbox/mailbox-read.js +31 -0
  130. package/dist/heart/mailbox/mailbox-types.js +27 -0
  131. package/dist/heart/mailbox/mailbox-view.js +197 -0
  132. package/dist/heart/mailbox/readers/agent-machine.js +425 -0
  133. package/dist/heart/mailbox/readers/continuity-readers.js +338 -0
  134. package/dist/heart/mailbox/readers/mail.js +375 -0
  135. package/dist/heart/mailbox/readers/runtime-readers.js +651 -0
  136. package/dist/heart/mailbox/readers/sessions.js +232 -0
  137. package/dist/heart/mailbox/readers/shared.js +111 -0
  138. package/dist/heart/mcp/mcp-server.js +656 -0
  139. package/dist/heart/migrate-config.js +100 -0
  140. package/dist/heart/model-capabilities.js +19 -0
  141. package/dist/heart/platform.js +81 -0
  142. package/dist/heart/provider-attempt.js +134 -0
  143. package/dist/heart/provider-binding-resolver.js +267 -0
  144. package/dist/heart/provider-credentials.js +425 -0
  145. package/dist/heart/provider-failover.js +301 -0
  146. package/dist/heart/provider-models.js +81 -0
  147. package/dist/heart/provider-ping.js +262 -0
  148. package/dist/heart/provider-readiness-cache.js +40 -0
  149. package/dist/heart/provider-visibility.js +188 -0
  150. package/dist/heart/providers/anthropic-token.js +131 -0
  151. package/dist/heart/providers/anthropic.js +139 -52
  152. package/dist/heart/providers/azure.js +23 -11
  153. package/dist/heart/providers/error-classification.js +127 -0
  154. package/dist/heart/providers/github-copilot.js +145 -0
  155. package/dist/heart/providers/minimax-vlm.js +189 -0
  156. package/dist/heart/providers/minimax.js +26 -8
  157. package/dist/heart/providers/openai-codex.js +55 -40
  158. package/dist/heart/runtime-capability-check.js +170 -0
  159. package/dist/heart/runtime-credentials.js +367 -0
  160. package/dist/heart/runtime-cwd.js +87 -0
  161. package/dist/heart/sense-truth.js +13 -4
  162. package/dist/heart/session-activity.js +48 -24
  163. package/dist/heart/session-events.js +1149 -0
  164. package/dist/heart/session-playback-cli-main.js +5 -0
  165. package/dist/heart/session-playback-cli.js +36 -0
  166. package/dist/heart/session-playback.js +231 -0
  167. package/dist/heart/session-stats-cli-main.js +5 -0
  168. package/dist/heart/session-stats.js +182 -0
  169. package/dist/heart/session-transcript.js +243 -0
  170. package/dist/heart/start-of-turn-packet.js +345 -0
  171. package/dist/heart/streaming.js +44 -27
  172. package/dist/heart/sync-classification.js +176 -0
  173. package/dist/heart/sync.js +449 -0
  174. package/dist/heart/target-resolution.js +9 -5
  175. package/dist/heart/tempo.js +93 -0
  176. package/dist/heart/temporal-view.js +41 -0
  177. package/dist/heart/timeouts.js +101 -0
  178. package/dist/heart/tool-activity-callbacks.js +59 -0
  179. package/dist/heart/tool-description.js +143 -0
  180. package/dist/heart/tool-friction.js +55 -0
  181. package/dist/heart/tool-loop.js +200 -0
  182. package/dist/heart/turn-context.js +421 -0
  183. package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +6 -5
  184. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
  185. package/dist/heart/versioning/ouro-path-installer.js +426 -0
  186. package/dist/heart/versioning/ouro-version-manager.js +295 -0
  187. package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
  188. package/dist/heart/{daemon → versioning}/update-checker.js +6 -1
  189. package/dist/heart/{daemon → versioning}/update-hooks.js +63 -59
  190. package/dist/mailbox-ui/assets/index-BPr5vNuM.css +1 -0
  191. package/dist/mailbox-ui/assets/index-CtUWEo-S.js +61 -0
  192. package/dist/mailbox-ui/index.html +15 -0
  193. package/dist/mailroom/attention.js +167 -0
  194. package/dist/mailroom/autonomy.js +209 -0
  195. package/dist/mailroom/blob-store.js +712 -0
  196. package/dist/mailroom/body-cache.js +61 -0
  197. package/dist/mailroom/core.js +788 -0
  198. package/dist/mailroom/entry.js +160 -0
  199. package/dist/mailroom/file-store.js +460 -0
  200. package/dist/mailroom/mbox-import.js +393 -0
  201. package/dist/mailroom/migration.js +164 -0
  202. package/dist/mailroom/outbound.js +380 -0
  203. package/dist/mailroom/policy.js +263 -0
  204. package/dist/mailroom/reader.js +233 -0
  205. package/dist/mailroom/search-cache.js +268 -0
  206. package/dist/mailroom/search-relevance.js +319 -0
  207. package/dist/mailroom/smtp-ingress.js +176 -0
  208. package/dist/mailroom/source-state.js +176 -0
  209. package/dist/mailroom/thread.js +109 -0
  210. package/dist/mailroom/travel-extract.js +89 -0
  211. package/dist/mind/bundle-manifest.js +7 -1
  212. package/dist/mind/context.js +250 -101
  213. package/dist/mind/diary-integrity.js +60 -0
  214. package/dist/mind/{memory.js → diary.js} +62 -75
  215. package/dist/mind/embedding-provider.js +60 -0
  216. package/dist/mind/file-state.js +179 -0
  217. package/dist/mind/friends/channel.js +39 -0
  218. package/dist/mind/friends/resolver.js +54 -2
  219. package/dist/mind/friends/store-file.js +39 -3
  220. package/dist/mind/friends/types.js +2 -2
  221. package/dist/mind/journal-index.js +161 -0
  222. package/dist/mind/note-search.js +268 -0
  223. package/dist/mind/obligation-steering.js +221 -0
  224. package/dist/mind/pending.js +4 -0
  225. package/dist/mind/prompt-refresh.js +3 -2
  226. package/dist/mind/prompt.js +1050 -135
  227. package/dist/mind/provenance-trust.js +26 -0
  228. package/dist/mind/scrutiny.js +173 -0
  229. package/dist/nerves/cli-logging.js +7 -1
  230. package/dist/nerves/coverage/audit-rules.js +15 -6
  231. package/dist/nerves/coverage/audit.js +28 -2
  232. package/dist/nerves/coverage/cli.js +1 -1
  233. package/dist/nerves/coverage/contract.js +5 -5
  234. package/dist/nerves/coverage/file-completeness.js +129 -5
  235. package/dist/nerves/event-buffer.js +111 -0
  236. package/dist/nerves/index.js +224 -4
  237. package/dist/nerves/observation.js +20 -0
  238. package/dist/nerves/redact.js +79 -0
  239. package/dist/nerves/review/cli-main.js +5 -0
  240. package/dist/nerves/review/cli.js +156 -0
  241. package/dist/nerves/review/core.js +152 -0
  242. package/dist/nerves/runtime.js +5 -1
  243. package/dist/repertoire/ado-client.js +15 -56
  244. package/dist/repertoire/ado-semantic.js +11 -10
  245. package/dist/repertoire/api-client.js +97 -0
  246. package/dist/repertoire/bitwarden-store.js +997 -0
  247. package/dist/repertoire/bundle-templates.js +72 -0
  248. package/dist/repertoire/bw-installer.js +180 -0
  249. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  250. package/dist/repertoire/coding/context-pack.js +330 -0
  251. package/dist/repertoire/coding/feedback.js +197 -30
  252. package/dist/repertoire/coding/manager.js +158 -9
  253. package/dist/repertoire/coding/spawner.js +55 -9
  254. package/dist/repertoire/coding/tools.js +170 -7
  255. package/dist/repertoire/commerce-errors.js +109 -0
  256. package/dist/repertoire/commerce-self-test.js +156 -0
  257. package/dist/repertoire/credential-access.js +178 -0
  258. package/dist/repertoire/duffel-client.js +185 -0
  259. package/dist/repertoire/github-client.js +14 -55
  260. package/dist/repertoire/graph-client.js +11 -52
  261. package/dist/repertoire/guardrails.js +396 -0
  262. package/dist/repertoire/mcp-client.js +295 -0
  263. package/dist/repertoire/mcp-manager.js +362 -0
  264. package/dist/repertoire/mcp-tools.js +63 -0
  265. package/dist/repertoire/shell-sessions.js +133 -0
  266. package/dist/repertoire/skills.js +18 -4
  267. package/dist/repertoire/stripe-client.js +131 -0
  268. package/dist/repertoire/tasks/board.js +31 -5
  269. package/dist/repertoire/tasks/fix.js +182 -0
  270. package/dist/repertoire/tasks/index.js +16 -4
  271. package/dist/repertoire/tasks/lifecycle.js +2 -2
  272. package/dist/repertoire/tasks/parser.js +3 -2
  273. package/dist/repertoire/tasks/scanner.js +194 -37
  274. package/dist/repertoire/tasks/transitions.js +16 -78
  275. package/dist/repertoire/tool-results.js +29 -0
  276. package/dist/repertoire/tools-attachments.js +317 -0
  277. package/dist/repertoire/tools-awaiting.js +360 -0
  278. package/dist/repertoire/tools-base.js +53 -1082
  279. package/dist/repertoire/tools-bluebubbles.js +1 -0
  280. package/dist/repertoire/tools-bridge.js +142 -0
  281. package/dist/repertoire/tools-bundle.js +984 -0
  282. package/dist/repertoire/tools-config.js +185 -0
  283. package/dist/repertoire/tools-continuity.js +248 -0
  284. package/dist/repertoire/tools-credential.js +381 -0
  285. package/dist/repertoire/tools-files.js +342 -0
  286. package/dist/repertoire/tools-flight.js +224 -0
  287. package/dist/repertoire/tools-flow.js +119 -0
  288. package/dist/repertoire/tools-github.js +1 -7
  289. package/dist/repertoire/tools-mail.js +1916 -0
  290. package/dist/repertoire/tools-notes.js +421 -0
  291. package/dist/repertoire/tools-obligations.js +142 -0
  292. package/dist/repertoire/tools-runtime.js +61 -0
  293. package/dist/repertoire/tools-session.js +809 -0
  294. package/dist/repertoire/tools-shell.js +120 -0
  295. package/dist/repertoire/tools-stripe.js +180 -0
  296. package/dist/repertoire/tools-surface.js +345 -0
  297. package/dist/repertoire/tools-teams.js +9 -39
  298. package/dist/repertoire/tools-travel.js +125 -0
  299. package/dist/repertoire/tools-trip.js +604 -0
  300. package/dist/repertoire/tools-user-profile.js +144 -0
  301. package/dist/repertoire/tools-vault.js +40 -0
  302. package/dist/repertoire/tools-voice.js +144 -0
  303. package/dist/repertoire/tools.js +115 -103
  304. package/dist/repertoire/travel-api-client.js +360 -0
  305. package/dist/repertoire/user-profile.js +131 -0
  306. package/dist/repertoire/vault-setup.js +246 -0
  307. package/dist/repertoire/vault-unlock.js +594 -0
  308. package/dist/scripts/claude-code-hook.js +41 -0
  309. package/dist/scripts/claude-code-stop-hook.js +47 -0
  310. package/dist/senses/attention-queue.js +116 -0
  311. package/dist/senses/await-turn-message.js +58 -0
  312. package/dist/senses/bluebubbles/active-turns.js +216 -0
  313. package/dist/senses/bluebubbles/attachment-cache.js +53 -0
  314. package/dist/senses/bluebubbles/attachment-download.js +137 -0
  315. package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +219 -18
  316. package/dist/senses/bluebubbles/entry.js +77 -0
  317. package/dist/senses/{bluebubbles-inbound-log.js → bluebubbles/inbound-log.js} +20 -3
  318. package/dist/senses/bluebubbles/index.js +2613 -0
  319. package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -71
  320. package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +33 -12
  321. package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +3 -3
  322. package/dist/senses/bluebubbles/processed-log.js +133 -0
  323. package/dist/senses/bluebubbles/replay.js +137 -0
  324. package/dist/senses/{bluebubbles-runtime-state.js → bluebubbles/runtime-state.js} +30 -2
  325. package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
  326. package/dist/senses/bluebubbles-meta-guard.js +40 -0
  327. package/dist/senses/cli/bracketed-paste.js +82 -0
  328. package/dist/senses/cli/image-paste.js +287 -0
  329. package/dist/senses/cli/image-ref-navigation.js +75 -0
  330. package/dist/senses/cli/ink-app.js +156 -0
  331. package/dist/senses/cli/inline-diff.js +64 -0
  332. package/dist/senses/cli/input-keys.js +174 -0
  333. package/dist/senses/cli/kill-ring.js +86 -0
  334. package/dist/senses/cli/message-list.js +51 -0
  335. package/dist/senses/cli/ouro-tui.js +607 -0
  336. package/dist/senses/cli/spinner-imperative.js +135 -0
  337. package/dist/senses/cli/spinner.js +101 -0
  338. package/dist/senses/cli/status-line.js +60 -0
  339. package/dist/senses/cli/streaming-markdown.js +526 -0
  340. package/dist/senses/cli/tool-display.js +85 -0
  341. package/dist/senses/cli/tool-render.js +85 -0
  342. package/dist/senses/cli/tui-store.js +240 -0
  343. package/dist/senses/cli/virtual-list.js +35 -0
  344. package/dist/senses/cli-entry.js +60 -8
  345. package/dist/senses/cli-layout.js +100 -0
  346. package/dist/senses/cli.js +516 -204
  347. package/dist/senses/commands.js +66 -3
  348. package/dist/senses/habit-turn-message.js +108 -0
  349. package/dist/senses/inner-dialog-worker.js +185 -21
  350. package/dist/senses/inner-dialog.js +469 -39
  351. package/dist/senses/mail-entry.js +66 -0
  352. package/dist/senses/mail.js +379 -0
  353. package/dist/senses/pipeline.js +654 -181
  354. package/dist/senses/proactive-content-guard.js +51 -0
  355. package/dist/senses/shared-turn.js +392 -0
  356. package/dist/senses/surface-tool.js +70 -0
  357. package/dist/senses/teams-entry.js +60 -8
  358. package/dist/senses/teams.js +387 -98
  359. package/dist/senses/trust-gate.js +100 -5
  360. package/dist/senses/voice/audio-playback.js +237 -0
  361. package/dist/senses/voice/audio-routing.js +119 -0
  362. package/dist/senses/voice/elevenlabs.js +202 -0
  363. package/dist/senses/voice/floor-control.js +431 -0
  364. package/dist/senses/voice/floor-controller.js +115 -0
  365. package/dist/senses/voice/golden-path.js +116 -0
  366. package/dist/senses/voice/index.js +29 -0
  367. package/dist/senses/voice/meeting.js +113 -0
  368. package/dist/senses/voice/outbound.js +190 -0
  369. package/dist/senses/voice/phone.js +33 -0
  370. package/dist/senses/voice/playback.js +139 -0
  371. package/dist/senses/voice/realtime-eval.js +496 -0
  372. package/dist/senses/voice/realtime-trace.js +531 -0
  373. package/dist/senses/voice/transcript.js +70 -0
  374. package/dist/senses/voice/turn.js +191 -0
  375. package/dist/senses/voice/twilio-phone-runtime.js +807 -0
  376. package/dist/senses/voice/twilio-phone.js +5079 -0
  377. package/dist/senses/voice/types.js +2 -0
  378. package/dist/senses/voice/whisper.js +161 -0
  379. package/dist/senses/voice-entry.js +81 -0
  380. package/dist/senses/voice-realtime-eval-command.js +99 -0
  381. package/dist/senses/voice-realtime-eval-entry.js +21 -0
  382. package/dist/senses/voice-twilio-entry.js +87 -0
  383. package/dist/trips/core.js +138 -0
  384. package/dist/trips/store.js +265 -0
  385. package/package.json +41 -7
  386. package/skills/agent-commerce.md +106 -0
  387. package/skills/browser-navigation.md +117 -0
  388. package/skills/commerce-setup-guide.md +116 -0
  389. package/skills/commerce-setup.md +84 -0
  390. package/skills/configure-dev-tools.md +99 -0
  391. package/skills/travel-planning.md +138 -0
  392. package/dist/heart/daemon/auth-flow.js +0 -351
  393. package/dist/heart/daemon/ouro-path-installer.js +0 -178
  394. package/dist/heart/safe-workspace.js +0 -228
  395. package/dist/heart/session-recall.js +0 -116
  396. package/dist/mind/associative-recall.js +0 -209
  397. package/dist/senses/bluebubbles-entry.js +0 -13
  398. package/dist/senses/bluebubbles.js +0 -1177
  399. package/dist/senses/debug-activity.js +0 -148
  400. package/subagents/README.md +0 -7
  401. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  402. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  403. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  404. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  405. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  406. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  407. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  408. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  409. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  410. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  411. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  412. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  413. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  414. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  415. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
  416. /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
@@ -33,7 +33,7 @@ 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.RESPAWN_GUARD_WINDOW_MS = exports.RESPAWN_GUARD_MAX_RESTARTS = void 0;
37
37
  const child_process_1 = require("child_process");
38
38
  const path = __importStar(require("path"));
39
39
  const identity_1 = require("../identity");
@@ -41,6 +41,18 @@ const runtime_1 = require("../../nerves/runtime");
41
41
  function startOfHour(ms) {
42
42
  return ms - 60 * 60 * 1000;
43
43
  }
44
+ /**
45
+ * Respawn-loop guard: refuse `restartAgent` if we've already orchestrated
46
+ * RESPAWN_GUARD_MAX_RESTARTS in the past RESPAWN_GUARD_WINDOW_MS.
47
+ *
48
+ * Calibrated for the 2026-05-11 BB sense incident: a misconfigured probe
49
+ * was triggering `restartAgent` every ~60s for hours. Five restarts in
50
+ * 10 minutes is well above the rate of legitimate operational restarts
51
+ * (a single human-initiated `ouro down && ouro up` produces one) and well
52
+ * below the rate of a death spiral (60/hr ⇒ 10/10min).
53
+ */
54
+ exports.RESPAWN_GUARD_MAX_RESTARTS = 5;
55
+ exports.RESPAWN_GUARD_WINDOW_MS = 10 * 60_000;
44
56
  class DaemonProcessManager {
45
57
  agents = new Map();
46
58
  maxRestartsPerHour;
@@ -51,24 +63,110 @@ class DaemonProcessManager {
51
63
  now;
52
64
  setTimeoutFn;
53
65
  clearTimeoutFn;
66
+ cooldownRecoveryMs;
67
+ maxCooldownRetries;
68
+ startupStaleAfterMs;
54
69
  existsSyncFn;
70
+ configCheckFn;
71
+ statusWriterFn;
72
+ onSnapshotChangeFn;
73
+ /**
74
+ * Notify the snapshot-change observer (if registered). Swallows any
75
+ * errors from the observer so process lifecycle code never fails
76
+ * because the observer threw.
77
+ */
78
+ notifySnapshotChange(snapshot) {
79
+ if (!this.onSnapshotChangeFn)
80
+ return;
81
+ try {
82
+ this.onSnapshotChangeFn(snapshot);
83
+ }
84
+ catch (error) {
85
+ (0, runtime_1.emitNervesEvent)({
86
+ level: "warn",
87
+ component: "daemon",
88
+ event: "daemon.snapshot_change_observer_error",
89
+ message: "snapshot-change observer threw",
90
+ meta: {
91
+ agent: snapshot.name,
92
+ error: error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error),
93
+ },
94
+ });
95
+ }
96
+ }
97
+ writeStatus(agent, text) {
98
+ try {
99
+ this.statusWriterFn(text);
100
+ }
101
+ catch (error) {
102
+ (0, runtime_1.emitNervesEvent)({
103
+ level: "warn",
104
+ component: "daemon",
105
+ event: "daemon.status_writer_error",
106
+ message: "daemon status writer threw",
107
+ meta: {
108
+ agent,
109
+ error: error instanceof Error ? error.message : String(error),
110
+ },
111
+ });
112
+ }
113
+ }
114
+ currentTimeMs() {
115
+ const value = this.now();
116
+ return Number.isFinite(value) ? value : Date.now();
117
+ }
118
+ isStartAttemptCurrent(state, attemptId) {
119
+ return state.startAttemptId === attemptId;
120
+ }
121
+ markConfigCheckFailed(state, errorReason, fixHint) {
122
+ const agent = state.config.name;
123
+ state.snapshot.status = "crashed";
124
+ state.snapshot.errorReason = errorReason;
125
+ state.snapshot.fixHint = fixHint;
126
+ (0, runtime_1.emitNervesEvent)({
127
+ level: "error",
128
+ component: "daemon",
129
+ event: "daemon.agent_config_invalid",
130
+ message: errorReason,
131
+ meta: { agent, fix: fixHint },
132
+ });
133
+ this.writeStatus(agent, `[daemon] ${agent}: ${errorReason}\n` +
134
+ (fixHint ? ` Fix: ${fixHint}\n` : ""));
135
+ this.notifySnapshotChange(state.snapshot);
136
+ }
55
137
  constructor(options) {
56
138
  this.maxRestartsPerHour = options.maxRestartsPerHour ?? 10;
57
139
  this.stabilityThresholdMs = options.stabilityThresholdMs ?? 60_000;
58
140
  this.initialBackoffMs = options.initialBackoffMs ?? 1_000;
59
141
  this.maxBackoffMs = options.maxBackoffMs ?? 60_000;
142
+ this.cooldownRecoveryMs = options.cooldownRecoveryMs ?? 5 * 60 * 1_000;
143
+ this.maxCooldownRetries = options.maxCooldownRetries ?? 3;
144
+ this.startupStaleAfterMs = options.startupStaleAfterMs ?? 45_000;
60
145
  this.spawnFn = options.spawn ?? ((command, args, spawnOptions) => (0, child_process_1.spawn)(command, args, spawnOptions));
61
146
  this.now = options.now ?? (() => Date.now());
62
147
  this.setTimeoutFn = options.setTimeoutFn ?? ((cb, delay) => setTimeout(cb, delay));
63
148
  this.clearTimeoutFn = options.clearTimeoutFn ?? ((timer) => clearTimeout(timer));
64
149
  this.existsSyncFn = options.existsSync ?? null;
150
+ this.configCheckFn = options.configCheck ?? null;
151
+ this.statusWriterFn = options.statusWriter ?? ((text) => {
152
+ process.stderr.write(text);
153
+ });
154
+ this.onSnapshotChangeFn = options.onSnapshotChange ?? null;
65
155
  for (const agent of options.agents) {
66
156
  this.agents.set(agent.name, {
67
157
  config: agent,
68
158
  process: null,
159
+ startInFlight: false,
160
+ startAttemptedAtMs: null,
161
+ startAttemptId: 0,
69
162
  restartTimer: null,
70
163
  crashTimestamps: [],
164
+ orchestratedRestartTimestamps: [],
165
+ respawnLoopTripped: false,
71
166
  stopRequested: false,
167
+ cooldownTimer: null,
168
+ cooldownRetryCount: 0,
169
+ fastCrashCount: 0,
72
170
  snapshot: {
73
171
  name: agent.name,
74
172
  channel: agent.channel,
@@ -78,6 +176,10 @@ class DaemonProcessManager {
78
176
  startedAt: null,
79
177
  lastCrashAt: null,
80
178
  backoffMs: this.initialBackoffMs,
179
+ lastExitCode: null,
180
+ lastSignal: null,
181
+ errorReason: null,
182
+ fixHint: null,
81
183
  },
82
184
  });
83
185
  }
@@ -89,53 +191,208 @@ class DaemonProcessManager {
89
191
  }
90
192
  }
91
193
  }
194
+ triggerAutoStartAgents() {
195
+ for (const state of this.agents.values()) {
196
+ if (!state.config.autoStart)
197
+ continue;
198
+ void this.startAgent(state.config.name).catch((error) => {
199
+ const errorReason = error instanceof Error ? error.message : String(error);
200
+ this.markConfigCheckFailed(state, `agent startup threw before the worker could run: ${errorReason}`, "Run 'ouro doctor' for diagnostics, then retry 'ouro up'.");
201
+ });
202
+ }
203
+ }
92
204
  async startAgent(agent) {
93
205
  const state = this.requireAgent(agent);
94
- if (state.process)
206
+ if (state.process || state.startInFlight)
95
207
  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";
208
+ const attemptId = state.startAttemptId + 1;
209
+ state.startAttemptId = attemptId;
210
+ state.startInFlight = true;
211
+ state.startAttemptedAtMs = this.currentTimeMs();
212
+ try {
213
+ this.clearRestartTimer(state);
214
+ state.stopRequested = false;
215
+ state.snapshot.status = "starting";
216
+ if (this.configCheckFn) {
217
+ let result;
218
+ try {
219
+ result = await this.configCheckFn(agent);
220
+ }
221
+ catch (error) {
222
+ const errorReason = error instanceof Error ? error.message : String(error);
223
+ this.markConfigCheckFailed(state, `agent config validation threw: ${errorReason}`, "Run 'ouro doctor' for diagnostics, then retry 'ouro up'.");
224
+ return;
225
+ }
226
+ if (!this.isStartAttemptCurrent(state, attemptId))
227
+ return;
228
+ if (state.stopRequested) {
229
+ state.snapshot.status = "stopped";
230
+ state.snapshot.pid = null;
231
+ this.notifySnapshotChange(state.snapshot);
232
+ return;
233
+ }
234
+ if (result.skip) {
235
+ state.snapshot.status = "stopped";
236
+ state.snapshot.errorReason = null;
237
+ state.snapshot.fixHint = null;
238
+ (0, runtime_1.emitNervesEvent)({
239
+ component: "daemon",
240
+ event: "daemon.agent_config_skipped",
241
+ message: result.error ?? "agent start skipped by config check",
242
+ meta: { agent, fix: result.fix ?? null },
243
+ });
244
+ this.notifySnapshotChange(state.snapshot);
245
+ return;
246
+ }
247
+ if (!result.ok) {
248
+ this.markConfigCheckFailed(state, result.error ?? "agent config validation failed", result.fix ?? null);
249
+ return;
250
+ }
251
+ /* v8 ignore next -- defensive duplicate stale-start guard before spawn preparation @preserve */
252
+ if (!this.isStartAttemptCurrent(state, attemptId))
253
+ return;
254
+ // Config check passed — clear any prior error so the pulse stops
255
+ // reporting the broken state. This is the recovery path: the user
256
+ // fixed their secrets/config, the next startAgent attempt sees a
257
+ // valid config, and the pulse goes quiet.
258
+ state.snapshot.errorReason = null;
259
+ state.snapshot.fixHint = null;
260
+ }
261
+ const runCwd = (0, identity_1.getRepoRoot)();
262
+ const entryScript = path.join((0, identity_1.getRepoRoot)(), "dist", state.config.entry);
263
+ if (this.existsSyncFn && !this.existsSyncFn(entryScript)) {
264
+ state.snapshot.status = "crashed";
265
+ (0, runtime_1.emitNervesEvent)({
266
+ level: "error",
267
+ component: "daemon",
268
+ event: "daemon.agent_entry_missing",
269
+ message: "agent entry script does not exist — cannot spawn. Run 'ouro daemon install' from the correct location.",
270
+ meta: { agent, entryScript },
271
+ });
272
+ this.notifySnapshotChange(state.snapshot);
273
+ return;
274
+ }
275
+ /* v8 ignore next -- defensive duplicate stale-start guard immediately before spawn @preserve */
276
+ if (!this.isStartAttemptCurrent(state, attemptId))
277
+ return;
278
+ if (state.stopRequested) {
279
+ state.snapshot.status = "stopped";
280
+ state.snapshot.pid = null;
281
+ this.notifySnapshotChange(state.snapshot);
282
+ return;
283
+ }
284
+ const args = [entryScript, "--agent", state.config.agentArg ?? agent, ...(state.config.args ?? [])];
285
+ const child = this.spawnFn("node", args, {
286
+ cwd: runCwd,
287
+ env: state.config.env ? { ...process.env, ...state.config.env } : process.env,
288
+ stdio: ["ignore", "ignore", "ignore", "ipc"],
289
+ });
290
+ /* v8 ignore next 7 -- defensive: spawn should always return a ChildProcess @preserve */
291
+ if (!child) {
292
+ state.snapshot.status = "crashed";
293
+ (0, runtime_1.emitNervesEvent)({ level: "error", component: "daemon", event: "daemon.agent_spawn_failed", message: "spawn returned null", meta: { agent } });
294
+ return;
295
+ }
296
+ if (!this.isStartAttemptCurrent(state, attemptId)) {
297
+ try {
298
+ child.kill("SIGTERM");
299
+ }
300
+ catch {
301
+ (0, runtime_1.emitNervesEvent)({
302
+ level: "warn",
303
+ component: "daemon",
304
+ event: "daemon.agent_stop_error",
305
+ message: "failed to stop stale managed agent startup",
306
+ meta: { agent },
307
+ });
308
+ }
309
+ return;
310
+ }
311
+ state.process = child;
312
+ state.snapshot.status = "running";
313
+ state.snapshot.pid = child.pid ?? null;
314
+ state.snapshot.startedAt = new Date(this.currentTimeMs()).toISOString();
315
+ const bootstrap = state.config.getRuntimeCredentialBootstrap?.() ?? null;
316
+ if (bootstrap) {
317
+ const message = {
318
+ type: "ouro.runtimeCredentialBootstrap",
319
+ agentName: bootstrap.agentName,
320
+ };
321
+ if (bootstrap.runtimeConfig)
322
+ message.runtimeConfig = bootstrap.runtimeConfig;
323
+ if (bootstrap.machineRuntimeConfig)
324
+ message.machineRuntimeConfig = bootstrap.machineRuntimeConfig;
325
+ if (bootstrap.machineId)
326
+ message.machineId = bootstrap.machineId;
327
+ if (bootstrap.providerCredentialRecords)
328
+ message.providerCredentialRecords = bootstrap.providerCredentialRecords;
329
+ try {
330
+ child.send?.(message);
331
+ (0, runtime_1.emitNervesEvent)({
332
+ component: "daemon",
333
+ event: "daemon.agent_runtime_credentials_bootstrap_sent",
334
+ message: "sent runtime credential bootstrap to managed agent process",
335
+ meta: {
336
+ agent,
337
+ runtimeConfig: !!bootstrap.runtimeConfig,
338
+ machineRuntimeConfig: !!bootstrap.machineRuntimeConfig,
339
+ providerCredentialRecords: bootstrap.providerCredentialRecords?.length ?? 0,
340
+ },
341
+ });
342
+ }
343
+ catch (error) {
344
+ (0, runtime_1.emitNervesEvent)({
345
+ level: "warn",
346
+ component: "daemon",
347
+ event: "daemon.agent_ipc_send_error",
348
+ message: "failed to send runtime credential bootstrap to managed agent process",
349
+ meta: { agent, error: error instanceof Error ? error.message : String(error) },
350
+ });
351
+ }
352
+ }
103
353
  (0, runtime_1.emitNervesEvent)({
104
- level: "error",
105
354
  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 },
355
+ event: "daemon.agent_started",
356
+ message: "daemon started managed agent process",
357
+ meta: { agent, pid: child.pid ?? null, cwd: runCwd },
358
+ });
359
+ this.notifySnapshotChange(state.snapshot);
360
+ /* v8 ignore start — child process error handler; requires real spawn to trigger */
361
+ child.on("error", (err) => {
362
+ (0, runtime_1.emitNervesEvent)({
363
+ level: "warn",
364
+ component: "daemon",
365
+ event: "daemon.agent_process_error",
366
+ message: "managed agent process emitted error",
367
+ meta: { agent, error: err.message },
368
+ });
369
+ });
370
+ /* v8 ignore stop */
371
+ child.once("exit", (code, signal) => {
372
+ this.onExit(state, code, signal);
109
373
  });
110
- return;
111
374
  }
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
- });
375
+ finally {
376
+ if (this.isStartAttemptCurrent(state, attemptId)) {
377
+ state.startInFlight = false;
378
+ state.startAttemptedAtMs = null;
379
+ }
380
+ }
131
381
  }
132
382
  async stopAgent(agent) {
133
383
  const state = this.requireAgent(agent);
134
384
  this.clearRestartTimer(state);
385
+ this.clearCooldownTimer(state);
135
386
  state.stopRequested = true;
387
+ // NOTE: do not touch state.respawnLoopTripped / orchestratedRestartTimestamps
388
+ // here. restartAgent calls stopAgent internally; clearing the guard here
389
+ // would reset the window every cycle and defeat the loop-detection. The
390
+ // guard self-clears when timestamps age out of the window (handled inside
391
+ // restartAgent at the prune step).
136
392
  if (!state.process) {
137
393
  state.snapshot.status = "stopped";
138
394
  state.snapshot.pid = null;
395
+ this.notifySnapshotChange(state.snapshot);
139
396
  return;
140
397
  }
141
398
  const child = state.process;
@@ -154,8 +411,89 @@ class DaemonProcessManager {
154
411
  meta: { agent },
155
412
  });
156
413
  }
414
+ this.notifySnapshotChange(state.snapshot);
157
415
  }
158
416
  async restartAgent(agent) {
417
+ const state = this.requireAgent(agent);
418
+ // Respawn-loop guard: prune timestamps outside the window, then check
419
+ // whether we've already restarted this agent too many times in it.
420
+ const now = this.now();
421
+ const windowStart = now - exports.RESPAWN_GUARD_WINDOW_MS;
422
+ state.orchestratedRestartTimestamps = state.orchestratedRestartTimestamps.filter((ts) => ts >= windowStart);
423
+ // If the window is now empty, the trip naturally self-clears. That means
424
+ // after RESPAWN_GUARD_WINDOW_MS of no restart attempts, the daemon is
425
+ // willing to try again (e.g. for a fresh health probe failure that has
426
+ // nothing to do with the original loop).
427
+ if (state.respawnLoopTripped && state.orchestratedRestartTimestamps.length === 0) {
428
+ state.respawnLoopTripped = false;
429
+ state.snapshot.errorReason = null;
430
+ state.snapshot.fixHint = null;
431
+ (0, runtime_1.emitNervesEvent)({
432
+ component: "daemon",
433
+ event: "daemon.agent_respawn_loop_cleared",
434
+ message: "respawn-loop guard cleared by window-aging",
435
+ meta: { agent, windowMs: exports.RESPAWN_GUARD_WINDOW_MS },
436
+ });
437
+ this.notifySnapshotChange(state.snapshot);
438
+ }
439
+ if (state.respawnLoopTripped) {
440
+ (0, runtime_1.emitNervesEvent)({
441
+ level: "error",
442
+ component: "daemon",
443
+ event: "daemon.agent_respawn_loop_blocked",
444
+ message: "refused agent restart — respawn-loop guard tripped; manual intervention required",
445
+ meta: {
446
+ agent,
447
+ recentRestartCount: state.orchestratedRestartTimestamps.length,
448
+ windowMs: exports.RESPAWN_GUARD_WINDOW_MS,
449
+ },
450
+ });
451
+ return;
452
+ }
453
+ if (state.orchestratedRestartTimestamps.length >= exports.RESPAWN_GUARD_MAX_RESTARTS) {
454
+ state.respawnLoopTripped = true;
455
+ 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`;
456
+ state.snapshot.fixHint = "investigate the root cause then run `ouro up` to resume";
457
+ (0, runtime_1.emitNervesEvent)({
458
+ level: "error",
459
+ component: "daemon",
460
+ event: "daemon.agent_respawn_loop_tripped",
461
+ message: "respawn-loop guard tripped; further restarts blocked",
462
+ meta: {
463
+ agent,
464
+ restartCount: state.orchestratedRestartTimestamps.length,
465
+ windowMs: exports.RESPAWN_GUARD_WINDOW_MS,
466
+ maxRestarts: exports.RESPAWN_GUARD_MAX_RESTARTS,
467
+ },
468
+ });
469
+ this.notifySnapshotChange(state.snapshot);
470
+ return;
471
+ }
472
+ state.orchestratedRestartTimestamps.push(now);
473
+ if (state.startInFlight && !state.process) {
474
+ const startedAt = state.startAttemptedAtMs;
475
+ /* v8 ignore next -- defensive: startInFlight always records a start timestamp @preserve */
476
+ const elapsedMs = startedAt === null ? 0 : this.currentTimeMs() - startedAt;
477
+ if (elapsedMs < this.startupStaleAfterMs) {
478
+ (0, runtime_1.emitNervesEvent)({
479
+ component: "daemon",
480
+ event: "daemon.agent_restart_deferred",
481
+ message: "managed agent restart skipped while startup is already in flight",
482
+ meta: { agent, elapsedMs, startupStaleAfterMs: this.startupStaleAfterMs },
483
+ });
484
+ return;
485
+ }
486
+ (0, runtime_1.emitNervesEvent)({
487
+ level: "warn",
488
+ component: "daemon",
489
+ event: "daemon.agent_startup_stale_recovered",
490
+ message: "managed agent startup was stale; clearing the pending attempt before restart",
491
+ meta: { agent, elapsedMs, startupStaleAfterMs: this.startupStaleAfterMs },
492
+ });
493
+ state.startAttemptId += 1;
494
+ state.startInFlight = false;
495
+ state.startAttemptedAtMs = null;
496
+ }
159
497
  await this.stopAgent(agent);
160
498
  await this.startAgent(agent);
161
499
  }
@@ -191,9 +529,13 @@ class DaemonProcessManager {
191
529
  if (!state.process)
192
530
  return;
193
531
  state.process = null;
532
+ state.startInFlight = false;
533
+ state.startAttemptedAtMs = null;
194
534
  state.snapshot.pid = null;
535
+ state.snapshot.lastExitCode = code;
536
+ state.snapshot.lastSignal = signal;
195
537
  const crashed = !state.stopRequested && code !== 0;
196
- const now = this.now();
538
+ const now = this.currentTimeMs();
197
539
  const startedAt = state.snapshot.startedAt ? Date.parse(state.snapshot.startedAt) : now;
198
540
  const runDuration = Math.max(0, now - startedAt);
199
541
  (0, runtime_1.emitNervesEvent)({
@@ -208,13 +550,45 @@ class DaemonProcessManager {
208
550
  if (runDuration >= this.stabilityThresholdMs) {
209
551
  state.snapshot.backoffMs = this.initialBackoffMs;
210
552
  }
553
+ this.notifySnapshotChange(state.snapshot);
211
554
  return;
212
555
  }
213
556
  state.snapshot.lastCrashAt = new Date(now).toISOString();
557
+ // Fast-crash detection: if the agent dies within 5 seconds of starting, it's likely
558
+ // a configuration issue (missing credentials, bad provider, etc.) not a transient failure.
559
+ // After 3 consecutive fast crashes, stop retrying and mark as config-failed.
560
+ const FAST_CRASH_THRESHOLD_MS = 5000;
561
+ const FAST_CRASH_MAX = 3;
562
+ if (runDuration < FAST_CRASH_THRESHOLD_MS) {
563
+ state.fastCrashCount = state.fastCrashCount + 1;
564
+ if (state.fastCrashCount >= FAST_CRASH_MAX) {
565
+ state.snapshot.status = "crashed";
566
+ // Capture the fast-crash diagnosis on the snapshot so it surfaces
567
+ // via the pulse. The error message is prescriptive: it tells the
568
+ // user (and their sibling agents) exactly what to do.
569
+ state.snapshot.errorReason = `agent crashed ${FAST_CRASH_MAX} times within ${FAST_CRASH_THRESHOLD_MS}ms of startup — likely a configuration issue (missing credentials, bad provider).`;
570
+ state.snapshot.fixHint = `Fix the config and run \`ouro up\` to restart, or check daemon logs for the underlying error.`;
571
+ (0, runtime_1.emitNervesEvent)({
572
+ level: "error",
573
+ component: "daemon",
574
+ event: "daemon.agent_config_failure",
575
+ message: `agent crashed ${FAST_CRASH_MAX} times within ${FAST_CRASH_THRESHOLD_MS}ms of startup — likely a configuration issue (missing credentials, bad provider). Not retrying. Fix the config and run \`ouro up\` to restart.`,
576
+ meta: { agent: state.config.name, fastCrashCount: state.fastCrashCount, avgRunDurationMs: runDuration },
577
+ });
578
+ this.notifySnapshotChange(state.snapshot);
579
+ return; // Don't schedule cooldown recovery — this needs human/agent intervention
580
+ }
581
+ }
582
+ else {
583
+ // Reset fast-crash counter on a stable run
584
+ state.fastCrashCount = 0;
585
+ }
214
586
  state.crashTimestamps = state.crashTimestamps.filter((crashTs) => crashTs >= startOfHour(now));
215
587
  state.crashTimestamps.push(now);
216
588
  if (state.crashTimestamps.length > this.maxRestartsPerHour) {
217
589
  state.snapshot.status = "crashed";
590
+ state.snapshot.errorReason = `agent exceeded restart limit (${this.maxRestartsPerHour}/hr) — entering cooldown`;
591
+ state.snapshot.fixHint = "investigate why the agent keeps crashing; cooldown will retry shortly";
218
592
  (0, runtime_1.emitNervesEvent)({
219
593
  level: "error",
220
594
  component: "daemon",
@@ -222,6 +596,8 @@ class DaemonProcessManager {
222
596
  message: "managed agent exceeded restart limit and is marked crashed",
223
597
  meta: { agent: state.config.name, maxRestartsPerHour: this.maxRestartsPerHour },
224
598
  });
599
+ this.notifySnapshotChange(state.snapshot);
600
+ this.scheduleCooldownRecovery(state);
225
601
  return;
226
602
  }
227
603
  state.snapshot.status = "starting";
@@ -232,6 +608,7 @@ class DaemonProcessManager {
232
608
  state.restartTimer = this.setTimeoutFn(() => {
233
609
  void this.startAgent(state.config.name);
234
610
  }, waitMs);
611
+ this.notifySnapshotChange(state.snapshot);
235
612
  }
236
613
  clearRestartTimer(state) {
237
614
  if (state.restartTimer === null)
@@ -239,6 +616,45 @@ class DaemonProcessManager {
239
616
  this.clearTimeoutFn(state.restartTimer);
240
617
  state.restartTimer = null;
241
618
  }
619
+ scheduleCooldownRecovery(state) {
620
+ if (state.cooldownRetryCount >= this.maxCooldownRetries) {
621
+ (0, runtime_1.emitNervesEvent)({
622
+ level: "error",
623
+ component: "daemon",
624
+ event: "daemon.agent_permanent_failure",
625
+ message: "managed agent exhausted all cooldown retries — permanently stopped",
626
+ meta: { agent: state.config.name, cooldownRetryCount: state.cooldownRetryCount, maxCooldownRetries: this.maxCooldownRetries },
627
+ });
628
+ return;
629
+ }
630
+ this.clearCooldownTimer(state);
631
+ state.cooldownTimer = this.setTimeoutFn(() => {
632
+ state.cooldownRetryCount += 1;
633
+ state.crashTimestamps = [];
634
+ state.snapshot.backoffMs = this.initialBackoffMs;
635
+ state.snapshot.status = "starting";
636
+ state.snapshot.restartCount += 1;
637
+ (0, runtime_1.emitNervesEvent)({
638
+ component: "daemon",
639
+ event: "daemon.agent_cooldown_recovery",
640
+ message: "attempting cooldown recovery for managed agent",
641
+ meta: { agent: state.config.name, cooldownRetryCount: state.cooldownRetryCount },
642
+ });
643
+ void this.startAgent(state.config.name);
644
+ }, this.cooldownRecoveryMs);
645
+ (0, runtime_1.emitNervesEvent)({
646
+ component: "daemon",
647
+ event: "daemon.agent_cooldown_scheduled",
648
+ message: `scheduled cooldown recovery in ${this.cooldownRecoveryMs}ms`,
649
+ meta: { agent: state.config.name, cooldownRecoveryMs: this.cooldownRecoveryMs, cooldownRetryCount: state.cooldownRetryCount },
650
+ });
651
+ }
652
+ clearCooldownTimer(state) {
653
+ if (state.cooldownTimer === null)
654
+ return;
655
+ this.clearTimeoutFn(state.cooldownTimer);
656
+ state.cooldownTimer = null;
657
+ }
242
658
  requireAgent(agent) {
243
659
  const state = this.agents.get(agent);
244
660
  if (!state) {