@ouro.bot/cli 0.1.0-alpha.7 → 0.1.0-alpha.700

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