@ouro.bot/cli 0.1.0-alpha.60 → 0.1.0-alpha.601

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 (420) 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 +3863 -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 +7571 -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 -1703
  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 +842 -69
  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 +375 -33
  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 +462 -38
  99. package/dist/heart/daemon/session-id-resolver.js +131 -0
  100. package/dist/heart/daemon/skill-management-installer.js +94 -0
  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 +162 -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 +195 -0
  132. package/dist/heart/mailbox/readers/agent-machine.js +382 -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 +43 -22
  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-B-461hes.js +61 -0
  191. package/dist/mailbox-ui/assets/index-BPr5vNuM.css +1 -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 +165 -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 +15 -24
  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 +2548 -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 +372 -27
  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 +5077 -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/daemon/subagent-installer.js +0 -166
  395. package/dist/heart/safe-workspace.js +0 -228
  396. package/dist/heart/session-recall.js +0 -116
  397. package/dist/mind/associative-recall.js +0 -209
  398. package/dist/senses/bluebubbles-entry.js +0 -13
  399. package/dist/senses/bluebubbles.js +0 -1177
  400. package/dist/senses/debug-activity.js +0 -148
  401. package/subagents/README.md +0 -86
  402. package/subagents/work-doer.md +0 -237
  403. package/subagents/work-merger.md +0 -618
  404. package/subagents/work-planner.md +0 -390
  405. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  406. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  407. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  408. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  409. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  410. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  411. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  412. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  413. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  414. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  415. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  416. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  417. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  418. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  419. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
  420. /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
@@ -43,6 +43,7 @@ exports.crontabLine = crontabLine;
43
43
  const os = __importStar(require("os"));
44
44
  const runtime_1 = require("../../nerves/runtime");
45
45
  const PLIST_PREFIX = "bot.ouro.";
46
+ const DAEMON_PLIST_FILENAME = "bot.ouro.daemon.plist";
46
47
  function plistLabel(job) {
47
48
  return `${PLIST_PREFIX}${job.agent}.${job.taskId}`;
48
49
  }
@@ -80,7 +81,7 @@ function scheduleToCalendarInterval(schedule) {
80
81
  result.Month = parseInt(month, 10);
81
82
  return Object.keys(result).length > 0 ? result : null;
82
83
  }
83
- function generatePlistXml(job) {
84
+ function generatePlistXml(job, envPath) {
84
85
  const label = plistLabel(job);
85
86
  const seconds = cadenceToSeconds(job.schedule);
86
87
  const calendar = seconds === null ? scheduleToCalendarInterval(job.schedule) : null;
@@ -97,7 +98,7 @@ function generatePlistXml(job) {
97
98
  else {
98
99
  triggerXml = ` <key>StartInterval</key>\n <integer>1800</integer>`;
99
100
  }
100
- return [
101
+ const lines = [
101
102
  `<?xml version="1.0" encoding="UTF-8"?>`,
102
103
  `<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">`,
103
104
  `<plist version="1.0">`,
@@ -110,14 +111,12 @@ function generatePlistXml(job) {
110
111
  ...job.command.split(" ").slice(1).map((arg) => ` <string>${arg}</string>`),
111
112
  ` </array>`,
112
113
  triggerXml,
113
- ` <key>StandardOutPath</key>`,
114
- ` <string>/tmp/${label}.stdout.log</string>`,
115
- ` <key>StandardErrorPath</key>`,
116
- ` <string>/tmp/${label}.stderr.log</string>`,
117
- `</dict>`,
118
- `</plist>`,
119
- ``,
120
- ].join("\n");
114
+ ];
115
+ if (envPath) {
116
+ lines.push(` <key>EnvironmentVariables</key>`, ` <dict>`, ` <key>PATH</key>`, ` <string>${envPath}</string>`, ` </dict>`);
117
+ }
118
+ lines.push(` <key>StandardOutPath</key>`, ` <string>/tmp/${label}.stdout.log</string>`, ` <key>StandardErrorPath</key>`, ` <string>/tmp/${label}.stderr.log</string>`, `</dict>`, `</plist>`, ``);
119
+ return lines.join("\n");
121
120
  }
122
121
  class LaunchdCronManager {
123
122
  deps;
@@ -148,7 +147,7 @@ class LaunchdCronManager {
148
147
  const label = plistLabel(job);
149
148
  const filename = `${label}.plist`;
150
149
  const fullPath = `${this.launchAgentsDir}/${filename}`;
151
- const xml = generatePlistXml(job);
150
+ const xml = generatePlistXml(job, this.deps.envPath);
152
151
  try {
153
152
  this.deps.exec(`launchctl unload "${fullPath}"`);
154
153
  }
@@ -178,7 +177,9 @@ class LaunchdCronManager {
178
177
  listPlistFiles() {
179
178
  if (!this.deps.existsFile(this.launchAgentsDir))
180
179
  return [];
181
- return this.deps.listDir(this.launchAgentsDir).filter((f) => f.startsWith(PLIST_PREFIX) && f.endsWith(".plist"));
180
+ return this.deps.listDir(this.launchAgentsDir).filter((f) => f.startsWith(PLIST_PREFIX) &&
181
+ f.endsWith(".plist") &&
182
+ f !== DAEMON_PLIST_FILENAME);
182
183
  }
183
184
  }
184
185
  exports.LaunchdCronManager = LaunchdCronManager;
@@ -248,6 +249,7 @@ function createOsCronManager(options = {}) {
248
249
  listDir: () => [],
249
250
  mkdirp: () => { },
250
251
  homeDir: os.homedir(),
252
+ envPath: process.env.PATH ?? "",
251
253
  };
252
254
  /* v8 ignore stop */
253
255
  return new LaunchdCronManager(deps);
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const runtime_1 = require("../../nerves/runtime");
5
5
  const runtime_logging_1 = require("./runtime-logging");
6
- const ouro_bot_wrapper_1 = require("./ouro-bot-wrapper");
6
+ const ouro_bot_wrapper_1 = require("../versioning/ouro-bot-wrapper");
7
7
  (0, runtime_logging_1.configureDaemonRuntimeLogger)("ouro-bot");
8
8
  (0, runtime_1.emitNervesEvent)({
9
9
  component: "daemon",
@@ -12,12 +12,14 @@ const ouro_bot_wrapper_1 = require("./ouro-bot-wrapper");
12
12
  meta: { args: process.argv.slice(2) },
13
13
  });
14
14
  void (0, ouro_bot_wrapper_1.runOuroBotWrapper)(process.argv.slice(2)).catch((error) => {
15
+ const message = error instanceof Error ? error.message : String(error);
16
+ process.stderr.write(`${message}\n`);
15
17
  (0, runtime_1.emitNervesEvent)({
16
18
  level: "error",
17
19
  component: "daemon",
18
20
  event: "daemon.ouro_bot_entry_error",
19
21
  message: "ouro.bot wrapper entrypoint failed",
20
- meta: { error: error instanceof Error ? error.message : String(error) },
22
+ meta: { error: message },
21
23
  });
22
24
  process.exit(1);
23
25
  });
@@ -12,12 +12,14 @@ const runtime_logging_1 = require("./runtime-logging");
12
12
  meta: { args: process.argv.slice(2) },
13
13
  });
14
14
  void (0, daemon_cli_1.runOuroCli)(process.argv.slice(2)).catch((error) => {
15
+ const message = error instanceof Error ? error.message : String(error);
16
+ process.stderr.write(`${message}\n`);
15
17
  (0, runtime_1.emitNervesEvent)({
16
18
  level: "error",
17
19
  component: "daemon",
18
20
  event: "daemon.cli_entry_error",
19
21
  message: "ouro CLI entrypoint failed",
20
- meta: { error: error instanceof Error ? error.message : String(error) },
22
+ meta: { error: message },
21
23
  });
22
24
  process.exit(1);
23
25
  });
@@ -51,24 +51,108 @@ class DaemonProcessManager {
51
51
  now;
52
52
  setTimeoutFn;
53
53
  clearTimeoutFn;
54
+ cooldownRecoveryMs;
55
+ maxCooldownRetries;
56
+ startupStaleAfterMs;
54
57
  existsSyncFn;
58
+ configCheckFn;
59
+ statusWriterFn;
60
+ onSnapshotChangeFn;
61
+ /**
62
+ * Notify the snapshot-change observer (if registered). Swallows any
63
+ * errors from the observer so process lifecycle code never fails
64
+ * because the observer threw.
65
+ */
66
+ notifySnapshotChange(snapshot) {
67
+ if (!this.onSnapshotChangeFn)
68
+ return;
69
+ try {
70
+ this.onSnapshotChangeFn(snapshot);
71
+ }
72
+ catch (error) {
73
+ (0, runtime_1.emitNervesEvent)({
74
+ level: "warn",
75
+ component: "daemon",
76
+ event: "daemon.snapshot_change_observer_error",
77
+ message: "snapshot-change observer threw",
78
+ meta: {
79
+ agent: snapshot.name,
80
+ error: error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error),
81
+ },
82
+ });
83
+ }
84
+ }
85
+ writeStatus(agent, text) {
86
+ try {
87
+ this.statusWriterFn(text);
88
+ }
89
+ catch (error) {
90
+ (0, runtime_1.emitNervesEvent)({
91
+ level: "warn",
92
+ component: "daemon",
93
+ event: "daemon.status_writer_error",
94
+ message: "daemon status writer threw",
95
+ meta: {
96
+ agent,
97
+ error: error instanceof Error ? error.message : String(error),
98
+ },
99
+ });
100
+ }
101
+ }
102
+ currentTimeMs() {
103
+ const value = this.now();
104
+ return Number.isFinite(value) ? value : Date.now();
105
+ }
106
+ isStartAttemptCurrent(state, attemptId) {
107
+ return state.startAttemptId === attemptId;
108
+ }
109
+ markConfigCheckFailed(state, errorReason, fixHint) {
110
+ const agent = state.config.name;
111
+ state.snapshot.status = "crashed";
112
+ state.snapshot.errorReason = errorReason;
113
+ state.snapshot.fixHint = fixHint;
114
+ (0, runtime_1.emitNervesEvent)({
115
+ level: "error",
116
+ component: "daemon",
117
+ event: "daemon.agent_config_invalid",
118
+ message: errorReason,
119
+ meta: { agent, fix: fixHint },
120
+ });
121
+ this.writeStatus(agent, `[daemon] ${agent}: ${errorReason}\n` +
122
+ (fixHint ? ` Fix: ${fixHint}\n` : ""));
123
+ this.notifySnapshotChange(state.snapshot);
124
+ }
55
125
  constructor(options) {
56
126
  this.maxRestartsPerHour = options.maxRestartsPerHour ?? 10;
57
127
  this.stabilityThresholdMs = options.stabilityThresholdMs ?? 60_000;
58
128
  this.initialBackoffMs = options.initialBackoffMs ?? 1_000;
59
129
  this.maxBackoffMs = options.maxBackoffMs ?? 60_000;
130
+ this.cooldownRecoveryMs = options.cooldownRecoveryMs ?? 5 * 60 * 1_000;
131
+ this.maxCooldownRetries = options.maxCooldownRetries ?? 3;
132
+ this.startupStaleAfterMs = options.startupStaleAfterMs ?? 45_000;
60
133
  this.spawnFn = options.spawn ?? ((command, args, spawnOptions) => (0, child_process_1.spawn)(command, args, spawnOptions));
61
134
  this.now = options.now ?? (() => Date.now());
62
135
  this.setTimeoutFn = options.setTimeoutFn ?? ((cb, delay) => setTimeout(cb, delay));
63
136
  this.clearTimeoutFn = options.clearTimeoutFn ?? ((timer) => clearTimeout(timer));
64
137
  this.existsSyncFn = options.existsSync ?? null;
138
+ this.configCheckFn = options.configCheck ?? null;
139
+ this.statusWriterFn = options.statusWriter ?? ((text) => {
140
+ process.stderr.write(text);
141
+ });
142
+ this.onSnapshotChangeFn = options.onSnapshotChange ?? null;
65
143
  for (const agent of options.agents) {
66
144
  this.agents.set(agent.name, {
67
145
  config: agent,
68
146
  process: null,
147
+ startInFlight: false,
148
+ startAttemptedAtMs: null,
149
+ startAttemptId: 0,
69
150
  restartTimer: null,
70
151
  crashTimestamps: [],
71
152
  stopRequested: false,
153
+ cooldownTimer: null,
154
+ cooldownRetryCount: 0,
155
+ fastCrashCount: 0,
72
156
  snapshot: {
73
157
  name: agent.name,
74
158
  channel: agent.channel,
@@ -78,6 +162,10 @@ class DaemonProcessManager {
78
162
  startedAt: null,
79
163
  lastCrashAt: null,
80
164
  backoffMs: this.initialBackoffMs,
165
+ lastExitCode: null,
166
+ lastSignal: null,
167
+ errorReason: null,
168
+ fixHint: null,
81
169
  },
82
170
  });
83
171
  }
@@ -89,53 +177,203 @@ class DaemonProcessManager {
89
177
  }
90
178
  }
91
179
  }
180
+ triggerAutoStartAgents() {
181
+ for (const state of this.agents.values()) {
182
+ if (!state.config.autoStart)
183
+ continue;
184
+ void this.startAgent(state.config.name).catch((error) => {
185
+ const errorReason = error instanceof Error ? error.message : String(error);
186
+ this.markConfigCheckFailed(state, `agent startup threw before the worker could run: ${errorReason}`, "Run 'ouro doctor' for diagnostics, then retry 'ouro up'.");
187
+ });
188
+ }
189
+ }
92
190
  async startAgent(agent) {
93
191
  const state = this.requireAgent(agent);
94
- if (state.process)
192
+ if (state.process || state.startInFlight)
95
193
  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";
194
+ const attemptId = state.startAttemptId + 1;
195
+ state.startAttemptId = attemptId;
196
+ state.startInFlight = true;
197
+ state.startAttemptedAtMs = this.currentTimeMs();
198
+ try {
199
+ this.clearRestartTimer(state);
200
+ state.stopRequested = false;
201
+ state.snapshot.status = "starting";
202
+ if (this.configCheckFn) {
203
+ let result;
204
+ try {
205
+ result = await this.configCheckFn(agent);
206
+ }
207
+ catch (error) {
208
+ const errorReason = error instanceof Error ? error.message : String(error);
209
+ this.markConfigCheckFailed(state, `agent config validation threw: ${errorReason}`, "Run 'ouro doctor' for diagnostics, then retry 'ouro up'.");
210
+ return;
211
+ }
212
+ if (!this.isStartAttemptCurrent(state, attemptId))
213
+ return;
214
+ if (state.stopRequested) {
215
+ state.snapshot.status = "stopped";
216
+ state.snapshot.pid = null;
217
+ this.notifySnapshotChange(state.snapshot);
218
+ return;
219
+ }
220
+ if (result.skip) {
221
+ state.snapshot.status = "stopped";
222
+ state.snapshot.errorReason = null;
223
+ state.snapshot.fixHint = null;
224
+ (0, runtime_1.emitNervesEvent)({
225
+ component: "daemon",
226
+ event: "daemon.agent_config_skipped",
227
+ message: result.error ?? "agent start skipped by config check",
228
+ meta: { agent, fix: result.fix ?? null },
229
+ });
230
+ this.notifySnapshotChange(state.snapshot);
231
+ return;
232
+ }
233
+ if (!result.ok) {
234
+ this.markConfigCheckFailed(state, result.error ?? "agent config validation failed", result.fix ?? null);
235
+ return;
236
+ }
237
+ /* v8 ignore next -- defensive duplicate stale-start guard before spawn preparation @preserve */
238
+ if (!this.isStartAttemptCurrent(state, attemptId))
239
+ return;
240
+ // Config check passed — clear any prior error so the pulse stops
241
+ // reporting the broken state. This is the recovery path: the user
242
+ // fixed their secrets/config, the next startAgent attempt sees a
243
+ // valid config, and the pulse goes quiet.
244
+ state.snapshot.errorReason = null;
245
+ state.snapshot.fixHint = null;
246
+ }
247
+ const runCwd = (0, identity_1.getRepoRoot)();
248
+ const entryScript = path.join((0, identity_1.getRepoRoot)(), "dist", state.config.entry);
249
+ if (this.existsSyncFn && !this.existsSyncFn(entryScript)) {
250
+ state.snapshot.status = "crashed";
251
+ (0, runtime_1.emitNervesEvent)({
252
+ level: "error",
253
+ component: "daemon",
254
+ event: "daemon.agent_entry_missing",
255
+ message: "agent entry script does not exist — cannot spawn. Run 'ouro daemon install' from the correct location.",
256
+ meta: { agent, entryScript },
257
+ });
258
+ this.notifySnapshotChange(state.snapshot);
259
+ return;
260
+ }
261
+ /* v8 ignore next -- defensive duplicate stale-start guard immediately before spawn @preserve */
262
+ if (!this.isStartAttemptCurrent(state, attemptId))
263
+ return;
264
+ if (state.stopRequested) {
265
+ state.snapshot.status = "stopped";
266
+ state.snapshot.pid = null;
267
+ this.notifySnapshotChange(state.snapshot);
268
+ return;
269
+ }
270
+ const args = [entryScript, "--agent", state.config.agentArg ?? agent, ...(state.config.args ?? [])];
271
+ const child = this.spawnFn("node", args, {
272
+ cwd: runCwd,
273
+ env: state.config.env ? { ...process.env, ...state.config.env } : process.env,
274
+ stdio: ["ignore", "ignore", "ignore", "ipc"],
275
+ });
276
+ /* v8 ignore next 7 -- defensive: spawn should always return a ChildProcess @preserve */
277
+ if (!child) {
278
+ state.snapshot.status = "crashed";
279
+ (0, runtime_1.emitNervesEvent)({ level: "error", component: "daemon", event: "daemon.agent_spawn_failed", message: "spawn returned null", meta: { agent } });
280
+ return;
281
+ }
282
+ if (!this.isStartAttemptCurrent(state, attemptId)) {
283
+ try {
284
+ child.kill("SIGTERM");
285
+ }
286
+ catch {
287
+ (0, runtime_1.emitNervesEvent)({
288
+ level: "warn",
289
+ component: "daemon",
290
+ event: "daemon.agent_stop_error",
291
+ message: "failed to stop stale managed agent startup",
292
+ meta: { agent },
293
+ });
294
+ }
295
+ return;
296
+ }
297
+ state.process = child;
298
+ state.snapshot.status = "running";
299
+ state.snapshot.pid = child.pid ?? null;
300
+ state.snapshot.startedAt = new Date(this.currentTimeMs()).toISOString();
301
+ const bootstrap = state.config.getRuntimeCredentialBootstrap?.() ?? null;
302
+ if (bootstrap) {
303
+ const message = {
304
+ type: "ouro.runtimeCredentialBootstrap",
305
+ agentName: bootstrap.agentName,
306
+ };
307
+ if (bootstrap.runtimeConfig)
308
+ message.runtimeConfig = bootstrap.runtimeConfig;
309
+ if (bootstrap.machineRuntimeConfig)
310
+ message.machineRuntimeConfig = bootstrap.machineRuntimeConfig;
311
+ if (bootstrap.machineId)
312
+ message.machineId = bootstrap.machineId;
313
+ if (bootstrap.providerCredentialRecords)
314
+ message.providerCredentialRecords = bootstrap.providerCredentialRecords;
315
+ try {
316
+ child.send?.(message);
317
+ (0, runtime_1.emitNervesEvent)({
318
+ component: "daemon",
319
+ event: "daemon.agent_runtime_credentials_bootstrap_sent",
320
+ message: "sent runtime credential bootstrap to managed agent process",
321
+ meta: {
322
+ agent,
323
+ runtimeConfig: !!bootstrap.runtimeConfig,
324
+ machineRuntimeConfig: !!bootstrap.machineRuntimeConfig,
325
+ providerCredentialRecords: bootstrap.providerCredentialRecords?.length ?? 0,
326
+ },
327
+ });
328
+ }
329
+ catch (error) {
330
+ (0, runtime_1.emitNervesEvent)({
331
+ level: "warn",
332
+ component: "daemon",
333
+ event: "daemon.agent_ipc_send_error",
334
+ message: "failed to send runtime credential bootstrap to managed agent process",
335
+ meta: { agent, error: error instanceof Error ? error.message : String(error) },
336
+ });
337
+ }
338
+ }
103
339
  (0, runtime_1.emitNervesEvent)({
104
- level: "error",
105
340
  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 },
341
+ event: "daemon.agent_started",
342
+ message: "daemon started managed agent process",
343
+ meta: { agent, pid: child.pid ?? null, cwd: runCwd },
344
+ });
345
+ this.notifySnapshotChange(state.snapshot);
346
+ /* v8 ignore start — child process error handler; requires real spawn to trigger */
347
+ child.on("error", (err) => {
348
+ (0, runtime_1.emitNervesEvent)({
349
+ level: "warn",
350
+ component: "daemon",
351
+ event: "daemon.agent_process_error",
352
+ message: "managed agent process emitted error",
353
+ meta: { agent, error: err.message },
354
+ });
355
+ });
356
+ /* v8 ignore stop */
357
+ child.once("exit", (code, signal) => {
358
+ this.onExit(state, code, signal);
109
359
  });
110
- return;
111
360
  }
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
- });
361
+ finally {
362
+ if (this.isStartAttemptCurrent(state, attemptId)) {
363
+ state.startInFlight = false;
364
+ state.startAttemptedAtMs = null;
365
+ }
366
+ }
131
367
  }
132
368
  async stopAgent(agent) {
133
369
  const state = this.requireAgent(agent);
134
370
  this.clearRestartTimer(state);
371
+ this.clearCooldownTimer(state);
135
372
  state.stopRequested = true;
136
373
  if (!state.process) {
137
374
  state.snapshot.status = "stopped";
138
375
  state.snapshot.pid = null;
376
+ this.notifySnapshotChange(state.snapshot);
139
377
  return;
140
378
  }
141
379
  const child = state.process;
@@ -154,8 +392,34 @@ class DaemonProcessManager {
154
392
  meta: { agent },
155
393
  });
156
394
  }
395
+ this.notifySnapshotChange(state.snapshot);
157
396
  }
158
397
  async restartAgent(agent) {
398
+ const state = this.requireAgent(agent);
399
+ if (state.startInFlight && !state.process) {
400
+ const startedAt = state.startAttemptedAtMs;
401
+ /* v8 ignore next -- defensive: startInFlight always records a start timestamp @preserve */
402
+ const elapsedMs = startedAt === null ? 0 : this.currentTimeMs() - startedAt;
403
+ if (elapsedMs < this.startupStaleAfterMs) {
404
+ (0, runtime_1.emitNervesEvent)({
405
+ component: "daemon",
406
+ event: "daemon.agent_restart_deferred",
407
+ message: "managed agent restart skipped while startup is already in flight",
408
+ meta: { agent, elapsedMs, startupStaleAfterMs: this.startupStaleAfterMs },
409
+ });
410
+ return;
411
+ }
412
+ (0, runtime_1.emitNervesEvent)({
413
+ level: "warn",
414
+ component: "daemon",
415
+ event: "daemon.agent_startup_stale_recovered",
416
+ message: "managed agent startup was stale; clearing the pending attempt before restart",
417
+ meta: { agent, elapsedMs, startupStaleAfterMs: this.startupStaleAfterMs },
418
+ });
419
+ state.startAttemptId += 1;
420
+ state.startInFlight = false;
421
+ state.startAttemptedAtMs = null;
422
+ }
159
423
  await this.stopAgent(agent);
160
424
  await this.startAgent(agent);
161
425
  }
@@ -191,9 +455,13 @@ class DaemonProcessManager {
191
455
  if (!state.process)
192
456
  return;
193
457
  state.process = null;
458
+ state.startInFlight = false;
459
+ state.startAttemptedAtMs = null;
194
460
  state.snapshot.pid = null;
461
+ state.snapshot.lastExitCode = code;
462
+ state.snapshot.lastSignal = signal;
195
463
  const crashed = !state.stopRequested && code !== 0;
196
- const now = this.now();
464
+ const now = this.currentTimeMs();
197
465
  const startedAt = state.snapshot.startedAt ? Date.parse(state.snapshot.startedAt) : now;
198
466
  const runDuration = Math.max(0, now - startedAt);
199
467
  (0, runtime_1.emitNervesEvent)({
@@ -208,13 +476,45 @@ class DaemonProcessManager {
208
476
  if (runDuration >= this.stabilityThresholdMs) {
209
477
  state.snapshot.backoffMs = this.initialBackoffMs;
210
478
  }
479
+ this.notifySnapshotChange(state.snapshot);
211
480
  return;
212
481
  }
213
482
  state.snapshot.lastCrashAt = new Date(now).toISOString();
483
+ // Fast-crash detection: if the agent dies within 5 seconds of starting, it's likely
484
+ // a configuration issue (missing credentials, bad provider, etc.) not a transient failure.
485
+ // After 3 consecutive fast crashes, stop retrying and mark as config-failed.
486
+ const FAST_CRASH_THRESHOLD_MS = 5000;
487
+ const FAST_CRASH_MAX = 3;
488
+ if (runDuration < FAST_CRASH_THRESHOLD_MS) {
489
+ state.fastCrashCount = state.fastCrashCount + 1;
490
+ if (state.fastCrashCount >= FAST_CRASH_MAX) {
491
+ state.snapshot.status = "crashed";
492
+ // Capture the fast-crash diagnosis on the snapshot so it surfaces
493
+ // via the pulse. The error message is prescriptive: it tells the
494
+ // user (and their sibling agents) exactly what to do.
495
+ 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).`;
496
+ state.snapshot.fixHint = `Fix the config and run \`ouro up\` to restart, or check daemon logs for the underlying error.`;
497
+ (0, runtime_1.emitNervesEvent)({
498
+ level: "error",
499
+ component: "daemon",
500
+ event: "daemon.agent_config_failure",
501
+ 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.`,
502
+ meta: { agent: state.config.name, fastCrashCount: state.fastCrashCount, avgRunDurationMs: runDuration },
503
+ });
504
+ this.notifySnapshotChange(state.snapshot);
505
+ return; // Don't schedule cooldown recovery — this needs human/agent intervention
506
+ }
507
+ }
508
+ else {
509
+ // Reset fast-crash counter on a stable run
510
+ state.fastCrashCount = 0;
511
+ }
214
512
  state.crashTimestamps = state.crashTimestamps.filter((crashTs) => crashTs >= startOfHour(now));
215
513
  state.crashTimestamps.push(now);
216
514
  if (state.crashTimestamps.length > this.maxRestartsPerHour) {
217
515
  state.snapshot.status = "crashed";
516
+ state.snapshot.errorReason = `agent exceeded restart limit (${this.maxRestartsPerHour}/hr) — entering cooldown`;
517
+ state.snapshot.fixHint = "investigate why the agent keeps crashing; cooldown will retry shortly";
218
518
  (0, runtime_1.emitNervesEvent)({
219
519
  level: "error",
220
520
  component: "daemon",
@@ -222,6 +522,8 @@ class DaemonProcessManager {
222
522
  message: "managed agent exceeded restart limit and is marked crashed",
223
523
  meta: { agent: state.config.name, maxRestartsPerHour: this.maxRestartsPerHour },
224
524
  });
525
+ this.notifySnapshotChange(state.snapshot);
526
+ this.scheduleCooldownRecovery(state);
225
527
  return;
226
528
  }
227
529
  state.snapshot.status = "starting";
@@ -232,6 +534,7 @@ class DaemonProcessManager {
232
534
  state.restartTimer = this.setTimeoutFn(() => {
233
535
  void this.startAgent(state.config.name);
234
536
  }, waitMs);
537
+ this.notifySnapshotChange(state.snapshot);
235
538
  }
236
539
  clearRestartTimer(state) {
237
540
  if (state.restartTimer === null)
@@ -239,6 +542,45 @@ class DaemonProcessManager {
239
542
  this.clearTimeoutFn(state.restartTimer);
240
543
  state.restartTimer = null;
241
544
  }
545
+ scheduleCooldownRecovery(state) {
546
+ if (state.cooldownRetryCount >= this.maxCooldownRetries) {
547
+ (0, runtime_1.emitNervesEvent)({
548
+ level: "error",
549
+ component: "daemon",
550
+ event: "daemon.agent_permanent_failure",
551
+ message: "managed agent exhausted all cooldown retries — permanently stopped",
552
+ meta: { agent: state.config.name, cooldownRetryCount: state.cooldownRetryCount, maxCooldownRetries: this.maxCooldownRetries },
553
+ });
554
+ return;
555
+ }
556
+ this.clearCooldownTimer(state);
557
+ state.cooldownTimer = this.setTimeoutFn(() => {
558
+ state.cooldownRetryCount += 1;
559
+ state.crashTimestamps = [];
560
+ state.snapshot.backoffMs = this.initialBackoffMs;
561
+ state.snapshot.status = "starting";
562
+ state.snapshot.restartCount += 1;
563
+ (0, runtime_1.emitNervesEvent)({
564
+ component: "daemon",
565
+ event: "daemon.agent_cooldown_recovery",
566
+ message: "attempting cooldown recovery for managed agent",
567
+ meta: { agent: state.config.name, cooldownRetryCount: state.cooldownRetryCount },
568
+ });
569
+ void this.startAgent(state.config.name);
570
+ }, this.cooldownRecoveryMs);
571
+ (0, runtime_1.emitNervesEvent)({
572
+ component: "daemon",
573
+ event: "daemon.agent_cooldown_scheduled",
574
+ message: `scheduled cooldown recovery in ${this.cooldownRecoveryMs}ms`,
575
+ meta: { agent: state.config.name, cooldownRecoveryMs: this.cooldownRecoveryMs, cooldownRetryCount: state.cooldownRetryCount },
576
+ });
577
+ }
578
+ clearCooldownTimer(state) {
579
+ if (state.cooldownTimer === null)
580
+ return;
581
+ this.clearTimeoutFn(state.cooldownTimer);
582
+ state.cooldownTimer = null;
583
+ }
242
584
  requireAgent(agent) {
243
585
  const state = this.agents.get(agent);
244
586
  if (!state) {