@ouro.bot/cli 0.1.0-alpha.42 → 0.1.0-alpha.420

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 (334) hide show
  1. package/README.md +118 -15
  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 +2627 -9
  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 +832 -0
  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 +424 -0
  28. package/dist/heart/bridges/manager.js +358 -0
  29. package/dist/heart/bridges/state-machine.js +135 -0
  30. package/dist/heart/bridges/store.js +123 -0
  31. package/dist/heart/bundle-state.js +168 -0
  32. package/dist/heart/commitments.js +111 -0
  33. package/dist/heart/config-registry.js +304 -0
  34. package/dist/heart/config.js +110 -128
  35. package/dist/heart/core.js +801 -217
  36. package/dist/heart/cross-chat-delivery.js +131 -0
  37. package/dist/heart/daemon/agent-config-check.js +419 -0
  38. package/dist/heart/daemon/agent-discovery.js +79 -3
  39. package/dist/heart/daemon/agent-service.js +360 -0
  40. package/dist/heart/daemon/agentic-repair.js +214 -0
  41. package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
  42. package/dist/heart/daemon/cadence.js +70 -0
  43. package/dist/heart/daemon/cli-defaults.js +605 -0
  44. package/dist/heart/daemon/cli-exec.js +4140 -0
  45. package/dist/heart/daemon/cli-help.js +413 -0
  46. package/dist/heart/daemon/cli-parse.js +1151 -0
  47. package/dist/heart/daemon/cli-render-doctor.js +57 -0
  48. package/dist/heart/daemon/cli-render.js +561 -0
  49. package/dist/heart/daemon/cli-types.js +8 -0
  50. package/dist/heart/daemon/daemon-cli.js +28 -1582
  51. package/dist/heart/daemon/daemon-entry.js +356 -3
  52. package/dist/heart/daemon/daemon-health.js +141 -0
  53. package/dist/heart/daemon/daemon-runtime-sync.js +171 -12
  54. package/dist/heart/daemon/daemon-tombstone.js +236 -0
  55. package/dist/heart/daemon/daemon.js +684 -58
  56. package/dist/heart/daemon/doctor-types.js +8 -0
  57. package/dist/heart/daemon/doctor.js +427 -0
  58. package/dist/heart/daemon/health-monitor.js +79 -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/inner-status.js +89 -0
  63. package/dist/heart/daemon/interactive-repair.js +307 -0
  64. package/dist/heart/daemon/launchd.js +46 -9
  65. package/dist/heart/daemon/log-tailer.js +82 -12
  66. package/dist/heart/daemon/logs-prune.js +105 -0
  67. package/dist/heart/daemon/message-router.js +2 -2
  68. package/dist/heart/daemon/os-cron-deps.js +134 -0
  69. package/dist/heart/daemon/ouro-bot-entry.js +4 -2
  70. package/dist/heart/daemon/ouro-entry.js +3 -1
  71. package/dist/heart/daemon/process-manager.js +214 -0
  72. package/dist/heart/daemon/provider-discovery.js +137 -0
  73. package/dist/heart/daemon/pulse.js +475 -0
  74. package/dist/heart/daemon/readiness-repair.js +250 -0
  75. package/dist/heart/daemon/run-hooks.js +2 -0
  76. package/dist/heart/daemon/runtime-logging.js +67 -16
  77. package/dist/heart/daemon/runtime-metadata.js +73 -0
  78. package/dist/heart/daemon/runtime-mode.js +67 -0
  79. package/dist/heart/daemon/safe-mode.js +161 -0
  80. package/dist/heart/daemon/sense-manager.js +145 -32
  81. package/dist/heart/daemon/session-id-resolver.js +131 -0
  82. package/dist/heart/daemon/skill-management-installer.js +94 -0
  83. package/dist/heart/daemon/socket-client.js +307 -0
  84. package/dist/heart/daemon/stale-bundle-prune.js +96 -0
  85. package/dist/heart/daemon/startup-tui.js +259 -0
  86. package/dist/heart/daemon/task-scheduler.js +3 -25
  87. package/dist/heart/daemon/thoughts.js +510 -0
  88. package/dist/heart/daemon/up-progress.js +218 -0
  89. package/dist/heart/delegation.js +62 -0
  90. package/dist/heart/habits/habit-migration.js +181 -0
  91. package/dist/heart/habits/habit-parser.js +140 -0
  92. package/dist/heart/habits/habit-scheduler.js +371 -0
  93. package/dist/heart/{daemon → hatch}/hatch-flow.js +53 -117
  94. package/dist/heart/{daemon → hatch}/hatch-specialist.js +3 -3
  95. package/dist/heart/{daemon → hatch}/specialist-prompt.js +12 -9
  96. package/dist/heart/{daemon → hatch}/specialist-tools.js +35 -12
  97. package/dist/heart/identity.js +161 -65
  98. package/dist/heart/kept-notes.js +357 -0
  99. package/dist/heart/kicks.js +1 -1
  100. package/dist/heart/machine-identity.js +161 -0
  101. package/dist/heart/mcp/mcp-server.js +653 -0
  102. package/dist/heart/migrate-config.js +100 -0
  103. package/dist/heart/model-capabilities.js +59 -0
  104. package/dist/heart/outlook/outlook-http-hooks.js +64 -0
  105. package/dist/heart/outlook/outlook-http-response.js +7 -0
  106. package/dist/heart/outlook/outlook-http-routes.js +232 -0
  107. package/dist/heart/outlook/outlook-http-static.js +99 -0
  108. package/dist/heart/outlook/outlook-http-transport.js +116 -0
  109. package/dist/heart/outlook/outlook-http.js +99 -0
  110. package/dist/heart/outlook/outlook-read.js +28 -0
  111. package/dist/heart/outlook/outlook-types.js +27 -0
  112. package/dist/heart/outlook/outlook-view.js +195 -0
  113. package/dist/heart/outlook/readers/agent-machine.js +359 -0
  114. package/dist/heart/outlook/readers/continuity-readers.js +332 -0
  115. package/dist/heart/outlook/readers/runtime-readers.js +660 -0
  116. package/dist/heart/outlook/readers/sessions.js +232 -0
  117. package/dist/heart/outlook/readers/shared.js +111 -0
  118. package/dist/heart/platform.js +81 -0
  119. package/dist/heart/progress-story.js +42 -0
  120. package/dist/heart/provider-attempt.js +133 -0
  121. package/dist/heart/provider-binding-resolver.js +239 -0
  122. package/dist/heart/provider-credentials.js +389 -0
  123. package/dist/heart/provider-failover.js +266 -0
  124. package/dist/heart/provider-models.js +81 -0
  125. package/dist/heart/provider-ping.js +237 -0
  126. package/dist/heart/provider-state.js +216 -0
  127. package/dist/heart/provider-visibility.js +186 -0
  128. package/dist/heart/providers/anthropic-token.js +131 -0
  129. package/dist/heart/providers/anthropic.js +193 -55
  130. package/dist/heart/providers/azure.js +103 -12
  131. package/dist/heart/providers/error-classification.js +63 -0
  132. package/dist/heart/providers/github-copilot.js +145 -0
  133. package/dist/heart/providers/minimax-vlm.js +189 -0
  134. package/dist/heart/providers/minimax.js +29 -7
  135. package/dist/heart/providers/openai-codex.js +62 -38
  136. package/dist/heart/runtime-credentials.js +260 -0
  137. package/dist/heart/sense-truth.js +3 -0
  138. package/dist/heart/session-activity.js +190 -0
  139. package/dist/heart/session-events.js +855 -0
  140. package/dist/heart/session-transcript.js +167 -0
  141. package/dist/heart/start-of-turn-packet.js +345 -0
  142. package/dist/heart/streaming.js +36 -27
  143. package/dist/heart/sync.js +332 -0
  144. package/dist/heart/target-resolution.js +127 -0
  145. package/dist/heart/tempo.js +93 -0
  146. package/dist/heart/temporal-view.js +41 -0
  147. package/dist/heart/tool-activity-callbacks.js +36 -0
  148. package/dist/heart/tool-description.js +135 -0
  149. package/dist/heart/tool-friction.js +55 -0
  150. package/dist/heart/tool-loop.js +200 -0
  151. package/dist/heart/turn-context.js +351 -0
  152. package/dist/heart/turn-coordinator.js +28 -0
  153. package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +1 -1
  154. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
  155. package/dist/heart/versioning/ouro-path-installer.js +301 -0
  156. package/dist/heart/versioning/ouro-version-manager.js +295 -0
  157. package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
  158. package/dist/heart/{daemon → versioning}/update-checker.js +3 -1
  159. package/dist/heart/{daemon → versioning}/update-hooks.js +63 -59
  160. package/dist/mind/bundle-manifest.js +7 -1
  161. package/dist/mind/context.js +134 -87
  162. package/dist/mind/diary-integrity.js +60 -0
  163. package/dist/mind/{memory.js → diary.js} +74 -93
  164. package/dist/mind/embedding-provider.js +60 -0
  165. package/dist/mind/file-state.js +179 -0
  166. package/dist/mind/first-impressions.js +14 -1
  167. package/dist/mind/friends/channel.js +21 -0
  168. package/dist/mind/friends/group-context.js +144 -0
  169. package/dist/mind/friends/resolver.js +38 -1
  170. package/dist/mind/friends/store-file.js +39 -3
  171. package/dist/mind/friends/trust-explanation.js +74 -0
  172. package/dist/mind/friends/types.js +1 -1
  173. package/dist/mind/journal-index.js +161 -0
  174. package/dist/mind/note-search.js +268 -0
  175. package/dist/mind/obligation-steering.js +221 -0
  176. package/dist/mind/pending.js +66 -7
  177. package/dist/mind/prompt-refresh.js +3 -2
  178. package/dist/mind/prompt.js +948 -168
  179. package/dist/mind/provenance-trust.js +26 -0
  180. package/dist/mind/scrutiny.js +173 -0
  181. package/dist/nerves/cli-logging.js +7 -1
  182. package/dist/nerves/coverage/audit-rules.js +15 -6
  183. package/dist/nerves/coverage/audit.js +28 -2
  184. package/dist/nerves/coverage/cli.js +1 -1
  185. package/dist/nerves/coverage/contract.js +5 -5
  186. package/dist/nerves/coverage/file-completeness.js +83 -5
  187. package/dist/nerves/coverage/run-artifacts.js +1 -1
  188. package/dist/nerves/event-buffer.js +111 -0
  189. package/dist/nerves/index.js +224 -4
  190. package/dist/nerves/observation.js +20 -0
  191. package/dist/nerves/redact.js +79 -0
  192. package/dist/nerves/runtime.js +5 -1
  193. package/dist/outlook-ui/assets/index-BAcU08c-.css +1 -0
  194. package/dist/outlook-ui/assets/index-D7l3l4vY.js +61 -0
  195. package/dist/outlook-ui/index.html +15 -0
  196. package/dist/repertoire/ado-client.js +15 -56
  197. package/dist/repertoire/ado-semantic.js +11 -10
  198. package/dist/repertoire/api-client.js +97 -0
  199. package/dist/repertoire/bitwarden-store.js +702 -0
  200. package/dist/repertoire/bundle-templates.js +72 -0
  201. package/dist/repertoire/bw-installer.js +79 -0
  202. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  203. package/dist/repertoire/coding/context-pack.js +330 -0
  204. package/dist/repertoire/coding/feedback.js +197 -30
  205. package/dist/repertoire/coding/manager.js +158 -9
  206. package/dist/repertoire/coding/spawner.js +55 -9
  207. package/dist/repertoire/coding/tools.js +170 -7
  208. package/dist/repertoire/commerce-errors.js +109 -0
  209. package/dist/repertoire/commerce-self-test.js +156 -0
  210. package/dist/repertoire/credential-access.js +111 -0
  211. package/dist/repertoire/duffel-client.js +185 -0
  212. package/dist/repertoire/github-client.js +14 -55
  213. package/dist/repertoire/graph-client.js +11 -52
  214. package/dist/repertoire/guardrails.js +371 -0
  215. package/dist/repertoire/mcp-client.js +255 -0
  216. package/dist/repertoire/mcp-manager.js +305 -0
  217. package/dist/repertoire/mcp-tools.js +63 -0
  218. package/dist/repertoire/shell-sessions.js +133 -0
  219. package/dist/repertoire/skills.js +15 -24
  220. package/dist/repertoire/stripe-client.js +131 -0
  221. package/dist/repertoire/tasks/board.js +43 -5
  222. package/dist/repertoire/tasks/fix.js +182 -0
  223. package/dist/repertoire/tasks/index.js +26 -1
  224. package/dist/repertoire/tasks/lifecycle.js +2 -2
  225. package/dist/repertoire/tasks/parser.js +3 -2
  226. package/dist/repertoire/tasks/scanner.js +194 -37
  227. package/dist/repertoire/tasks/transitions.js +16 -78
  228. package/dist/repertoire/tool-results.js +29 -0
  229. package/dist/repertoire/tools-attachments.js +317 -0
  230. package/dist/repertoire/tools-base.js +42 -687
  231. package/dist/repertoire/tools-bluebubbles.js +1 -0
  232. package/dist/repertoire/tools-bridge.js +141 -0
  233. package/dist/repertoire/tools-bundle.js +984 -0
  234. package/dist/repertoire/tools-config.js +185 -0
  235. package/dist/repertoire/tools-continuity.js +248 -0
  236. package/dist/repertoire/tools-credential.js +361 -0
  237. package/dist/repertoire/tools-files.js +342 -0
  238. package/dist/repertoire/tools-flight.js +224 -0
  239. package/dist/repertoire/tools-flow.js +105 -0
  240. package/dist/repertoire/tools-github.js +1 -7
  241. package/dist/repertoire/tools-notes.js +376 -0
  242. package/dist/repertoire/tools-session.js +739 -0
  243. package/dist/repertoire/tools-shell.js +120 -0
  244. package/dist/repertoire/tools-stripe.js +180 -0
  245. package/dist/repertoire/tools-surface.js +243 -0
  246. package/dist/repertoire/tools-teams.js +9 -39
  247. package/dist/repertoire/tools-travel.js +125 -0
  248. package/dist/repertoire/tools-user-profile.js +144 -0
  249. package/dist/repertoire/tools-vault.js +40 -0
  250. package/dist/repertoire/tools.js +144 -113
  251. package/dist/repertoire/travel-api-client.js +360 -0
  252. package/dist/repertoire/user-profile.js +131 -0
  253. package/dist/repertoire/vault-setup.js +246 -0
  254. package/dist/repertoire/vault-unlock.js +421 -0
  255. package/dist/scripts/claude-code-hook.js +41 -0
  256. package/dist/scripts/claude-code-stop-hook.js +47 -0
  257. package/dist/senses/attention-queue.js +116 -0
  258. package/dist/senses/bluebubbles/attachment-cache.js +53 -0
  259. package/dist/senses/bluebubbles/attachment-download.js +137 -0
  260. package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +260 -9
  261. package/dist/senses/bluebubbles/entry.js +73 -0
  262. package/dist/senses/bluebubbles/inbound-log.js +113 -0
  263. package/dist/senses/bluebubbles/index.js +1620 -0
  264. package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -70
  265. package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +33 -12
  266. package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +45 -3
  267. package/dist/senses/bluebubbles/replay.js +129 -0
  268. package/dist/senses/bluebubbles/runtime-state.js +109 -0
  269. package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
  270. package/dist/senses/cli/bracketed-paste.js +82 -0
  271. package/dist/senses/cli/image-paste.js +287 -0
  272. package/dist/senses/cli/image-ref-navigation.js +75 -0
  273. package/dist/senses/cli/ink-app.js +156 -0
  274. package/dist/senses/cli/inline-diff.js +64 -0
  275. package/dist/senses/cli/input-keys.js +174 -0
  276. package/dist/senses/cli/kill-ring.js +86 -0
  277. package/dist/senses/cli/message-list.js +51 -0
  278. package/dist/senses/cli/ouro-tui.js +605 -0
  279. package/dist/senses/cli/spinner-imperative.js +135 -0
  280. package/dist/senses/cli/spinner.js +101 -0
  281. package/dist/senses/cli/status-line.js +60 -0
  282. package/dist/senses/cli/streaming-markdown.js +526 -0
  283. package/dist/senses/cli/tool-display.js +83 -0
  284. package/dist/senses/cli/tool-render.js +85 -0
  285. package/dist/senses/cli/tui-store.js +240 -0
  286. package/dist/senses/cli/virtual-list.js +35 -0
  287. package/dist/senses/cli-entry.js +60 -8
  288. package/dist/senses/cli-layout.js +187 -0
  289. package/dist/senses/cli.js +526 -211
  290. package/dist/senses/commands.js +66 -3
  291. package/dist/senses/continuity.js +94 -0
  292. package/dist/senses/habit-turn-message.js +108 -0
  293. package/dist/senses/inner-dialog-worker.js +112 -19
  294. package/dist/senses/inner-dialog.js +596 -94
  295. package/dist/senses/pipeline.js +539 -61
  296. package/dist/senses/proactive-content-guard.js +51 -0
  297. package/dist/senses/shared-turn.js +205 -0
  298. package/dist/senses/surface-tool.js +68 -0
  299. package/dist/senses/teams-entry.js +60 -8
  300. package/dist/senses/teams.js +569 -237
  301. package/dist/senses/trust-gate.js +5 -5
  302. package/package.json +29 -7
  303. package/skills/agent-commerce.md +106 -0
  304. package/skills/browser-navigation.md +117 -0
  305. package/skills/commerce-setup-guide.md +116 -0
  306. package/skills/commerce-setup.md +84 -0
  307. package/skills/configure-dev-tools.md +101 -0
  308. package/skills/travel-planning.md +138 -0
  309. package/dist/heart/daemon/ouro-path-installer.js +0 -178
  310. package/dist/heart/daemon/subagent-installer.js +0 -134
  311. package/dist/mind/associative-recall.js +0 -209
  312. package/dist/senses/bluebubbles-entry.js +0 -11
  313. package/dist/senses/bluebubbles.js +0 -832
  314. package/dist/senses/debug-activity.js +0 -127
  315. package/subagents/README.md +0 -60
  316. package/subagents/work-doer.md +0 -235
  317. package/subagents/work-merger.md +0 -618
  318. package/subagents/work-planner.md +0 -382
  319. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  320. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  321. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  322. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  323. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  324. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  325. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  326. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  327. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  328. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  329. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  330. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  331. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  332. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  333. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
  334. /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
@@ -2,19 +2,106 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ensureCurrentDaemonRuntime = ensureCurrentDaemonRuntime;
4
4
  const runtime_1 = require("../../nerves/runtime");
5
+ /* v8 ignore start -- daemon liveness poll: real socket timing untestable in vitest @preserve */
6
+ async function verifyDaemonStarted(deps) {
7
+ if (!deps.checkSocketAlive)
8
+ return true;
9
+ const maxWaitMs = 10_000;
10
+ const pollIntervalMs = 500;
11
+ const deadline = Date.now() + maxWaitMs;
12
+ while (Date.now() < deadline) {
13
+ await new Promise((r) => setTimeout(r, pollIntervalMs));
14
+ if (await deps.checkSocketAlive(deps.socketPath))
15
+ return true;
16
+ }
17
+ return false;
18
+ }
19
+ /* v8 ignore stop */
5
20
  function isKnownVersion(version) {
6
21
  return version !== "unknown" && version.trim().length > 0;
7
22
  }
23
+ function isKnownRuntimeValue(value) {
24
+ return typeof value === "string" && value.trim().length > 0 && value !== "unknown";
25
+ }
8
26
  function formatErrorReason(error) {
9
27
  return error instanceof Error ? error.message : String(error);
10
28
  }
29
+ function normalizeRuntimeIdentity(value) {
30
+ return {
31
+ version: typeof value.version === "string" ? value.version : "unknown",
32
+ lastUpdated: typeof value.lastUpdated === "string" ? value.lastUpdated : "unknown",
33
+ repoRoot: typeof value.repoRoot === "string" ? value.repoRoot : "unknown",
34
+ configFingerprint: typeof value.configFingerprint === "string" ? value.configFingerprint : "unknown",
35
+ managedAgents: typeof value.managedAgents === "string" ? value.managedAgents : "unknown",
36
+ };
37
+ }
38
+ async function readRunningRuntimeIdentity(deps) {
39
+ if (!deps.fetchRunningRuntimeMetadata) {
40
+ return normalizeRuntimeIdentity({
41
+ version: await deps.fetchRunningVersion(),
42
+ });
43
+ }
44
+ const metadata = normalizeRuntimeIdentity(await deps.fetchRunningRuntimeMetadata());
45
+ if (isKnownVersion(metadata.version))
46
+ return metadata;
47
+ return normalizeRuntimeIdentity({
48
+ ...metadata,
49
+ version: await deps.fetchRunningVersion(),
50
+ });
51
+ }
52
+ function collectRuntimeDriftReasons(local, running) {
53
+ const reasons = [];
54
+ const comparableVersions = isKnownVersion(local.version) && isKnownVersion(running.version);
55
+ if (comparableVersions && local.version !== running.version) {
56
+ reasons.push({ key: "version", label: "version", local: local.version, running: running.version });
57
+ }
58
+ if (comparableVersions && isKnownRuntimeValue(local.lastUpdated) && isKnownRuntimeValue(running.lastUpdated) && local.lastUpdated !== running.lastUpdated) {
59
+ reasons.push({ key: "lastUpdated", label: "last updated", local: local.lastUpdated, running: running.lastUpdated });
60
+ }
61
+ if (isKnownRuntimeValue(local.repoRoot) && isKnownRuntimeValue(running.repoRoot) && local.repoRoot !== running.repoRoot) {
62
+ reasons.push({ key: "repoRoot", label: "code path", local: local.repoRoot, running: running.repoRoot });
63
+ }
64
+ if (isKnownRuntimeValue(local.configFingerprint)
65
+ && isKnownRuntimeValue(running.configFingerprint)
66
+ && local.configFingerprint !== running.configFingerprint) {
67
+ reasons.push({
68
+ key: "configFingerprint",
69
+ label: "config fingerprint",
70
+ local: local.configFingerprint,
71
+ running: running.configFingerprint,
72
+ });
73
+ }
74
+ if (isKnownRuntimeValue(local.managedAgents)
75
+ && isKnownRuntimeValue(running.managedAgents)
76
+ && local.managedAgents !== running.managedAgents) {
77
+ reasons.push({
78
+ key: "managedAgents",
79
+ label: "managed agents",
80
+ local: local.managedAgents,
81
+ running: running.managedAgents,
82
+ });
83
+ }
84
+ return reasons;
85
+ }
86
+ function formatRuntimeDriftPublicSummary(reasons) {
87
+ return reasons.map((reason) => reason.label).join(", ");
88
+ }
11
89
  async function ensureCurrentDaemonRuntime(deps) {
90
+ const localRuntime = normalizeRuntimeIdentity({
91
+ version: deps.localVersion,
92
+ lastUpdated: deps.localLastUpdated,
93
+ repoRoot: deps.localRepoRoot,
94
+ configFingerprint: deps.localConfigFingerprint,
95
+ managedAgents: deps.localManagedAgents,
96
+ });
12
97
  try {
13
- const runningVersion = await deps.fetchRunningVersion();
98
+ const runningRuntime = await readRunningRuntimeIdentity(deps);
99
+ const runningVersion = runningRuntime.version;
100
+ const driftReasons = collectRuntimeDriftReasons(localRuntime, runningRuntime);
14
101
  let result;
15
- if (isKnownVersion(deps.localVersion) &&
16
- isKnownVersion(runningVersion) &&
17
- runningVersion !== deps.localVersion) {
102
+ if (driftReasons.length > 0) {
103
+ const includesVersionDrift = driftReasons.some((entry) => entry.key === "version");
104
+ const publicDriftSummary = formatRuntimeDriftPublicSummary(driftReasons);
18
105
  try {
19
106
  await deps.stopDaemon();
20
107
  }
@@ -22,32 +109,72 @@ async function ensureCurrentDaemonRuntime(deps) {
22
109
  const reason = formatErrorReason(error);
23
110
  result = {
24
111
  alreadyRunning: true,
25
- message: `daemon already running (${deps.socketPath}; could not replace stale daemon ${runningVersion} -> ${deps.localVersion}: ${reason})`,
112
+ message: includesVersionDrift
113
+ ? `daemon already running (${deps.socketPath}; could not replace stale daemon ${runningVersion} -> ${deps.localVersion}: ${reason})`
114
+ : `daemon already running (${deps.socketPath}; could not replace runtime drift ${publicDriftSummary}: ${reason})`,
26
115
  };
27
116
  (0, runtime_1.emitNervesEvent)({
28
117
  level: "warn",
29
118
  component: "daemon",
30
119
  event: "daemon.runtime_sync_decision",
31
120
  message: "evaluated daemon runtime sync outcome",
32
- meta: { socketPath: deps.socketPath, localVersion: deps.localVersion, runningVersion, action: "stale_replace_failed", reason },
121
+ meta: {
122
+ socketPath: deps.socketPath,
123
+ localVersion: deps.localVersion,
124
+ localLastUpdated: localRuntime.lastUpdated,
125
+ localRepoRoot: localRuntime.repoRoot,
126
+ localConfigFingerprint: localRuntime.configFingerprint,
127
+ localManagedAgents: localRuntime.managedAgents,
128
+ runningVersion,
129
+ runningLastUpdated: runningRuntime.lastUpdated,
130
+ runningRepoRoot: runningRuntime.repoRoot,
131
+ runningConfigFingerprint: runningRuntime.configFingerprint,
132
+ runningManagedAgents: runningRuntime.managedAgents,
133
+ action: "stale_replace_failed",
134
+ driftKeys: driftReasons.map((entry) => entry.key),
135
+ reason,
136
+ },
33
137
  });
34
138
  return result;
35
139
  }
36
140
  deps.cleanupStaleSocket(deps.socketPath);
37
141
  const started = await deps.startDaemonProcess(deps.socketPath);
142
+ const pid = started.pid ?? "unknown";
143
+ const verified = await verifyDaemonStarted(deps);
144
+ /* v8 ignore next -- daemon liveness failure: requires real daemon crash timing @preserve */
145
+ const suffix = verified ? "" : "\ndaemon restart has not answered yet; check logs with `ouro logs` or run `ouro doctor`.";
38
146
  result = {
39
147
  alreadyRunning: false,
40
- message: `restarted stale daemon from ${runningVersion} to ${deps.localVersion} (pid ${started.pid ?? "unknown"})`,
148
+ message: includesVersionDrift
149
+ ? `restarted stale daemon ${runningVersion} -> ${deps.localVersion} (pid ${pid})${suffix}`
150
+ : `restarted daemon after runtime drift: ${publicDriftSummary} (pid ${pid})${suffix}`,
151
+ verifyStartupStatus: verified,
152
+ startedPid: started.pid ?? null,
41
153
  };
42
154
  (0, runtime_1.emitNervesEvent)({
43
155
  component: "daemon",
44
156
  event: "daemon.runtime_sync_decision",
45
157
  message: "evaluated daemon runtime sync outcome",
46
- meta: { socketPath: deps.socketPath, localVersion: deps.localVersion, runningVersion, action: "stale_restarted", pid: started.pid ?? null },
158
+ meta: {
159
+ socketPath: deps.socketPath,
160
+ localVersion: deps.localVersion,
161
+ localLastUpdated: localRuntime.lastUpdated,
162
+ localRepoRoot: localRuntime.repoRoot,
163
+ localConfigFingerprint: localRuntime.configFingerprint,
164
+ localManagedAgents: localRuntime.managedAgents,
165
+ runningVersion,
166
+ runningLastUpdated: runningRuntime.lastUpdated,
167
+ runningRepoRoot: runningRuntime.repoRoot,
168
+ runningConfigFingerprint: runningRuntime.configFingerprint,
169
+ runningManagedAgents: runningRuntime.managedAgents,
170
+ action: "stale_restarted",
171
+ driftKeys: driftReasons.map((entry) => entry.key),
172
+ pid: started.pid ?? null,
173
+ },
47
174
  });
48
175
  return result;
49
176
  }
50
- if (!isKnownVersion(deps.localVersion) || !isKnownVersion(runningVersion)) {
177
+ if (!isKnownVersion(localRuntime.version) || !isKnownVersion(runningVersion)) {
51
178
  result = {
52
179
  alreadyRunning: true,
53
180
  message: `daemon already running (${deps.socketPath}; unable to verify version)`,
@@ -56,7 +183,20 @@ async function ensureCurrentDaemonRuntime(deps) {
56
183
  component: "daemon",
57
184
  event: "daemon.runtime_sync_decision",
58
185
  message: "evaluated daemon runtime sync outcome",
59
- meta: { socketPath: deps.socketPath, localVersion: deps.localVersion, runningVersion, action: "unknown_version" },
186
+ meta: {
187
+ socketPath: deps.socketPath,
188
+ localVersion: deps.localVersion,
189
+ localLastUpdated: localRuntime.lastUpdated,
190
+ localRepoRoot: localRuntime.repoRoot,
191
+ localConfigFingerprint: localRuntime.configFingerprint,
192
+ localManagedAgents: localRuntime.managedAgents,
193
+ runningVersion,
194
+ runningLastUpdated: runningRuntime.lastUpdated,
195
+ runningRepoRoot: runningRuntime.repoRoot,
196
+ runningConfigFingerprint: runningRuntime.configFingerprint,
197
+ runningManagedAgents: runningRuntime.managedAgents,
198
+ action: "unknown_version",
199
+ },
60
200
  });
61
201
  return result;
62
202
  }
@@ -72,19 +212,38 @@ async function ensureCurrentDaemonRuntime(deps) {
72
212
  component: "daemon",
73
213
  event: "daemon.runtime_sync_decision",
74
214
  message: "evaluated daemon runtime sync outcome",
75
- meta: { socketPath: deps.socketPath, localVersion: deps.localVersion, action: "status_lookup_failed", reason },
215
+ meta: {
216
+ socketPath: deps.socketPath,
217
+ localVersion: deps.localVersion,
218
+ localLastUpdated: localRuntime.lastUpdated,
219
+ localRepoRoot: localRuntime.repoRoot,
220
+ localConfigFingerprint: localRuntime.configFingerprint,
221
+ localManagedAgents: localRuntime.managedAgents,
222
+ action: "status_lookup_failed",
223
+ reason,
224
+ },
76
225
  });
77
226
  return result;
78
227
  }
79
228
  const result = {
80
229
  alreadyRunning: true,
81
230
  message: `daemon already running (${deps.socketPath})`,
231
+ verifyStartupStatus: true,
232
+ startedPid: null,
82
233
  };
83
234
  (0, runtime_1.emitNervesEvent)({
84
235
  component: "daemon",
85
236
  event: "daemon.runtime_sync_decision",
86
237
  message: "evaluated daemon runtime sync outcome",
87
- meta: { socketPath: deps.socketPath, localVersion: deps.localVersion, action: "already_current" },
238
+ meta: {
239
+ socketPath: deps.socketPath,
240
+ localVersion: deps.localVersion,
241
+ localLastUpdated: localRuntime.lastUpdated,
242
+ localRepoRoot: localRuntime.repoRoot,
243
+ localConfigFingerprint: localRuntime.configFingerprint,
244
+ localManagedAgents: localRuntime.managedAgents,
245
+ action: "already_current",
246
+ },
88
247
  });
89
248
  return result;
90
249
  }
@@ -0,0 +1,236 @@
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.RECENT_CRASHES_MAX = void 0;
37
+ exports.getTombstonePath = getTombstonePath;
38
+ exports.setTombstonePath = setTombstonePath;
39
+ exports.captureDeathForensics = captureDeathForensics;
40
+ exports.writeDaemonTombstone = writeDaemonTombstone;
41
+ exports.readDaemonTombstone = readDaemonTombstone;
42
+ const child_process_1 = require("child_process");
43
+ const fs = __importStar(require("fs"));
44
+ const os = __importStar(require("os"));
45
+ const path = __importStar(require("path"));
46
+ const runtime_1 = require("../../nerves/runtime");
47
+ /** Maximum number of historical crash timestamps to retain in the tombstone. */
48
+ exports.RECENT_CRASHES_MAX = 100;
49
+ let _tombstonePath = null;
50
+ function getTombstonePath() {
51
+ if (!_tombstonePath) {
52
+ _tombstonePath = path.join(os.homedir(), ".ouro-cli", "daemon-death.json");
53
+ }
54
+ return _tombstonePath;
55
+ }
56
+ /** Overrides the tombstone path for testing. Pass null to reset. */
57
+ function setTombstonePath(p) {
58
+ _tombstonePath = p;
59
+ }
60
+ /* v8 ignore start -- shells out to ps; covered via injected runPs in unit tests @preserve */
61
+ function defaultRunPs(args) {
62
+ try {
63
+ return (0, child_process_1.execSync)(`ps ${args.join(" ")}`, { encoding: "utf-8", timeout: 2000 });
64
+ }
65
+ catch {
66
+ return null;
67
+ }
68
+ }
69
+ /* v8 ignore stop */
70
+ const DEFAULT_FORENSICS_DEPS = {
71
+ /* v8 ignore next -- defensive: process.ppid always exists in node @preserve */
72
+ ppid: () => (typeof process !== "undefined" && typeof process.ppid === "number" ? process.ppid : 0),
73
+ runPs: defaultRunPs,
74
+ };
75
+ /**
76
+ * Capture forensic data about who is likely to have killed the daemon.
77
+ * Synchronous and best-effort: never throws, always returns a structured
78
+ * record (with nulls if anything failed). Called from inside SIGTERM/SIGINT
79
+ * handlers, so it must be fast and have no async dependencies.
80
+ */
81
+ function captureDeathForensics(deps = DEFAULT_FORENSICS_DEPS) {
82
+ const parentPid = (() => {
83
+ try {
84
+ const p = deps.ppid();
85
+ return typeof p === "number" && p > 0 ? p : null;
86
+ }
87
+ catch {
88
+ return null;
89
+ }
90
+ })();
91
+ let parentCommand = null;
92
+ if (parentPid !== null) {
93
+ const psParent = deps.runPs(["-p", String(parentPid), "-o", "command="]);
94
+ if (psParent !== null) {
95
+ const trimmed = psParent.trim();
96
+ parentCommand = trimmed.length > 0 ? trimmed : null;
97
+ }
98
+ }
99
+ const processSnapshot = (() => {
100
+ const psAll = deps.runPs(["-eo", "pid,ppid,command"]);
101
+ if (psAll === null)
102
+ return null;
103
+ const lines = psAll.split("\n");
104
+ // Filter for relevant processes only — node, vitest, ouro, kill commands.
105
+ // Keeps the snapshot small and human-scannable.
106
+ const relevant = lines.filter((line) => {
107
+ const lower = line.toLowerCase();
108
+ return (lower.includes("node")
109
+ || lower.includes("vitest")
110
+ || lower.includes("ouro")
111
+ || lower.includes("/kill ")
112
+ || lower.includes("pkill")
113
+ || lower.includes("killall"));
114
+ });
115
+ if (relevant.length === 0)
116
+ return null;
117
+ return relevant.join("\n");
118
+ })();
119
+ const killerHint = deriveKillerHint(parentCommand, processSnapshot);
120
+ return { parentPid, parentCommand, processSnapshot, killerHint };
121
+ }
122
+ function deriveKillerHint(parentCommand, snapshot) {
123
+ if (parentCommand !== null && parentCommand.toLowerCase().includes("launchd")) {
124
+ return "process was reparented to launchd — likely killed by launchctl bootout, KeepAlive thrash, or RSS pressure";
125
+ }
126
+ if (snapshot !== null) {
127
+ const lower = snapshot.toLowerCase();
128
+ if (lower.includes("vitest")) {
129
+ return "vitest worker is running — possible test cleanup killing detached processes";
130
+ }
131
+ if (lower.includes("pkill") || lower.includes("killall")) {
132
+ return "saw pkill/killall in process list — explicit kill command";
133
+ }
134
+ }
135
+ return null;
136
+ }
137
+ function writeDaemonTombstone(reason, error, forensicsDeps) {
138
+ const now = new Date().toISOString();
139
+ const filePath = getTombstonePath();
140
+ // Read existing recentCrashes from previous tombstone (best-effort)
141
+ let existingCrashes = [];
142
+ try {
143
+ const raw = fs.readFileSync(filePath, "utf-8");
144
+ const existing = JSON.parse(raw);
145
+ if (Array.isArray(existing.recentCrashes)) {
146
+ existingCrashes = existing.recentCrashes.filter((entry) => typeof entry === "string");
147
+ }
148
+ }
149
+ catch {
150
+ // No existing tombstone or unreadable — start fresh
151
+ }
152
+ // Append the new crash and cap at the most recent RECENT_CRASHES_MAX entries
153
+ // so the tombstone doesn't grow without bound (we saw it hit 12,265 entries
154
+ // after a March 31 crash-restart thrash loop).
155
+ const recentCrashes = [...existingCrashes, now].slice(-exports.RECENT_CRASHES_MAX);
156
+ // Forensics: only meaningful for signal-driven deaths (sigterm/sigint).
157
+ // For uncaughtException etc. we skip the snapshot to keep tombstone writes
158
+ // fast on the unhappy path.
159
+ const shouldCaptureForensics = reason === "sigterm" || reason === "sigint";
160
+ const forensics = shouldCaptureForensics
161
+ ? captureDeathForensics(forensicsDeps)
162
+ : undefined;
163
+ const tombstone = {
164
+ reason,
165
+ message: error?.message ?? reason,
166
+ stack: error?.stack ?? null,
167
+ timestamp: now,
168
+ pid: process.pid,
169
+ uptimeSeconds: Math.floor(process.uptime()),
170
+ recentCrashes,
171
+ ...(forensics ? { forensics } : {}),
172
+ };
173
+ try {
174
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
175
+ fs.writeFileSync(filePath, JSON.stringify(tombstone, null, 2) + "\n", "utf-8");
176
+ }
177
+ catch {
178
+ // Best-effort: if we can't write, we're already dying.
179
+ }
180
+ (0, runtime_1.emitNervesEvent)({
181
+ level: "error",
182
+ component: "daemon",
183
+ event: "daemon.tombstone_written",
184
+ message: `daemon tombstone written: ${reason}`,
185
+ meta: {
186
+ reason,
187
+ errorMessage: error?.message ?? null,
188
+ filePath,
189
+ parentPid: forensics?.parentPid ?? null,
190
+ killerHint: forensics?.killerHint ?? null,
191
+ },
192
+ });
193
+ }
194
+ function readDaemonTombstone() {
195
+ const filePath = getTombstonePath();
196
+ try {
197
+ const raw = fs.readFileSync(filePath, "utf-8");
198
+ const parsed = JSON.parse(raw);
199
+ if (typeof parsed.reason !== "string" || typeof parsed.timestamp !== "string") {
200
+ return null;
201
+ }
202
+ (0, runtime_1.emitNervesEvent)({
203
+ component: "daemon",
204
+ event: "daemon.tombstone_read",
205
+ message: "read daemon tombstone",
206
+ meta: { filePath },
207
+ });
208
+ const forensics = parseForensics(parsed.forensics);
209
+ return {
210
+ reason: parsed.reason,
211
+ message: typeof parsed.message === "string" ? parsed.message : String(parsed.reason),
212
+ stack: typeof parsed.stack === "string" ? parsed.stack : null,
213
+ timestamp: parsed.timestamp,
214
+ pid: typeof parsed.pid === "number" ? parsed.pid : 0,
215
+ uptimeSeconds: typeof parsed.uptimeSeconds === "number" ? parsed.uptimeSeconds : 0,
216
+ recentCrashes: Array.isArray(parsed.recentCrashes)
217
+ ? parsed.recentCrashes.filter((e) => typeof e === "string")
218
+ : [],
219
+ ...(forensics ? { forensics } : {}),
220
+ };
221
+ }
222
+ catch {
223
+ return null;
224
+ }
225
+ }
226
+ function parseForensics(value) {
227
+ if (value === null || typeof value !== "object")
228
+ return null;
229
+ const v = value;
230
+ return {
231
+ parentPid: typeof v.parentPid === "number" ? v.parentPid : null,
232
+ parentCommand: typeof v.parentCommand === "string" ? v.parentCommand : null,
233
+ processSnapshot: typeof v.processSnapshot === "string" ? v.processSnapshot : null,
234
+ killerHint: typeof v.killerHint === "string" ? v.killerHint : null,
235
+ };
236
+ }