@ouro.bot/cli 0.1.0-alpha.50 → 0.1.0-alpha.501

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 (369) hide show
  1. package/README.md +133 -19
  2. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +3 -2
  3. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +2 -2
  4. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +1 -1
  5. package/changelog.json +3184 -0
  6. package/dist/arc/attention-types.js +8 -0
  7. package/dist/arc/cares.js +140 -0
  8. package/dist/arc/episodes.js +117 -0
  9. package/dist/arc/intentions.js +133 -0
  10. package/dist/arc/json-store.js +117 -0
  11. package/dist/arc/obligations.js +237 -0
  12. package/dist/arc/packets.js +193 -0
  13. package/dist/arc/presence.js +185 -0
  14. package/dist/arc/task-lifecycle.js +65 -0
  15. package/dist/heart/active-work.js +867 -35
  16. package/dist/heart/agent-entry.js +58 -3
  17. package/dist/heart/attachments/image-normalize.js +194 -0
  18. package/dist/heart/attachments/materialize.js +97 -0
  19. package/dist/heart/attachments/originals.js +88 -0
  20. package/dist/heart/attachments/render.js +29 -0
  21. package/dist/heart/attachments/sources/adapter.js +2 -0
  22. package/dist/heart/attachments/sources/bluebubbles.js +156 -0
  23. package/dist/heart/attachments/sources/cli-local-file.js +78 -0
  24. package/dist/heart/attachments/sources/index.js +16 -0
  25. package/dist/heart/attachments/store.js +103 -0
  26. package/dist/heart/attachments/types.js +93 -0
  27. package/dist/heart/auth/auth-flow.js +426 -0
  28. package/dist/heart/background-operations.js +281 -0
  29. package/dist/heart/bundle-state.js +168 -0
  30. package/dist/heart/commitments.js +111 -0
  31. package/dist/heart/config-registry.js +304 -0
  32. package/dist/heart/config.js +119 -129
  33. package/dist/heart/core.js +898 -244
  34. package/dist/heart/cross-chat-delivery.js +131 -0
  35. package/dist/heart/daemon/agent-config-check.js +490 -0
  36. package/dist/heart/daemon/agent-discovery.js +79 -3
  37. package/dist/heart/daemon/agent-service.js +360 -0
  38. package/dist/heart/daemon/agentic-repair.js +216 -0
  39. package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
  40. package/dist/heart/daemon/cadence.js +70 -0
  41. package/dist/heart/daemon/cli-defaults.js +640 -0
  42. package/dist/heart/daemon/cli-exec.js +7239 -0
  43. package/dist/heart/daemon/cli-help.js +493 -0
  44. package/dist/heart/daemon/cli-parse.js +1533 -0
  45. package/dist/heart/daemon/cli-render-doctor.js +57 -0
  46. package/dist/heart/daemon/cli-render.js +561 -0
  47. package/dist/heart/daemon/cli-types.js +8 -0
  48. package/dist/heart/daemon/connect-bay.js +323 -0
  49. package/dist/heart/daemon/daemon-cli.js +29 -1631
  50. package/dist/heart/daemon/daemon-entry.js +345 -3
  51. package/dist/heart/daemon/daemon-health.js +141 -0
  52. package/dist/heart/daemon/daemon-runtime-sync.js +190 -12
  53. package/dist/heart/daemon/daemon-tombstone.js +236 -0
  54. package/dist/heart/daemon/daemon.js +677 -58
  55. package/dist/heart/daemon/dns-workflow.js +394 -0
  56. package/dist/heart/daemon/doctor-types.js +8 -0
  57. package/dist/heart/daemon/doctor.js +615 -0
  58. package/dist/heart/daemon/health-monitor.js +92 -1
  59. package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
  60. package/dist/heart/daemon/hooks/bundle-meta.js +115 -1
  61. package/dist/heart/daemon/http-health-probe.js +80 -0
  62. package/dist/heart/daemon/human-command-screens.js +234 -0
  63. package/dist/heart/daemon/human-readiness.js +114 -0
  64. package/dist/heart/daemon/inner-status.js +89 -0
  65. package/dist/heart/daemon/interactive-repair.js +394 -0
  66. package/dist/heart/daemon/launchd.js +25 -5
  67. package/dist/heart/daemon/log-tailer.js +82 -12
  68. package/dist/heart/daemon/logs-prune.js +110 -0
  69. package/dist/heart/daemon/message-router.js +2 -2
  70. package/dist/heart/daemon/os-cron-deps.js +134 -0
  71. package/dist/heart/daemon/ouro-bot-entry.js +4 -2
  72. package/dist/heart/daemon/ouro-entry.js +3 -1
  73. package/dist/heart/daemon/process-manager.js +214 -0
  74. package/dist/heart/daemon/provider-discovery.js +137 -0
  75. package/dist/heart/daemon/provider-ping-progress.js +83 -0
  76. package/dist/heart/daemon/pulse.js +475 -0
  77. package/dist/heart/daemon/readiness-repair.js +365 -0
  78. package/dist/heart/daemon/run-hooks.js +2 -0
  79. package/dist/heart/daemon/runtime-logging.js +67 -16
  80. package/dist/heart/daemon/runtime-metadata.js +73 -0
  81. package/dist/heart/daemon/runtime-mode.js +67 -0
  82. package/dist/heart/daemon/safe-mode.js +161 -0
  83. package/dist/heart/daemon/sense-manager.js +178 -37
  84. package/dist/heart/daemon/session-id-resolver.js +131 -0
  85. package/dist/heart/daemon/skill-management-installer.js +94 -0
  86. package/dist/heart/daemon/socket-client.js +109 -4
  87. package/dist/heart/daemon/stale-bundle-prune.js +96 -0
  88. package/dist/heart/daemon/startup-tui.js +264 -0
  89. package/dist/heart/daemon/task-scheduler.js +3 -25
  90. package/dist/heart/daemon/terminal-ui.js +499 -0
  91. package/dist/heart/daemon/thoughts.js +162 -17
  92. package/dist/heart/daemon/up-progress.js +366 -0
  93. package/dist/heart/daemon/vault-items.js +56 -0
  94. package/dist/heart/delegation.js +1 -1
  95. package/dist/heart/habits/habit-migration.js +189 -0
  96. package/dist/heart/habits/habit-parser.js +140 -0
  97. package/dist/heart/habits/habit-runtime-state.js +100 -0
  98. package/dist/heart/habits/habit-scheduler.js +372 -0
  99. package/dist/heart/{daemon → hatch}/hatch-flow.js +52 -117
  100. package/dist/heart/{daemon → hatch}/hatch-specialist.js +3 -3
  101. package/dist/heart/{daemon → hatch}/specialist-prompt.js +12 -9
  102. package/dist/heart/{daemon → hatch}/specialist-tools.js +35 -12
  103. package/dist/heart/identity.js +201 -66
  104. package/dist/heart/kept-notes.js +357 -0
  105. package/dist/heart/kicks.js +1 -1
  106. package/dist/heart/machine-identity.js +161 -0
  107. package/dist/heart/mail-import-discovery.js +353 -0
  108. package/dist/heart/mcp/mcp-server.js +653 -0
  109. package/dist/heart/migrate-config.js +100 -0
  110. package/dist/heart/model-capabilities.js +59 -0
  111. package/dist/heart/outlook/outlook-http-hooks.js +66 -0
  112. package/dist/heart/outlook/outlook-http-response.js +7 -0
  113. package/dist/heart/outlook/outlook-http-routes.js +244 -0
  114. package/dist/heart/outlook/outlook-http-static.js +103 -0
  115. package/dist/heart/outlook/outlook-http-transport.js +116 -0
  116. package/dist/heart/outlook/outlook-http.js +99 -0
  117. package/dist/heart/outlook/outlook-read.js +31 -0
  118. package/dist/heart/outlook/outlook-types.js +27 -0
  119. package/dist/heart/outlook/outlook-view.js +195 -0
  120. package/dist/heart/outlook/readers/agent-machine.js +382 -0
  121. package/dist/heart/outlook/readers/continuity-readers.js +336 -0
  122. package/dist/heart/outlook/readers/mail.js +362 -0
  123. package/dist/heart/outlook/readers/runtime-readers.js +644 -0
  124. package/dist/heart/outlook/readers/sessions.js +232 -0
  125. package/dist/heart/outlook/readers/shared.js +111 -0
  126. package/dist/heart/platform.js +81 -0
  127. package/dist/heart/provider-attempt.js +134 -0
  128. package/dist/heart/provider-binding-resolver.js +255 -0
  129. package/dist/heart/provider-credentials.js +424 -0
  130. package/dist/heart/provider-failover.js +301 -0
  131. package/dist/heart/provider-models.js +81 -0
  132. package/dist/heart/provider-ping.js +262 -0
  133. package/dist/heart/provider-state.js +216 -0
  134. package/dist/heart/provider-visibility.js +188 -0
  135. package/dist/heart/providers/anthropic-token.js +131 -0
  136. package/dist/heart/providers/anthropic.js +193 -55
  137. package/dist/heart/providers/azure.js +104 -13
  138. package/dist/heart/providers/error-classification.js +63 -0
  139. package/dist/heart/providers/github-copilot.js +145 -0
  140. package/dist/heart/providers/minimax-vlm.js +189 -0
  141. package/dist/heart/providers/minimax.js +29 -7
  142. package/dist/heart/providers/openai-codex.js +63 -39
  143. package/dist/heart/runtime-capability-check.js +170 -0
  144. package/dist/heart/runtime-credentials.js +260 -0
  145. package/dist/heart/sense-truth.js +11 -4
  146. package/dist/heart/session-activity.js +43 -22
  147. package/dist/heart/session-events.js +1089 -0
  148. package/dist/heart/session-playback-cli-main.js +5 -0
  149. package/dist/heart/session-playback-cli.js +36 -0
  150. package/dist/heart/session-playback.js +231 -0
  151. package/dist/heart/session-transcript.js +167 -0
  152. package/dist/heart/start-of-turn-packet.js +345 -0
  153. package/dist/heart/streaming.js +48 -28
  154. package/dist/heart/sync.js +332 -0
  155. package/dist/heart/target-resolution.js +127 -0
  156. package/dist/heart/tempo.js +93 -0
  157. package/dist/heart/temporal-view.js +41 -0
  158. package/dist/heart/tool-activity-callbacks.js +36 -0
  159. package/dist/heart/tool-description.js +135 -0
  160. package/dist/heart/tool-friction.js +55 -0
  161. package/dist/heart/tool-loop.js +200 -0
  162. package/dist/heart/turn-context.js +372 -0
  163. package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +1 -1
  164. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
  165. package/dist/heart/versioning/ouro-path-installer.js +425 -0
  166. package/dist/heart/versioning/ouro-version-manager.js +295 -0
  167. package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
  168. package/dist/heart/{daemon → versioning}/update-checker.js +5 -1
  169. package/dist/heart/{daemon → versioning}/update-hooks.js +63 -59
  170. package/dist/mailroom/attention.js +167 -0
  171. package/dist/mailroom/autonomy.js +209 -0
  172. package/dist/mailroom/blob-store.js +606 -0
  173. package/dist/mailroom/core.js +672 -0
  174. package/dist/mailroom/entry.js +160 -0
  175. package/dist/mailroom/file-store.js +426 -0
  176. package/dist/mailroom/mbox-import.js +382 -0
  177. package/dist/mailroom/outbound.js +380 -0
  178. package/dist/mailroom/policy.js +263 -0
  179. package/dist/mailroom/reader.js +219 -0
  180. package/dist/mailroom/search-cache.js +182 -0
  181. package/dist/mailroom/search-relevance.js +319 -0
  182. package/dist/mailroom/smtp-ingress.js +176 -0
  183. package/dist/mailroom/source-state.js +176 -0
  184. package/dist/mailroom/thread.js +109 -0
  185. package/dist/mailroom/travel-extract.js +89 -0
  186. package/dist/mind/bundle-manifest.js +7 -1
  187. package/dist/mind/context.js +164 -101
  188. package/dist/mind/diary-integrity.js +60 -0
  189. package/dist/mind/{memory.js → diary.js} +74 -93
  190. package/dist/mind/embedding-provider.js +60 -0
  191. package/dist/mind/file-state.js +179 -0
  192. package/dist/mind/friends/channel.js +30 -0
  193. package/dist/mind/friends/group-context.js +144 -0
  194. package/dist/mind/friends/resolver.js +54 -2
  195. package/dist/mind/friends/store-file.js +39 -3
  196. package/dist/mind/friends/trust-explanation.js +74 -0
  197. package/dist/mind/friends/types.js +2 -2
  198. package/dist/mind/journal-index.js +161 -0
  199. package/dist/mind/note-search.js +268 -0
  200. package/dist/mind/obligation-steering.js +221 -0
  201. package/dist/mind/pending.js +4 -0
  202. package/dist/mind/prompt-refresh.js +3 -2
  203. package/dist/mind/prompt.js +948 -110
  204. package/dist/mind/provenance-trust.js +26 -0
  205. package/dist/mind/scrutiny.js +173 -0
  206. package/dist/nerves/cli-logging.js +7 -1
  207. package/dist/nerves/coverage/audit-rules.js +15 -6
  208. package/dist/nerves/coverage/audit.js +28 -2
  209. package/dist/nerves/coverage/cli.js +1 -1
  210. package/dist/nerves/coverage/contract.js +5 -5
  211. package/dist/nerves/coverage/file-completeness.js +106 -5
  212. package/dist/nerves/coverage/run-artifacts.js +1 -1
  213. package/dist/nerves/event-buffer.js +111 -0
  214. package/dist/nerves/index.js +224 -4
  215. package/dist/nerves/observation.js +20 -0
  216. package/dist/nerves/redact.js +79 -0
  217. package/dist/nerves/review/cli-main.js +5 -0
  218. package/dist/nerves/review/cli.js +156 -0
  219. package/dist/nerves/review/core.js +152 -0
  220. package/dist/nerves/runtime.js +5 -1
  221. package/dist/outlook-ui/assets/index-BPr5vNuM.css +1 -0
  222. package/dist/outlook-ui/assets/index-Cm51CY9W.js +61 -0
  223. package/dist/outlook-ui/index.html +15 -0
  224. package/dist/repertoire/ado-client.js +15 -56
  225. package/dist/repertoire/ado-semantic.js +11 -10
  226. package/dist/repertoire/api-client.js +97 -0
  227. package/dist/repertoire/bitwarden-store.js +774 -0
  228. package/dist/repertoire/bundle-templates.js +72 -0
  229. package/dist/repertoire/bw-installer.js +180 -0
  230. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  231. package/dist/repertoire/coding/context-pack.js +330 -0
  232. package/dist/repertoire/coding/feedback.js +197 -30
  233. package/dist/repertoire/coding/manager.js +158 -9
  234. package/dist/repertoire/coding/spawner.js +55 -9
  235. package/dist/repertoire/coding/tools.js +170 -7
  236. package/dist/repertoire/commerce-errors.js +109 -0
  237. package/dist/repertoire/commerce-self-test.js +156 -0
  238. package/dist/repertoire/credential-access.js +111 -0
  239. package/dist/repertoire/duffel-client.js +185 -0
  240. package/dist/repertoire/github-client.js +14 -55
  241. package/dist/repertoire/graph-client.js +11 -52
  242. package/dist/repertoire/guardrails.js +396 -0
  243. package/dist/repertoire/mcp-client.js +255 -0
  244. package/dist/repertoire/mcp-manager.js +305 -0
  245. package/dist/repertoire/mcp-tools.js +63 -0
  246. package/dist/repertoire/shell-sessions.js +133 -0
  247. package/dist/repertoire/skills.js +15 -24
  248. package/dist/repertoire/stripe-client.js +131 -0
  249. package/dist/repertoire/tasks/board.js +31 -5
  250. package/dist/repertoire/tasks/fix.js +182 -0
  251. package/dist/repertoire/tasks/index.js +16 -4
  252. package/dist/repertoire/tasks/lifecycle.js +2 -2
  253. package/dist/repertoire/tasks/parser.js +3 -2
  254. package/dist/repertoire/tasks/scanner.js +194 -37
  255. package/dist/repertoire/tasks/transitions.js +16 -78
  256. package/dist/repertoire/tool-results.js +29 -0
  257. package/dist/repertoire/tools-attachments.js +317 -0
  258. package/dist/repertoire/tools-base.js +46 -921
  259. package/dist/repertoire/tools-bluebubbles.js +1 -0
  260. package/dist/repertoire/tools-bridge.js +141 -0
  261. package/dist/repertoire/tools-bundle.js +984 -0
  262. package/dist/repertoire/tools-config.js +185 -0
  263. package/dist/repertoire/tools-continuity.js +248 -0
  264. package/dist/repertoire/tools-credential.js +381 -0
  265. package/dist/repertoire/tools-files.js +342 -0
  266. package/dist/repertoire/tools-flight.js +224 -0
  267. package/dist/repertoire/tools-flow.js +105 -0
  268. package/dist/repertoire/tools-github.js +1 -7
  269. package/dist/repertoire/tools-mail.js +1377 -0
  270. package/dist/repertoire/tools-notes.js +376 -0
  271. package/dist/repertoire/tools-session.js +749 -0
  272. package/dist/repertoire/tools-shell.js +120 -0
  273. package/dist/repertoire/tools-stripe.js +180 -0
  274. package/dist/repertoire/tools-surface.js +243 -0
  275. package/dist/repertoire/tools-teams.js +9 -39
  276. package/dist/repertoire/tools-travel.js +125 -0
  277. package/dist/repertoire/tools-trip.js +356 -0
  278. package/dist/repertoire/tools-user-profile.js +144 -0
  279. package/dist/repertoire/tools-vault.js +40 -0
  280. package/dist/repertoire/tools.js +144 -115
  281. package/dist/repertoire/travel-api-client.js +360 -0
  282. package/dist/repertoire/user-profile.js +131 -0
  283. package/dist/repertoire/vault-setup.js +246 -0
  284. package/dist/repertoire/vault-unlock.js +561 -0
  285. package/dist/scripts/claude-code-hook.js +41 -0
  286. package/dist/scripts/claude-code-stop-hook.js +47 -0
  287. package/dist/senses/attention-queue.js +116 -0
  288. package/dist/senses/bluebubbles/attachment-cache.js +53 -0
  289. package/dist/senses/bluebubbles/attachment-download.js +137 -0
  290. package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +219 -18
  291. package/dist/senses/bluebubbles/entry.js +73 -0
  292. package/dist/senses/{bluebubbles-inbound-log.js → bluebubbles/inbound-log.js} +20 -3
  293. package/dist/senses/bluebubbles/index.js +1881 -0
  294. package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -70
  295. package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +33 -12
  296. package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +3 -3
  297. package/dist/senses/bluebubbles/processed-log.js +111 -0
  298. package/dist/senses/bluebubbles/replay.js +129 -0
  299. package/dist/senses/{bluebubbles-runtime-state.js → bluebubbles/runtime-state.js} +2 -2
  300. package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
  301. package/dist/senses/cli/bracketed-paste.js +82 -0
  302. package/dist/senses/cli/image-paste.js +287 -0
  303. package/dist/senses/cli/image-ref-navigation.js +75 -0
  304. package/dist/senses/cli/ink-app.js +156 -0
  305. package/dist/senses/cli/inline-diff.js +64 -0
  306. package/dist/senses/cli/input-keys.js +174 -0
  307. package/dist/senses/cli/kill-ring.js +86 -0
  308. package/dist/senses/cli/message-list.js +51 -0
  309. package/dist/senses/cli/ouro-tui.js +605 -0
  310. package/dist/senses/cli/spinner-imperative.js +135 -0
  311. package/dist/senses/cli/spinner.js +101 -0
  312. package/dist/senses/cli/status-line.js +60 -0
  313. package/dist/senses/cli/streaming-markdown.js +526 -0
  314. package/dist/senses/cli/tool-display.js +83 -0
  315. package/dist/senses/cli/tool-render.js +85 -0
  316. package/dist/senses/cli/tui-store.js +240 -0
  317. package/dist/senses/cli/virtual-list.js +35 -0
  318. package/dist/senses/cli-entry.js +60 -8
  319. package/dist/senses/cli-layout.js +187 -0
  320. package/dist/senses/cli.js +511 -209
  321. package/dist/senses/commands.js +66 -3
  322. package/dist/senses/habit-turn-message.js +108 -0
  323. package/dist/senses/inner-dialog-worker.js +175 -21
  324. package/dist/senses/inner-dialog.js +330 -27
  325. package/dist/senses/mail-entry.js +66 -0
  326. package/dist/senses/mail.js +379 -0
  327. package/dist/senses/pipeline.js +573 -164
  328. package/dist/senses/proactive-content-guard.js +51 -0
  329. package/dist/senses/shared-turn.js +248 -0
  330. package/dist/senses/surface-tool.js +68 -0
  331. package/dist/senses/teams-entry.js +60 -8
  332. package/dist/senses/teams.js +405 -170
  333. package/dist/senses/trust-gate.js +100 -5
  334. package/dist/trips/core.js +138 -0
  335. package/dist/trips/store.js +146 -0
  336. package/package.json +39 -8
  337. package/skills/agent-commerce.md +106 -0
  338. package/skills/browser-navigation.md +117 -0
  339. package/skills/commerce-setup-guide.md +116 -0
  340. package/skills/commerce-setup.md +84 -0
  341. package/skills/configure-dev-tools.md +101 -0
  342. package/skills/travel-planning.md +138 -0
  343. package/dist/heart/daemon/ouro-path-installer.js +0 -178
  344. package/dist/heart/daemon/subagent-installer.js +0 -166
  345. package/dist/heart/session-recall.js +0 -116
  346. package/dist/mind/associative-recall.js +0 -209
  347. package/dist/senses/bluebubbles-entry.js +0 -13
  348. package/dist/senses/bluebubbles.js +0 -1142
  349. package/dist/senses/debug-activity.js +0 -148
  350. package/subagents/README.md +0 -86
  351. package/subagents/work-doer.md +0 -237
  352. package/subagents/work-merger.md +0 -618
  353. package/subagents/work-planner.md +0 -390
  354. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  355. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  356. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  357. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  358. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  359. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  360. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  361. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  362. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  363. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  364. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  365. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  366. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  367. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  368. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
  369. /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ensureSkillManagement = ensureSkillManagement;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const runtime_1 = require("../../nerves/runtime");
40
+ const identity_1 = require("../identity");
41
+ const SKILL_MANAGEMENT_URL = "https://raw.githubusercontent.com/ouroborosbot/ouroboros-skills/main/skills/skill-management/SKILL.md";
42
+ async function ensureSkillManagement() {
43
+ const bundlesRoot = (0, identity_1.getAgentBundlesRoot)();
44
+ if (!fs.existsSync(bundlesRoot))
45
+ return;
46
+ // Find all agent bundles
47
+ const entries = fs.readdirSync(bundlesRoot).filter(e => e.endsWith(".ouro"));
48
+ if (entries.length === 0)
49
+ return;
50
+ // Check if ANY bundle is missing the skill
51
+ const missing = entries.filter(e => {
52
+ const targetPath = path.join(bundlesRoot, e, "skills", "skill-management.md");
53
+ return !fs.existsSync(targetPath);
54
+ });
55
+ if (missing.length === 0)
56
+ return;
57
+ // eslint-disable-next-line no-console -- terminal UX: visible install status
58
+ console.log("installing skill-management from ouroboros-skills...");
59
+ try {
60
+ const response = await fetch(SKILL_MANAGEMENT_URL);
61
+ if (!response.ok) {
62
+ // eslint-disable-next-line no-console -- terminal UX: visible install status
63
+ console.error(`✗ failed to fetch skill-management (HTTP ${response.status})`);
64
+ (0, runtime_1.emitNervesEvent)({
65
+ level: "warn",
66
+ component: "daemon",
67
+ event: "daemon.skill_management_install_error",
68
+ message: "failed to fetch skill-management from GitHub",
69
+ meta: { status: response.status, url: SKILL_MANAGEMENT_URL },
70
+ });
71
+ return;
72
+ }
73
+ const content = await response.text();
74
+ for (const bundle of missing) {
75
+ const skillsDir = path.join(bundlesRoot, bundle, "skills");
76
+ const targetPath = path.join(skillsDir, "skill-management.md");
77
+ fs.mkdirSync(skillsDir, { recursive: true });
78
+ fs.writeFileSync(targetPath, content, "utf-8");
79
+ }
80
+ // eslint-disable-next-line no-console -- terminal UX: visible install status
81
+ console.log(`✓ installed skill-management (${missing.length} agent${missing.length > 1 ? "s" : ""})`);
82
+ }
83
+ catch (error) {
84
+ // eslint-disable-next-line no-console -- terminal UX: visible install status
85
+ console.error(`✗ failed to install skill-management: ${error instanceof Error ? error.message : String(error)}`);
86
+ (0, runtime_1.emitNervesEvent)({
87
+ level: "warn",
88
+ component: "daemon",
89
+ event: "daemon.skill_management_install_error",
90
+ message: "failed to install skill-management skill",
91
+ meta: { error: error instanceof Error ? error.message : String(error) },
92
+ });
93
+ }
94
+ }
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.DEFAULT_DAEMON_SOCKET_PATH = void 0;
37
+ exports.__bypassVitestGuardForTests = __bypassVitestGuardForTests;
37
38
  exports.sendDaemonCommand = sendDaemonCommand;
38
39
  exports.checkDaemonSocketAlive = checkDaemonSocketAlive;
39
40
  exports.requestInnerWake = requestInnerWake;
@@ -41,7 +42,81 @@ const fs = __importStar(require("fs"));
41
42
  const net = __importStar(require("net"));
42
43
  const runtime_1 = require("../../nerves/runtime");
43
44
  exports.DEFAULT_DAEMON_SOCKET_PATH = "/tmp/ouroboros-daemon.sock";
45
+ /**
46
+ * Defense-in-depth: detect if we're running under vitest. Tests that forget
47
+ * to `vi.mock("../../heart/daemon/socket-client")` would otherwise leak real
48
+ * `inner.wake` commands (with whatever literal agent name the test uses,
49
+ * commonly "testagent") into whichever real daemon happens to be running on
50
+ * the developer's machine. That has caused real outages when test loops
51
+ * flooded the production-on-localhost daemon.
52
+ *
53
+ * We detect vitest via `process.argv` (not env vars — the project bans those)
54
+ * and convert all socket operations into safe no-ops. Individual tests are
55
+ * still expected to vi.mock this module so they can assert on call counts,
56
+ * but this guard guarantees that "I forgot to mock" can never again take
57
+ * down the daemon.
58
+ *
59
+ * The socket-client's OWN tests (which exercise the real functions against a
60
+ * test socket) call `__bypassVitestGuardForTests(true)` to opt out of the
61
+ * guard. We persist that flag on globalThis so it survives `vi.resetModules`
62
+ * (which clears the module cache between tests).
63
+ *
64
+ * HARDENING (cross-file leak protection): the bypass flag lives on globalThis
65
+ * and is process-wide. With vitest's default `fileParallelism: true`,
66
+ * concurrently-running test files in the same worker can have the bypass on
67
+ * even though they didn't ask for it — and any test that uses
68
+ * `name: "testagent"` and exercises a code path through this module will
69
+ * leak `inner.wake testagent` to the production daemon socket. We saw this
70
+ * cause a real outage on 2026-04-08 (daemon SIGTERMed mid-validation).
71
+ *
72
+ * Defense: even when the bypass flag is set, we ALWAYS hard-block calls that
73
+ * target the production daemon socket path. Test files that legitimately
74
+ * exercise the real socket-client code paths use a synthetic socket path like
75
+ * `/tmp/daemon.sock` or `/tmp/some-real-daemon.sock` — those keep working.
76
+ * Calls to `/tmp/ouroboros-daemon.sock` (DEFAULT_DAEMON_SOCKET_PATH) under
77
+ * vitest are unconditionally blocked, regardless of bypass state. This makes
78
+ * the cross-file leak vector impossible without restricting legitimate
79
+ * socket-client unit tests.
80
+ */
81
+ const BYPASS_KEY = "__ouro_socket_client_bypass_vitest_guard__";
82
+ /** ONLY for socket-client.test.ts. Do not call from any other test or production code. */
83
+ function __bypassVitestGuardForTests(bypass) {
84
+ ;
85
+ globalThis[BYPASS_KEY] = bypass;
86
+ }
87
+ function isVitestProcess() {
88
+ /* v8 ignore next -- defensive: process and process.argv always exist in node @preserve */
89
+ if (typeof process === "undefined" || !Array.isArray(process.argv))
90
+ return false;
91
+ return process.argv.some((arg) => typeof arg === "string" && arg.includes("vitest"));
92
+ }
93
+ function isProductionDaemonSocket(socketPath) {
94
+ return socketPath === exports.DEFAULT_DAEMON_SOCKET_PATH;
95
+ }
96
+ /**
97
+ * Returns true when this socket call should be converted into a safe no-op
98
+ * because we're under vitest and the call would otherwise leak into a real
99
+ * daemon. The bypass flag short-circuits the guard for non-production socket
100
+ * paths only — production socket paths are ALWAYS blocked under vitest.
101
+ */
102
+ function shouldSuppressSocketCall(socketPath) {
103
+ if (!isVitestProcess())
104
+ return false;
105
+ if (isProductionDaemonSocket(socketPath))
106
+ return true;
107
+ return globalThis[BYPASS_KEY] !== true;
108
+ }
44
109
  function sendDaemonCommand(socketPath, command) {
110
+ if (shouldSuppressSocketCall(socketPath)) {
111
+ (0, runtime_1.emitNervesEvent)({
112
+ level: "warn",
113
+ component: "daemon",
114
+ event: "daemon.socket_command_test_blocked",
115
+ message: "blocked socket command from leaking into real daemon under vitest",
116
+ meta: { socketPath, kind: command.kind, isProductionSocket: isProductionDaemonSocket(socketPath) },
117
+ });
118
+ return Promise.resolve({ ok: true, message: "test mode: socket call suppressed" });
119
+ }
45
120
  (0, runtime_1.emitNervesEvent)({
46
121
  component: "daemon",
47
122
  event: "daemon.socket_command_start",
@@ -52,8 +127,23 @@ function sendDaemonCommand(socketPath, command) {
52
127
  const client = net.createConnection(socketPath);
53
128
  let raw = "";
54
129
  client.on("connect", () => {
55
- client.write(JSON.stringify(command));
56
- client.end();
130
+ // Write the command + newline delimiter. DO NOT call client.end()
131
+ // afterwards — the server closes the connection once it has written
132
+ // its response, which triggers our on("end") handler with the full
133
+ // response in `raw`.
134
+ //
135
+ // Calling client.end() here half-closes the TCP connection. The
136
+ // daemon server uses net.createServer() with the default
137
+ // allowHalfOpen: false, so when the server sees the client's FIN it
138
+ // auto-closes its own writable side — and any response the server
139
+ // tries to write after processing a long-running command (like
140
+ // agent.senseTurn, which runs a full LLM turn) gets dropped on the
141
+ // floor. Verified via repro: with client.end(), agent.senseTurn
142
+ // returns empty in ~149ms; without it, the same call returns the real
143
+ // response in ~5.8s. This is a regression of the fix in #303 that
144
+ // was silently reverted by 253e4b1f (commit titled "socket half-close
145
+ // fix" but actually *added* the half-close back).
146
+ client.write(JSON.stringify(command) + "\n");
57
147
  });
58
148
  client.on("data", (chunk) => {
59
149
  raw += chunk.toString("utf-8");
@@ -132,6 +222,9 @@ function sendDaemonCommand(socketPath, command) {
132
222
  });
133
223
  }
134
224
  function checkDaemonSocketAlive(socketPath) {
225
+ if (shouldSuppressSocketCall(socketPath)) {
226
+ return Promise.resolve(false);
227
+ }
135
228
  (0, runtime_1.emitNervesEvent)({
136
229
  component: "daemon",
137
230
  event: "daemon.socket_alive_check_start",
@@ -161,8 +254,10 @@ function checkDaemonSocketAlive(socketPath) {
161
254
  });
162
255
  }
163
256
  client.on("connect", () => {
164
- client.write(JSON.stringify({ kind: "daemon.status" }));
165
- client.end();
257
+ // Same half-close rationale as sendDaemonCommand: do NOT call
258
+ // client.end() here. The server closes after responding and that
259
+ // triggers the on("end") handler below.
260
+ client.write(JSON.stringify({ kind: "daemon.status" }) + "\n");
166
261
  });
167
262
  client.on("data", (chunk) => {
168
263
  raw += chunk.toString("utf-8");
@@ -184,6 +279,16 @@ function checkDaemonSocketAlive(socketPath) {
184
279
  });
185
280
  }
186
281
  async function requestInnerWake(agent, socketPath = exports.DEFAULT_DAEMON_SOCKET_PATH) {
282
+ if (shouldSuppressSocketCall(socketPath)) {
283
+ (0, runtime_1.emitNervesEvent)({
284
+ level: "warn",
285
+ component: "daemon",
286
+ event: "daemon.inner_wake_test_blocked",
287
+ message: "blocked inner wake from leaking into real daemon under vitest",
288
+ meta: { agent, socketPath, isProductionSocket: isProductionDaemonSocket(socketPath) },
289
+ });
290
+ return null;
291
+ }
187
292
  const socketAvailable = fs.existsSync(socketPath);
188
293
  (0, runtime_1.emitNervesEvent)({
189
294
  component: "daemon",
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.pruneStaleEphemeralBundles = pruneStaleEphemeralBundles;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const identity_1 = require("../identity");
40
+ const runtime_1 = require("../../nerves/runtime");
41
+ /**
42
+ * Scan the bundles root for `.ouro` directories that have no `agent.json`
43
+ * file (definitively dead ephemeral bundles) and delete them.
44
+ *
45
+ * Returns a list of pruned bundle directory names (e.g. `["stale.ouro"]`)
46
+ * for display purposes. Bundles that have `agent.json` -- even if disabled
47
+ * -- are never deleted. Errors on individual bundles are swallowed so that
48
+ * one permission-denied doesn't block pruning the rest.
49
+ */
50
+ function pruneStaleEphemeralBundles(deps = {}) {
51
+ const bundlesRoot = deps.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
52
+ const readdirSync = deps.readdirSync ?? fs.readdirSync;
53
+ const existsSync = deps.existsSync ?? fs.existsSync;
54
+ const rmSync = deps.rmSync ?? fs.rmSync;
55
+ let entries;
56
+ try {
57
+ entries = readdirSync(bundlesRoot, { withFileTypes: true });
58
+ }
59
+ catch {
60
+ return [];
61
+ }
62
+ const pruned = [];
63
+ for (const entry of entries) {
64
+ if (!entry.isDirectory() || !entry.name.endsWith(".ouro"))
65
+ continue;
66
+ const bundlePath = path.join(bundlesRoot, entry.name);
67
+ const agentJsonPath = path.join(bundlePath, "agent.json");
68
+ if (existsSync(agentJsonPath))
69
+ continue;
70
+ try {
71
+ rmSync(bundlePath, { recursive: true, force: true });
72
+ pruned.push(entry.name);
73
+ (0, runtime_1.emitNervesEvent)({
74
+ level: "info",
75
+ component: "daemon",
76
+ event: "daemon.stale_bundle_pruned",
77
+ message: `pruned stale ephemeral bundle: ${entry.name}`,
78
+ meta: { bundle: entry.name, bundlePath },
79
+ });
80
+ }
81
+ catch (error) {
82
+ (0, runtime_1.emitNervesEvent)({
83
+ level: "warn",
84
+ component: "daemon",
85
+ event: "daemon.stale_bundle_prune_error",
86
+ message: `failed to prune stale bundle: ${entry.name}`,
87
+ meta: {
88
+ bundle: entry.name,
89
+ bundlePath,
90
+ error: error instanceof Error ? error.message : String(error),
91
+ },
92
+ });
93
+ }
94
+ }
95
+ return pruned;
96
+ }
@@ -0,0 +1,264 @@
1
+ "use strict";
2
+ /**
3
+ * Startup TUI — real-time progress display for `ouro up`.
4
+ *
5
+ * Replaces the old `verifyDaemonAlive()` socket poll with a richer system
6
+ * that shows per-agent status, waits for stability, and reports degraded
7
+ * agents with actionable error information.
8
+ *
9
+ * Pure functions (`renderStartupProgress`, `assessStability`) are fully
10
+ * testable. The polling loop (`pollDaemonStartup`) uses dependency injection
11
+ * for all I/O.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.assessStability = assessStability;
15
+ exports.renderStartupProgress = renderStartupProgress;
16
+ exports.renderWaitingForDaemon = renderWaitingForDaemon;
17
+ exports.pollDaemonStartup = pollDaemonStartup;
18
+ const cli_render_1 = require("./cli-render");
19
+ const runtime_1 = require("../../nerves/runtime");
20
+ const terminal_ui_1 = require("./terminal-ui");
21
+ // ── Constants ──
22
+ const SPINNER_FRAMES = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏";
23
+ const STABILITY_THRESHOLD_MS = 5_000;
24
+ const POLL_INTERVAL_MS = 500;
25
+ // ── ANSI helpers ──
26
+ const RESET = "\x1b[0m";
27
+ const BOLD = "\x1b[1m";
28
+ const DIM = "\x1b[2m";
29
+ const GREEN = "\x1b[38;2;46;204;64m";
30
+ const RED = "\x1b[38;2;231;76;60m";
31
+ const YELLOW = "\x1b[38;2;230;190;50m";
32
+ // ── Pure functions ──
33
+ /**
34
+ * Assess whether all workers have reached a terminal state (stable or crashed).
35
+ * A worker is "stable" when status is "running" and it has been running for
36
+ * at least STABILITY_THRESHOLD_MS. A worker is "definitively failed" when
37
+ * status is "crashed". All other states are unresolved.
38
+ */
39
+ function assessStability(payload, now) {
40
+ const stable = [];
41
+ const degraded = [];
42
+ let allResolved = true;
43
+ for (const worker of payload.workers) {
44
+ if (worker.status === "crashed") {
45
+ degraded.push({
46
+ agent: worker.agent,
47
+ errorReason: worker.errorReason ?? "unknown error",
48
+ fixHint: worker.fixHint ?? "check daemon logs",
49
+ });
50
+ }
51
+ else if (worker.status === "running" && worker.startedAt !== null) {
52
+ const startedMs = new Date(worker.startedAt).getTime();
53
+ const runningMs = now - startedMs;
54
+ if (runningMs >= STABILITY_THRESHOLD_MS) {
55
+ stable.push(worker.agent);
56
+ }
57
+ else {
58
+ allResolved = false;
59
+ }
60
+ }
61
+ else {
62
+ // starting, stopped, or running with null startedAt — not yet resolved
63
+ allResolved = false;
64
+ }
65
+ }
66
+ return { resolved: allResolved, stable, degraded };
67
+ }
68
+ /**
69
+ * Build an ANSI string for in-place terminal display during polling.
70
+ * Uses cursor-up and line-clear escapes to overwrite previous output.
71
+ */
72
+ function renderStartupProgress(payload, elapsed, prevLineCount = 0, options = {}) {
73
+ const isTTY = options.isTTY ?? true;
74
+ const frameIndex = Math.floor(elapsed / 100) % SPINNER_FRAMES.length;
75
+ const spinner = SPINNER_FRAMES[frameIndex];
76
+ const lines = [];
77
+ const elapsedSec = (elapsed / 1000).toFixed(1);
78
+ lines.push(isTTY
79
+ ? `${spinner} ${BOLD}waiting for agents${RESET} ${DIM}(${elapsedSec}s)${RESET}`
80
+ : `${spinner} waiting for agents (${elapsedSec}s)`);
81
+ for (const worker of payload.workers) {
82
+ const statusText = isTTY ? colorStatus(worker.status) : worker.status;
83
+ lines.push(` ${worker.agent}/${worker.worker}: ${statusText}`);
84
+ }
85
+ return renderStartupLines(lines, prevLineCount, isTTY);
86
+ }
87
+ /**
88
+ * Render a pre-socket status line showing what the daemon is doing.
89
+ */
90
+ function renderWaitingForDaemon(elapsed, latestEvent, prevLineCount = 0, options = {}) {
91
+ const isTTY = options.isTTY ?? true;
92
+ const elapsedSec = (elapsed / 1000).toFixed(1);
93
+ const frameIndex = Math.floor(elapsed / 100) % SPINNER_FRAMES.length;
94
+ const spinner = SPINNER_FRAMES[frameIndex];
95
+ const lines = [];
96
+ lines.push(isTTY
97
+ ? `${spinner} ${BOLD}starting background service${RESET} ${DIM}(${elapsedSec}s)${RESET}`
98
+ : `${spinner} starting background service (${elapsedSec}s)`);
99
+ if (latestEvent) {
100
+ const detail = `latest daemon event: ${latestEvent}`;
101
+ lines.push(isTTY ? ` ${DIM}${detail}${RESET}` : ` ${detail}`);
102
+ }
103
+ return renderStartupLines(lines, prevLineCount, isTTY);
104
+ }
105
+ /**
106
+ * Render the final summary after all agents have resolved.
107
+ */
108
+ function renderFinalSummary(result, isTTY) {
109
+ const lines = [];
110
+ for (const agent of result.stable) {
111
+ lines.push(isTTY ? ` ${GREEN}\u2713${RESET} ${agent}: ${GREEN}stable${RESET}` : ` \u2713 ${agent}: stable`);
112
+ }
113
+ for (const d of result.degraded) {
114
+ lines.push(isTTY ? ` ${RED}\u2717${RESET} ${d.agent}: ${RED}degraded${RESET}` : ` \u2717 ${d.agent}: degraded`);
115
+ if (d.errorReason !== "unknown error") {
116
+ lines.push(isTTY ? ` ${DIM}error: ${d.errorReason}${RESET}` : ` error: ${d.errorReason}`);
117
+ }
118
+ if (d.fixHint !== "check daemon logs") {
119
+ lines.push(isTTY ? ` ${DIM}fix: ${d.fixHint}${RESET}` : ` fix: ${d.fixHint}`);
120
+ }
121
+ }
122
+ if (!isTTY)
123
+ return lines.join("\n") + "\n";
124
+ return lines.map((line) => `\x1b[2K${line}`).join("\n") + "\n";
125
+ }
126
+ // ── Polling loop ──
127
+ /**
128
+ * Poll the daemon's status socket until all agents are stable or definitively
129
+ * failed, rendering real-time progress to the terminal.
130
+ *
131
+ * Detects daemon process death: if the spawned PID is no longer alive and the
132
+ * socket never came up, reports the failure immediately instead of spinning.
133
+ */
134
+ async function pollDaemonStartup(deps) {
135
+ const startTime = deps.now();
136
+ let prevLineCount = 0;
137
+ const isTTY = deps.isTTY ?? true;
138
+ const isAlive = deps.isProcessAlive ?? defaultIsProcessAlive;
139
+ const shouldRender = deps.render ?? true;
140
+ let lastProgress = null;
141
+ const reportProgress = (message) => {
142
+ if (!deps.onProgress || message === lastProgress)
143
+ return;
144
+ lastProgress = message;
145
+ deps.onProgress(message);
146
+ };
147
+ (0, runtime_1.emitNervesEvent)({
148
+ component: "daemon",
149
+ event: "daemon.startup_poll_start",
150
+ message: "beginning startup stability polling",
151
+ meta: { socketPath: deps.socketPath, daemonPid: deps.daemonPid },
152
+ });
153
+ while (true) {
154
+ const now = deps.now();
155
+ const elapsed = now - startTime;
156
+ let payload = null;
157
+ try {
158
+ const response = await deps.sendCommand(deps.socketPath, { kind: "daemon.status" });
159
+ payload = (0, cli_render_1.parseStatusPayload)(response.data);
160
+ }
161
+ catch {
162
+ // Socket not yet available — check if the daemon process is still alive
163
+ if (deps.daemonPid !== null && !isAlive(deps.daemonPid)) {
164
+ const latestEvent = deps.readLatestDaemonEvent?.() ?? null;
165
+ const errorMsg = latestEvent ?? "daemon process died during startup";
166
+ (0, runtime_1.emitNervesEvent)({
167
+ level: "error",
168
+ component: "daemon",
169
+ event: "daemon.startup_process_died",
170
+ message: "daemon process died before socket came up",
171
+ meta: { pid: deps.daemonPid, lastEvent: latestEvent },
172
+ });
173
+ // Clear the waiting line
174
+ if (isTTY && prevLineCount > 0) {
175
+ let clear = `\x1b[${prevLineCount}A`;
176
+ for (let i = 0; i < prevLineCount; i++)
177
+ clear += `\x1b[2K\n`;
178
+ deps.writeRaw(clear);
179
+ }
180
+ return {
181
+ stable: [],
182
+ degraded: [{ agent: "daemon", errorReason: errorMsg, fixHint: "check daemon logs or run `ouro doctor`" }],
183
+ };
184
+ }
185
+ // Show what the daemon is doing from its log
186
+ const latestEvent = deps.readLatestDaemonEvent?.() ?? null;
187
+ reportProgress([
188
+ "waiting for Ouro to answer",
189
+ latestEvent ? `- latest daemon event: ${latestEvent}` : "- background service is still starting",
190
+ ].join("\n"));
191
+ if (shouldRender) {
192
+ const output = renderWaitingForDaemon(elapsed, latestEvent, prevLineCount, { isTTY });
193
+ deps.writeRaw(output);
194
+ prevLineCount = latestEvent ? 2 : 1;
195
+ }
196
+ }
197
+ if (payload) {
198
+ reportProgress(formatStartupProgressDetail(payload));
199
+ if (shouldRender) {
200
+ const output = renderStartupProgress(payload, elapsed, prevLineCount, { isTTY });
201
+ deps.writeRaw(output);
202
+ prevLineCount = payload.workers.length + 1;
203
+ }
204
+ const assessment = assessStability(payload, now);
205
+ if (assessment.resolved) {
206
+ const result = {
207
+ stable: assessment.stable,
208
+ degraded: assessment.degraded,
209
+ };
210
+ if (shouldRender) {
211
+ const summary = renderFinalSummary(result, isTTY);
212
+ deps.writeRaw(summary);
213
+ }
214
+ (0, runtime_1.emitNervesEvent)({
215
+ component: "daemon",
216
+ event: "daemon.startup_poll_end",
217
+ message: "startup polling complete",
218
+ meta: {
219
+ stableCount: result.stable.length,
220
+ degradedCount: result.degraded.length,
221
+ elapsedMs: elapsed,
222
+ },
223
+ });
224
+ return result;
225
+ }
226
+ }
227
+ await deps.sleep(POLL_INTERVAL_MS);
228
+ }
229
+ }
230
+ function formatStartupWorkerLine(payload) {
231
+ const base = `- ${payload.agent}/${payload.worker}: ${payload.status}`;
232
+ if (payload.status === "crashed" && payload.errorReason) {
233
+ return `${base} (${payload.errorReason})`;
234
+ }
235
+ return base;
236
+ }
237
+ function formatStartupProgressDetail(payload) {
238
+ if (payload.workers.length === 0)
239
+ return "Ouro answered";
240
+ return [
241
+ "Ouro answered",
242
+ ...payload.workers.map((worker) => formatStartupWorkerLine(worker)),
243
+ ].join("\n");
244
+ }
245
+ function colorStatus(status) {
246
+ const statusColor = status === "running" ? GREEN
247
+ : status === "crashed" ? RED
248
+ : YELLOW;
249
+ return `${statusColor}${status}${RESET}`;
250
+ }
251
+ function renderStartupLines(lines, prevLineCount, isTTY) {
252
+ return (0, terminal_ui_1.renderOverwriteFrame)(lines, prevLineCount, isTTY);
253
+ }
254
+ /* v8 ignore start -- process liveness check: uses real process.kill(0), tested via deployment @preserve */
255
+ function defaultIsProcessAlive(pid) {
256
+ try {
257
+ process.kill(pid, 0);
258
+ return true;
259
+ }
260
+ catch {
261
+ return false;
262
+ }
263
+ }
264
+ /* v8 ignore stop */
@@ -39,6 +39,7 @@ const path = __importStar(require("path"));
39
39
  const identity_1 = require("../identity");
40
40
  const runtime_1 = require("../../nerves/runtime");
41
41
  const parser_1 = require("../../repertoire/tasks/parser");
42
+ const cadence_1 = require("./cadence");
42
43
  function walkMarkdownFiles(root, readdirSync, existsSync, files) {
43
44
  if (!existsSync(root))
44
45
  return;
@@ -53,29 +54,6 @@ function walkMarkdownFiles(root, readdirSync, existsSync, files) {
53
54
  }
54
55
  }
55
56
  }
56
- function parseCadence(raw) {
57
- if (typeof raw !== "string")
58
- return null;
59
- const value = raw.trim();
60
- if (!value)
61
- return null;
62
- // Cron format (minute hour day month weekday)
63
- if (/^\S+\s+\S+\s+\S+\s+\S+\s+\S+$/.test(value)) {
64
- return value;
65
- }
66
- const cadenceMatch = /^(\d+)(m|h|d)$/.exec(value);
67
- if (!cadenceMatch)
68
- return null;
69
- const interval = Number.parseInt(cadenceMatch[1], 10);
70
- if (!Number.isFinite(interval) || interval <= 0)
71
- return null;
72
- const unit = cadenceMatch[2];
73
- if (unit === "m")
74
- return `*/${interval} * * * *`;
75
- if (unit === "h")
76
- return `0 */${interval} * * *`;
77
- return `0 0 */${interval} * *`;
78
- }
79
57
  function parseScheduledAt(raw) {
80
58
  if (typeof raw !== "string")
81
59
  return null;
@@ -140,7 +118,7 @@ class TaskDrivenScheduler {
140
118
  const nextTaskPaths = new Map();
141
119
  for (const agent of this.agents) {
142
120
  const taskRoot = path.join(this.bundlesRoot, `${agent}.ouro`, "tasks");
143
- const collections = ["one-shots", "ongoing", "habits"];
121
+ const collections = ["one-shots", "ongoing"];
144
122
  const files = [];
145
123
  for (const collection of collections) {
146
124
  walkMarkdownFiles(path.join(taskRoot, collection), this.readdirSync, this.existsSync, files);
@@ -157,7 +135,7 @@ class TaskDrivenScheduler {
157
135
  nextTaskPaths.set(`${agent}:${taskId}`, filePath);
158
136
  if (task.status === "done")
159
137
  continue;
160
- const cadence = parseCadence(task.frontmatter.cadence);
138
+ const cadence = (0, cadence_1.parseCadenceToCron)(task.frontmatter.cadence);
161
139
  if (cadence) {
162
140
  const id = `${agent}:${taskId}:cadence`;
163
141
  nextJobs.set(id, {