@ouro.bot/cli 0.1.0-alpha.35 → 0.1.0-alpha.350

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 (324) hide show
  1. package/README.md +188 -187
  2. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +3 -2
  3. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +1 -1
  4. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +1 -1
  5. package/changelog.json +2088 -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 +832 -0
  16. package/dist/heart/agent-entry.js +37 -2
  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 +463 -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 +53 -21
  35. package/dist/heart/core.js +744 -252
  36. package/dist/heart/cross-chat-delivery.js +131 -0
  37. package/dist/heart/daemon/agent-config-check.js +561 -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 +170 -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 +591 -0
  44. package/dist/heart/daemon/cli-exec.js +2633 -0
  45. package/dist/heart/daemon/cli-help.js +306 -0
  46. package/dist/heart/daemon/cli-parse.js +913 -0
  47. package/dist/heart/daemon/cli-render-doctor.js +57 -0
  48. package/dist/heart/daemon/cli-render.js +512 -0
  49. package/dist/heart/daemon/cli-types.js +8 -0
  50. package/dist/heart/daemon/daemon-cli.js +30 -1171
  51. package/dist/heart/daemon/daemon-entry.js +358 -3
  52. package/dist/heart/daemon/daemon-health.js +141 -0
  53. package/dist/heart/daemon/daemon-runtime-sync.js +157 -12
  54. package/dist/heart/daemon/daemon-tombstone.js +236 -0
  55. package/dist/heart/daemon/daemon.js +751 -58
  56. package/dist/heart/daemon/doctor-types.js +8 -0
  57. package/dist/heart/daemon/doctor.js +465 -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 +91 -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 +17 -8
  68. package/dist/heart/daemon/os-cron-deps.js +134 -0
  69. package/dist/heart/daemon/ouro-bot-entry.js +1 -1
  70. package/dist/heart/daemon/process-manager.js +201 -0
  71. package/dist/heart/daemon/provider-discovery.js +105 -0
  72. package/dist/heart/daemon/pulse.js +463 -0
  73. package/dist/heart/daemon/run-hooks.js +2 -0
  74. package/dist/heart/daemon/runtime-logging.js +67 -16
  75. package/dist/heart/daemon/runtime-metadata.js +101 -0
  76. package/dist/heart/daemon/runtime-mode.js +67 -0
  77. package/dist/heart/daemon/safe-mode.js +161 -0
  78. package/dist/heart/daemon/sense-manager.js +72 -3
  79. package/dist/heart/daemon/session-id-resolver.js +131 -0
  80. package/dist/heart/daemon/skill-management-installer.js +94 -0
  81. package/dist/heart/daemon/socket-client.js +307 -0
  82. package/dist/heart/daemon/stale-bundle-prune.js +96 -0
  83. package/dist/heart/daemon/startup-tui.js +237 -0
  84. package/dist/heart/daemon/task-scheduler.js +3 -25
  85. package/dist/heart/daemon/thoughts.js +510 -0
  86. package/dist/heart/daemon/up-progress.js +135 -0
  87. package/dist/heart/delegation.js +62 -0
  88. package/dist/heart/habits/habit-migration.js +181 -0
  89. package/dist/heart/habits/habit-parser.js +140 -0
  90. package/dist/heart/habits/habit-scheduler.js +371 -0
  91. package/dist/heart/{daemon → hatch}/hatch-flow.js +52 -120
  92. package/dist/heart/{daemon → hatch}/hatch-specialist.js +3 -3
  93. package/dist/heart/{daemon → hatch}/specialist-prompt.js +10 -7
  94. package/dist/heart/{daemon → hatch}/specialist-tools.js +56 -10
  95. package/dist/heart/identity.js +154 -59
  96. package/dist/heart/kept-notes.js +357 -0
  97. package/dist/heart/kicks.js +2 -20
  98. package/dist/heart/machine-identity.js +161 -0
  99. package/dist/heart/mcp/mcp-server.js +653 -0
  100. package/dist/heart/migrate-config.js +127 -0
  101. package/dist/heart/model-capabilities.js +59 -0
  102. package/dist/heart/outlook/outlook-http-hooks.js +64 -0
  103. package/dist/heart/outlook/outlook-http-response.js +7 -0
  104. package/dist/heart/outlook/outlook-http-routes.js +232 -0
  105. package/dist/heart/outlook/outlook-http-static.js +99 -0
  106. package/dist/heart/outlook/outlook-http-transport.js +116 -0
  107. package/dist/heart/outlook/outlook-http.js +99 -0
  108. package/dist/heart/outlook/outlook-read.js +28 -0
  109. package/dist/heart/outlook/outlook-types.js +27 -0
  110. package/dist/heart/outlook/outlook-view.js +194 -0
  111. package/dist/heart/outlook/readers/agent-machine.js +355 -0
  112. package/dist/heart/outlook/readers/continuity-readers.js +332 -0
  113. package/dist/heart/outlook/readers/runtime-readers.js +660 -0
  114. package/dist/heart/outlook/readers/sessions.js +231 -0
  115. package/dist/heart/outlook/readers/shared.js +111 -0
  116. package/dist/heart/progress-story.js +42 -0
  117. package/dist/heart/provider-attempt.js +133 -0
  118. package/dist/heart/provider-binding-resolver.js +240 -0
  119. package/dist/heart/provider-credential-pool.js +395 -0
  120. package/dist/heart/provider-failover.js +135 -0
  121. package/dist/heart/provider-models.js +81 -0
  122. package/dist/heart/provider-ping.js +258 -0
  123. package/dist/heart/provider-state.js +208 -0
  124. package/dist/heart/providers/anthropic-token.js +163 -0
  125. package/dist/heart/providers/anthropic.js +171 -50
  126. package/dist/heart/providers/azure.js +97 -11
  127. package/dist/heart/providers/error-classification.js +63 -0
  128. package/dist/heart/providers/github-copilot.js +135 -0
  129. package/dist/heart/providers/minimax-vlm.js +189 -0
  130. package/dist/heart/providers/minimax.js +23 -6
  131. package/dist/heart/providers/openai-codex.js +33 -23
  132. package/dist/heart/session-activity.js +190 -0
  133. package/dist/heart/session-events.js +727 -0
  134. package/dist/heart/session-transcript.js +162 -0
  135. package/dist/heart/start-of-turn-packet.js +341 -0
  136. package/dist/heart/streaming.js +36 -27
  137. package/dist/heart/sync.js +332 -0
  138. package/dist/heart/target-resolution.js +127 -0
  139. package/dist/heart/tempo.js +93 -0
  140. package/dist/heart/temporal-view.js +41 -0
  141. package/dist/heart/tool-activity-callbacks.js +36 -0
  142. package/dist/heart/tool-description.js +135 -0
  143. package/dist/heart/tool-friction.js +55 -0
  144. package/dist/heart/tool-loop.js +200 -0
  145. package/dist/heart/turn-context.js +358 -0
  146. package/dist/heart/turn-coordinator.js +28 -0
  147. package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +1 -1
  148. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
  149. package/dist/heart/versioning/ouro-path-installer.js +296 -0
  150. package/dist/heart/versioning/ouro-version-manager.js +295 -0
  151. package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
  152. package/dist/heart/{daemon → versioning}/update-checker.js +12 -2
  153. package/dist/heart/{daemon → versioning}/update-hooks.js +63 -59
  154. package/dist/mind/bundle-manifest.js +7 -1
  155. package/dist/mind/context.js +140 -94
  156. package/dist/mind/diary-integrity.js +60 -0
  157. package/dist/mind/{memory.js → diary.js} +84 -96
  158. package/dist/mind/embedding-provider.js +60 -0
  159. package/dist/mind/file-state.js +179 -0
  160. package/dist/mind/first-impressions.js +14 -1
  161. package/dist/mind/friends/channel.js +56 -0
  162. package/dist/mind/friends/group-context.js +144 -0
  163. package/dist/mind/friends/resolver.js +38 -1
  164. package/dist/mind/friends/store-file.js +58 -3
  165. package/dist/mind/friends/trust-explanation.js +74 -0
  166. package/dist/mind/friends/types.js +9 -1
  167. package/dist/mind/journal-index.js +161 -0
  168. package/dist/mind/note-search.js +268 -0
  169. package/dist/mind/obligation-steering.js +221 -0
  170. package/dist/mind/pending.js +74 -7
  171. package/dist/mind/prompt.js +1000 -112
  172. package/dist/mind/provenance-trust.js +26 -0
  173. package/dist/mind/scrutiny.js +173 -0
  174. package/dist/mind/token-estimate.js +8 -12
  175. package/dist/nerves/cli-logging.js +7 -1
  176. package/dist/nerves/coverage/audit-rules.js +15 -6
  177. package/dist/nerves/coverage/audit.js +28 -2
  178. package/dist/nerves/coverage/cli.js +1 -1
  179. package/dist/nerves/coverage/file-completeness.js +83 -5
  180. package/dist/nerves/coverage/run-artifacts.js +1 -1
  181. package/dist/nerves/event-buffer.js +111 -0
  182. package/dist/nerves/index.js +224 -4
  183. package/dist/nerves/observation.js +20 -0
  184. package/dist/nerves/redact.js +79 -0
  185. package/dist/nerves/runtime.js +5 -1
  186. package/dist/outlook-ui/assets/index-LwChZTgL.css +1 -0
  187. package/dist/outlook-ui/assets/index-xTdv64BV.js +61 -0
  188. package/dist/outlook-ui/index.html +15 -0
  189. package/dist/repertoire/ado-client.js +15 -56
  190. package/dist/repertoire/ado-semantic.js +11 -10
  191. package/dist/repertoire/api-client.js +97 -0
  192. package/dist/repertoire/bitwarden-store.js +319 -0
  193. package/dist/repertoire/bundle-templates.js +72 -0
  194. package/dist/repertoire/bw-installer.js +79 -0
  195. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  196. package/dist/repertoire/coding/context-pack.js +330 -0
  197. package/dist/repertoire/coding/feedback.js +197 -30
  198. package/dist/repertoire/coding/manager.js +158 -9
  199. package/dist/repertoire/coding/spawner.js +55 -9
  200. package/dist/repertoire/coding/tools.js +170 -7
  201. package/dist/repertoire/commerce-errors.js +109 -0
  202. package/dist/repertoire/commerce-self-test.js +156 -0
  203. package/dist/repertoire/credential-access.js +527 -0
  204. package/dist/repertoire/duffel-client.js +185 -0
  205. package/dist/repertoire/github-client.js +14 -55
  206. package/dist/repertoire/graph-client.js +11 -52
  207. package/dist/repertoire/guardrails.js +375 -0
  208. package/dist/repertoire/mcp-client.js +255 -0
  209. package/dist/repertoire/mcp-manager.js +305 -0
  210. package/dist/repertoire/mcp-tools.js +63 -0
  211. package/dist/repertoire/shell-sessions.js +133 -0
  212. package/dist/repertoire/skills.js +15 -24
  213. package/dist/repertoire/stripe-client.js +131 -0
  214. package/dist/repertoire/tasks/board.js +43 -5
  215. package/dist/repertoire/tasks/fix.js +182 -0
  216. package/dist/repertoire/tasks/index.js +28 -10
  217. package/dist/repertoire/tasks/lifecycle.js +2 -2
  218. package/dist/repertoire/tasks/parser.js +3 -2
  219. package/dist/repertoire/tasks/scanner.js +194 -37
  220. package/dist/repertoire/tasks/transitions.js +16 -79
  221. package/dist/repertoire/tool-results.js +29 -0
  222. package/dist/repertoire/tools-attachments.js +316 -0
  223. package/dist/repertoire/tools-base.js +45 -771
  224. package/dist/repertoire/tools-bluebubbles.js +1 -0
  225. package/dist/repertoire/tools-bridge.js +141 -0
  226. package/dist/repertoire/tools-bundle.js +984 -0
  227. package/dist/repertoire/tools-config.js +185 -0
  228. package/dist/repertoire/tools-continuity.js +248 -0
  229. package/dist/repertoire/tools-credential.js +182 -0
  230. package/dist/repertoire/tools-files.js +342 -0
  231. package/dist/repertoire/tools-flight.js +224 -0
  232. package/dist/repertoire/tools-flow.js +105 -0
  233. package/dist/repertoire/tools-github.js +1 -7
  234. package/dist/repertoire/tools-notes.js +376 -0
  235. package/dist/repertoire/tools-session.js +739 -0
  236. package/dist/repertoire/tools-shell.js +120 -0
  237. package/dist/repertoire/tools-stripe.js +180 -0
  238. package/dist/repertoire/tools-surface.js +243 -0
  239. package/dist/repertoire/tools-teams.js +12 -62
  240. package/dist/repertoire/tools-travel.js +125 -0
  241. package/dist/repertoire/tools-user-profile.js +144 -0
  242. package/dist/repertoire/tools-vault.js +110 -0
  243. package/dist/repertoire/tools.js +144 -138
  244. package/dist/repertoire/travel-api-client.js +360 -0
  245. package/dist/repertoire/user-profile.js +118 -0
  246. package/dist/repertoire/vault-setup.js +241 -0
  247. package/dist/scripts/claude-code-hook.js +41 -0
  248. package/dist/scripts/claude-code-stop-hook.js +47 -0
  249. package/dist/senses/attention-queue.js +116 -0
  250. package/dist/senses/bluebubbles/attachment-cache.js +53 -0
  251. package/dist/senses/bluebubbles/attachment-download.js +137 -0
  252. package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +225 -9
  253. package/dist/senses/bluebubbles/entry.js +13 -0
  254. package/dist/senses/bluebubbles/inbound-log.js +113 -0
  255. package/dist/senses/bluebubbles/index.js +1590 -0
  256. package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -70
  257. package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +43 -12
  258. package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +46 -6
  259. package/dist/senses/bluebubbles/replay.js +129 -0
  260. package/dist/senses/bluebubbles/runtime-state.js +109 -0
  261. package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
  262. package/dist/senses/cli/bracketed-paste.js +82 -0
  263. package/dist/senses/cli/image-paste.js +287 -0
  264. package/dist/senses/cli/image-ref-navigation.js +75 -0
  265. package/dist/senses/cli/ink-app.js +156 -0
  266. package/dist/senses/cli/inline-diff.js +64 -0
  267. package/dist/senses/cli/input-keys.js +174 -0
  268. package/dist/senses/cli/kill-ring.js +86 -0
  269. package/dist/senses/cli/message-list.js +51 -0
  270. package/dist/senses/cli/ouro-tui.js +605 -0
  271. package/dist/senses/cli/spinner-imperative.js +135 -0
  272. package/dist/senses/cli/spinner.js +101 -0
  273. package/dist/senses/cli/status-line.js +60 -0
  274. package/dist/senses/cli/streaming-markdown.js +526 -0
  275. package/dist/senses/cli/tool-display.js +83 -0
  276. package/dist/senses/cli/tool-render.js +85 -0
  277. package/dist/senses/cli/tui-store.js +240 -0
  278. package/dist/senses/cli/virtual-list.js +35 -0
  279. package/dist/senses/cli-entry.js +1 -1
  280. package/dist/senses/cli-layout.js +187 -0
  281. package/dist/senses/cli.js +603 -246
  282. package/dist/senses/commands.js +65 -1
  283. package/dist/senses/continuity.js +94 -0
  284. package/dist/senses/habit-turn-message.js +108 -0
  285. package/dist/senses/inner-dialog-worker.js +112 -19
  286. package/dist/senses/inner-dialog.js +633 -86
  287. package/dist/senses/pipeline.js +567 -0
  288. package/dist/senses/shared-turn.js +199 -0
  289. package/dist/senses/surface-tool.js +68 -0
  290. package/dist/senses/teams.js +665 -160
  291. package/dist/senses/trust-gate.js +112 -2
  292. package/package.json +29 -7
  293. package/skills/agent-commerce.md +106 -0
  294. package/skills/browser-navigation.md +110 -0
  295. package/skills/commerce-setup-guide.md +116 -0
  296. package/skills/commerce-setup.md +84 -0
  297. package/skills/configure-dev-tools.md +81 -0
  298. package/skills/travel-planning.md +138 -0
  299. package/dist/heart/daemon/ouro-path-installer.js +0 -178
  300. package/dist/heart/daemon/subagent-installer.js +0 -134
  301. package/dist/mind/associative-recall.js +0 -197
  302. package/dist/senses/bluebubbles-entry.js +0 -11
  303. package/dist/senses/bluebubbles.js +0 -548
  304. package/dist/senses/debug-activity.js +0 -127
  305. package/subagents/README.md +0 -73
  306. package/subagents/work-doer.md +0 -235
  307. package/subagents/work-merger.md +0 -618
  308. package/subagents/work-planner.md +0 -382
  309. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  310. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  311. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  312. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  313. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  314. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  315. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  316. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  317. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  318. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  319. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  320. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  321. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  322. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  323. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
  324. /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.pruneDaemonLogs = pruneDaemonLogs;
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
6
+ const crypto_1 = require("crypto");
7
+ const nerves_1 = require("../../nerves");
8
+ const runtime_1 = require("../../nerves/runtime");
9
+ const identity_1 = require("../identity");
10
+ function pruneDaemonLogs(options = {}) {
11
+ /* v8 ignore next -- defensive: tests always pass logsDir to avoid prod paths @preserve */
12
+ const logsDir = options.logsDir ?? (0, identity_1.getAgentDaemonLogsDir)(options.agentName);
13
+ const maxSizeBytes = options.maxSizeBytes ?? nerves_1.DEFAULT_MAX_LOG_SIZE_BYTES;
14
+ const maxGenerations = options.maxGenerations ?? nerves_1.DEFAULT_MAX_GENERATIONS;
15
+ const traceId = (0, crypto_1.randomUUID)();
16
+ (0, runtime_1.emitNervesEvent)({
17
+ component: "nerves",
18
+ event: "nerves.logs_prune_start",
19
+ trace_id: traceId,
20
+ message: "pruning daemon logs",
21
+ meta: { logsDir, maxSizeBytes, maxGenerations },
22
+ });
23
+ let completed = false;
24
+ try {
25
+ if (!(0, fs_1.existsSync)(logsDir)) {
26
+ completed = true;
27
+ (0, runtime_1.emitNervesEvent)({
28
+ component: "nerves",
29
+ event: "nerves.logs_prune_end",
30
+ trace_id: traceId,
31
+ message: "daemon logs dir does not exist",
32
+ meta: { logsDir, filesCompacted: 0, bytesFreed: 0 },
33
+ });
34
+ return { filesCompacted: 0, bytesFreed: 0 };
35
+ }
36
+ let filesCompacted = 0;
37
+ let bytesFreed = 0;
38
+ // Enumerate and rotate each active .ndjson stream. We explicitly skip
39
+ // .gz and other files — only the active stream can be a rotation
40
+ // candidate. Legacy .N.ndjson uncompressed files are handled inside
41
+ // rotateIfNeeded's generation-shift step, so we skip them here to
42
+ // avoid double-rotating.
43
+ for (const name of (0, fs_1.readdirSync)(logsDir)) {
44
+ if (!name.endsWith(".ndjson"))
45
+ continue;
46
+ // Skip legacy generation files like daemon.1.ndjson; rotateIfNeeded
47
+ // will migrate them on the next rotation of their parent stream.
48
+ if (/\.\d+\.ndjson$/.test(name))
49
+ continue;
50
+ const filePath = (0, path_1.join)(logsDir, name);
51
+ let sizeBefore;
52
+ try {
53
+ sizeBefore = (0, fs_1.statSync)(filePath).size;
54
+ /* v8 ignore start -- defensive: file disappears between readdir and stat @preserve */
55
+ }
56
+ catch {
57
+ continue;
58
+ }
59
+ /* v8 ignore stop */
60
+ if (sizeBefore < maxSizeBytes)
61
+ continue;
62
+ // rotateIfNeeded returns true because we pre-checked sizeBefore >=
63
+ // maxSizeBytes above. The false branch of `if (rotated)` is defensive
64
+ // and unreachable under normal flow; we skip it from coverage so a
65
+ // future refactor that weakens the pre-check still reports correct
66
+ // counts without needing a contrived test.
67
+ const rotated = (0, nerves_1.rotateIfNeeded)(filePath, {
68
+ maxSizeBytes,
69
+ maxGenerations,
70
+ compress: true,
71
+ });
72
+ /* v8 ignore next 3 -- defensive: pre-check guarantees rotated=true @preserve */
73
+ if (!rotated) {
74
+ continue;
75
+ }
76
+ filesCompacted += 1;
77
+ bytesFreed += sizeBefore;
78
+ }
79
+ completed = true;
80
+ (0, runtime_1.emitNervesEvent)({
81
+ component: "nerves",
82
+ event: "nerves.logs_prune_end",
83
+ trace_id: traceId,
84
+ message: "daemon logs pruned",
85
+ meta: { logsDir, filesCompacted, bytesFreed },
86
+ });
87
+ return { filesCompacted, bytesFreed };
88
+ }
89
+ catch (err) {
90
+ /* v8 ignore next -- defensive: completed=true only reached after try returns @preserve */
91
+ if (!completed) {
92
+ /* v8 ignore next -- defensive: rotation always throws real Errors @preserve */
93
+ const reason = err instanceof Error ? err.message : String(err);
94
+ (0, runtime_1.emitNervesEvent)({
95
+ component: "nerves",
96
+ event: "nerves.logs_prune_error",
97
+ trace_id: traceId,
98
+ level: "error",
99
+ message: "daemon logs prune failed",
100
+ meta: { logsDir, error: reason },
101
+ });
102
+ }
103
+ throw err;
104
+ }
105
+ }
@@ -35,9 +35,9 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.FileMessageRouter = void 0;
37
37
  const fs = __importStar(require("fs"));
38
- const os = __importStar(require("os"));
39
38
  const path = __importStar(require("path"));
40
39
  const runtime_1 = require("../../nerves/runtime");
40
+ const identity_1 = require("../identity");
41
41
  function messageId(nowIso) {
42
42
  return `msg-${nowIso.replace(/[^0-9]/g, "")}`;
43
43
  }
@@ -45,7 +45,7 @@ class FileMessageRouter {
45
45
  baseDir;
46
46
  now;
47
47
  constructor(options = {}) {
48
- this.baseDir = options.baseDir ?? path.join(os.homedir(), ".agentstate", "messages");
48
+ this.baseDir = options.baseDir ?? (0, identity_1.getAgentMessagesRoot)();
49
49
  this.now = options.now ?? (() => new Date().toISOString());
50
50
  fs.mkdirSync(this.baseDir, { recursive: true });
51
51
  }
@@ -77,12 +77,21 @@ class FileMessageRouter {
77
77
  if (!fs.existsSync(inboxPath))
78
78
  return [];
79
79
  const raw = fs.readFileSync(inboxPath, "utf-8");
80
- fs.writeFileSync(inboxPath, "", "utf-8");
81
- const messages = raw
82
- .split("\n")
83
- .map((line) => line.trim())
84
- .filter((line) => line.length > 0)
85
- .map((line) => JSON.parse(line));
80
+ const messages = [];
81
+ const unparsed = [];
82
+ for (const line of raw.split("\n")) {
83
+ const trimmed = line.trim();
84
+ if (!trimmed)
85
+ continue;
86
+ try {
87
+ messages.push(JSON.parse(trimmed));
88
+ }
89
+ catch {
90
+ unparsed.push(trimmed);
91
+ }
92
+ }
93
+ // Only clear inbox after parsing; preserve lines that failed to parse.
94
+ fs.writeFileSync(inboxPath, unparsed.length > 0 ? unparsed.map((l) => `${l}\n`).join("") : "", "utf-8");
86
95
  (0, runtime_1.emitNervesEvent)({
87
96
  component: "daemon",
88
97
  event: "daemon.message_polled",
@@ -0,0 +1,134 @@
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.createRealOsCronDeps = createRealOsCronDeps;
37
+ exports.createRealCrontabDeps = createRealCrontabDeps;
38
+ exports.resolveOuroBinaryPath = resolveOuroBinaryPath;
39
+ const child_process_1 = require("child_process");
40
+ const fs = __importStar(require("fs"));
41
+ const os = __importStar(require("os"));
42
+ const path = __importStar(require("path"));
43
+ const runtime_1 = require("../../nerves/runtime");
44
+ function createRealOsCronDeps() {
45
+ (0, runtime_1.emitNervesEvent)({
46
+ component: "daemon",
47
+ event: "daemon.os_cron_deps_created",
48
+ message: "created real OS cron deps",
49
+ meta: { platform: process.platform },
50
+ });
51
+ return {
52
+ exec: (cmd) => {
53
+ try {
54
+ (0, child_process_1.execSync)(cmd, { stdio: "ignore" });
55
+ }
56
+ catch {
57
+ /* best effort */
58
+ }
59
+ },
60
+ writeFile: (p, c) => fs.writeFileSync(p, c, "utf-8"),
61
+ removeFile: (p) => {
62
+ try {
63
+ fs.unlinkSync(p);
64
+ }
65
+ catch {
66
+ /* best effort — file may already be gone */
67
+ }
68
+ },
69
+ existsFile: (p) => fs.existsSync(p),
70
+ listDir: (dir) => {
71
+ try {
72
+ return fs.readdirSync(dir);
73
+ }
74
+ catch {
75
+ return [];
76
+ }
77
+ },
78
+ mkdirp: (dir) => fs.mkdirSync(dir, { recursive: true }),
79
+ homeDir: os.homedir(),
80
+ };
81
+ }
82
+ function createRealCrontabDeps() {
83
+ (0, runtime_1.emitNervesEvent)({
84
+ component: "daemon",
85
+ event: "daemon.crontab_deps_created",
86
+ message: "created real crontab deps",
87
+ meta: {},
88
+ });
89
+ /* v8 ignore start -- crontab exec closures: calling these in tests would modify the real system crontab @preserve */
90
+ return {
91
+ execOutput: (cmd) => (0, child_process_1.execSync)(cmd, { encoding: "utf-8" }),
92
+ execWrite: (cmd, stdin) => {
93
+ (0, child_process_1.execSync)(cmd, { input: stdin, stdio: ["pipe", "ignore", "ignore"] });
94
+ },
95
+ };
96
+ /* v8 ignore stop */
97
+ }
98
+ /* v8 ignore start -- ouro path resolution: probes process.argv, filesystem layout, and PATH; branches depend on install method and runtime environment @preserve */
99
+ function resolveOuroBinaryPath() {
100
+ // Try to resolve from process.argv[1] — the script being run
101
+ const scriptPath = process.argv[1];
102
+ if (scriptPath) {
103
+ // If running via node dist/heart/daemon/daemon-entry.js, resolve the ouro wrapper
104
+ // The ouro binary is typically at the package root's bin
105
+ const distDir = path.resolve(path.dirname(scriptPath));
106
+ const packageBin = path.resolve(distDir, "..", "..", "..", "node_modules", ".bin", "ouro");
107
+ if (fs.existsSync(packageBin)) {
108
+ return packageBin;
109
+ }
110
+ // Try the repo-local scripts/ouro.sh
111
+ const repoOuro = path.resolve(distDir, "..", "..", "..", "scripts", "ouro.sh");
112
+ if (fs.existsSync(repoOuro)) {
113
+ return repoOuro;
114
+ }
115
+ }
116
+ // Try which ouro
117
+ try {
118
+ const result = (0, child_process_1.execSync)("which ouro", { encoding: "utf-8" }).trim();
119
+ if (result.length > 0)
120
+ return result;
121
+ }
122
+ catch {
123
+ /* not on PATH */
124
+ }
125
+ // Fallback: use "ouro" and rely on PATH
126
+ (0, runtime_1.emitNervesEvent)({
127
+ component: "daemon",
128
+ event: "daemon.ouro_path_fallback",
129
+ message: "could not resolve full ouro binary path, falling back to 'ouro'",
130
+ meta: {},
131
+ });
132
+ return "ouro";
133
+ }
134
+ /* v8 ignore stop */
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const runtime_1 = require("../../nerves/runtime");
5
5
  const runtime_logging_1 = require("./runtime-logging");
6
- const ouro_bot_wrapper_1 = require("./ouro-bot-wrapper");
6
+ const ouro_bot_wrapper_1 = require("../versioning/ouro-bot-wrapper");
7
7
  (0, runtime_logging_1.configureDaemonRuntimeLogger)("ouro-bot");
8
8
  (0, runtime_1.emitNervesEvent)({
9
9
  component: "daemon",
@@ -51,15 +51,70 @@ class DaemonProcessManager {
51
51
  now;
52
52
  setTimeoutFn;
53
53
  clearTimeoutFn;
54
+ cooldownRecoveryMs;
55
+ maxCooldownRetries;
56
+ existsSyncFn;
57
+ configCheckFn;
58
+ statusWriterFn;
59
+ onSnapshotChangeFn;
60
+ /**
61
+ * Notify the snapshot-change observer (if registered). Swallows any
62
+ * errors from the observer so process lifecycle code never fails
63
+ * because the observer threw.
64
+ */
65
+ notifySnapshotChange(snapshot) {
66
+ if (!this.onSnapshotChangeFn)
67
+ return;
68
+ try {
69
+ this.onSnapshotChangeFn(snapshot);
70
+ }
71
+ catch (error) {
72
+ (0, runtime_1.emitNervesEvent)({
73
+ level: "warn",
74
+ component: "daemon",
75
+ event: "daemon.snapshot_change_observer_error",
76
+ message: "snapshot-change observer threw",
77
+ meta: {
78
+ agent: snapshot.name,
79
+ error: error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error),
80
+ },
81
+ });
82
+ }
83
+ }
84
+ writeStatus(agent, text) {
85
+ try {
86
+ this.statusWriterFn(text);
87
+ }
88
+ catch (error) {
89
+ (0, runtime_1.emitNervesEvent)({
90
+ level: "warn",
91
+ component: "daemon",
92
+ event: "daemon.status_writer_error",
93
+ message: "daemon status writer threw",
94
+ meta: {
95
+ agent,
96
+ error: error instanceof Error ? error.message : String(error),
97
+ },
98
+ });
99
+ }
100
+ }
54
101
  constructor(options) {
55
102
  this.maxRestartsPerHour = options.maxRestartsPerHour ?? 10;
56
103
  this.stabilityThresholdMs = options.stabilityThresholdMs ?? 60_000;
57
104
  this.initialBackoffMs = options.initialBackoffMs ?? 1_000;
58
105
  this.maxBackoffMs = options.maxBackoffMs ?? 60_000;
106
+ this.cooldownRecoveryMs = options.cooldownRecoveryMs ?? 5 * 60 * 1_000;
107
+ this.maxCooldownRetries = options.maxCooldownRetries ?? 3;
59
108
  this.spawnFn = options.spawn ?? ((command, args, spawnOptions) => (0, child_process_1.spawn)(command, args, spawnOptions));
60
109
  this.now = options.now ?? (() => Date.now());
61
110
  this.setTimeoutFn = options.setTimeoutFn ?? ((cb, delay) => setTimeout(cb, delay));
62
111
  this.clearTimeoutFn = options.clearTimeoutFn ?? ((timer) => clearTimeout(timer));
112
+ this.existsSyncFn = options.existsSync ?? null;
113
+ this.configCheckFn = options.configCheck ?? null;
114
+ this.statusWriterFn = options.statusWriter ?? ((text) => {
115
+ process.stderr.write(text);
116
+ });
117
+ this.onSnapshotChangeFn = options.onSnapshotChange ?? null;
63
118
  for (const agent of options.agents) {
64
119
  this.agents.set(agent.name, {
65
120
  config: agent,
@@ -67,6 +122,9 @@ class DaemonProcessManager {
67
122
  restartTimer: null,
68
123
  crashTimestamps: [],
69
124
  stopRequested: false,
125
+ cooldownTimer: null,
126
+ cooldownRetryCount: 0,
127
+ fastCrashCount: 0,
70
128
  snapshot: {
71
129
  name: agent.name,
72
130
  channel: agent.channel,
@@ -76,6 +134,10 @@ class DaemonProcessManager {
76
134
  startedAt: null,
77
135
  lastCrashAt: null,
78
136
  backoffMs: this.initialBackoffMs,
137
+ lastExitCode: null,
138
+ lastSignal: null,
139
+ errorReason: null,
140
+ fixHint: null,
79
141
  },
80
142
  });
81
143
  }
@@ -94,14 +156,62 @@ class DaemonProcessManager {
94
156
  this.clearRestartTimer(state);
95
157
  state.stopRequested = false;
96
158
  state.snapshot.status = "starting";
159
+ if (this.configCheckFn) {
160
+ const result = await this.configCheckFn(agent);
161
+ if (!result.ok) {
162
+ state.snapshot.status = "crashed";
163
+ // Surface the error and fix to the snapshot so sibling agents can
164
+ // read it via the pulse. Without this, the diagnosis stayed
165
+ // trapped in the nerves event and stderr — visible to humans
166
+ // running `ouro status` or grepping logs, but invisible to
167
+ // peer agents trying to coordinate around the broken state.
168
+ state.snapshot.errorReason = result.error ?? "agent config validation failed";
169
+ state.snapshot.fixHint = result.fix ?? null;
170
+ (0, runtime_1.emitNervesEvent)({
171
+ level: "error",
172
+ component: "daemon",
173
+ event: "daemon.agent_config_invalid",
174
+ message: result.error ?? "agent config validation failed",
175
+ meta: { agent, fix: result.fix ?? null },
176
+ });
177
+ this.writeStatus(agent, `[daemon] ${agent}: ${result.error}\n` +
178
+ (result.fix ? ` Fix: ${result.fix}\n` : ""));
179
+ this.notifySnapshotChange(state.snapshot);
180
+ return;
181
+ }
182
+ // Config check passed — clear any prior error so the pulse stops
183
+ // reporting the broken state. This is the recovery path: the user
184
+ // fixed their secrets/config, the next startAgent attempt sees a
185
+ // valid config, and the pulse goes quiet.
186
+ state.snapshot.errorReason = null;
187
+ state.snapshot.fixHint = null;
188
+ }
97
189
  const runCwd = (0, identity_1.getRepoRoot)();
98
190
  const entryScript = path.join((0, identity_1.getRepoRoot)(), "dist", state.config.entry);
191
+ if (this.existsSyncFn && !this.existsSyncFn(entryScript)) {
192
+ state.snapshot.status = "crashed";
193
+ (0, runtime_1.emitNervesEvent)({
194
+ level: "error",
195
+ component: "daemon",
196
+ event: "daemon.agent_entry_missing",
197
+ message: "agent entry script does not exist — cannot spawn. Run 'ouro daemon install' from the correct location.",
198
+ meta: { agent, entryScript },
199
+ });
200
+ this.notifySnapshotChange(state.snapshot);
201
+ return;
202
+ }
99
203
  const args = [entryScript, "--agent", state.config.agentArg ?? agent, ...(state.config.args ?? [])];
100
204
  const child = this.spawnFn("node", args, {
101
205
  cwd: runCwd,
102
206
  env: state.config.env ? { ...process.env, ...state.config.env } : process.env,
103
207
  stdio: ["ignore", "ignore", "ignore", "ipc"],
104
208
  });
209
+ /* v8 ignore next 7 -- defensive: spawn should always return a ChildProcess @preserve */
210
+ if (!child) {
211
+ state.snapshot.status = "crashed";
212
+ (0, runtime_1.emitNervesEvent)({ level: "error", component: "daemon", event: "daemon.agent_spawn_failed", message: "spawn returned null", meta: { agent } });
213
+ return;
214
+ }
105
215
  state.process = child;
106
216
  state.snapshot.status = "running";
107
217
  state.snapshot.pid = child.pid ?? null;
@@ -112,6 +222,18 @@ class DaemonProcessManager {
112
222
  message: "daemon started managed agent process",
113
223
  meta: { agent, pid: child.pid ?? null, cwd: runCwd },
114
224
  });
225
+ this.notifySnapshotChange(state.snapshot);
226
+ /* v8 ignore start — child process error handler; requires real spawn to trigger */
227
+ child.on("error", (err) => {
228
+ (0, runtime_1.emitNervesEvent)({
229
+ level: "warn",
230
+ component: "daemon",
231
+ event: "daemon.agent_process_error",
232
+ message: "managed agent process emitted error",
233
+ meta: { agent, error: err.message },
234
+ });
235
+ });
236
+ /* v8 ignore stop */
115
237
  child.once("exit", (code, signal) => {
116
238
  this.onExit(state, code, signal);
117
239
  });
@@ -119,10 +241,12 @@ class DaemonProcessManager {
119
241
  async stopAgent(agent) {
120
242
  const state = this.requireAgent(agent);
121
243
  this.clearRestartTimer(state);
244
+ this.clearCooldownTimer(state);
122
245
  state.stopRequested = true;
123
246
  if (!state.process) {
124
247
  state.snapshot.status = "stopped";
125
248
  state.snapshot.pid = null;
249
+ this.notifySnapshotChange(state.snapshot);
126
250
  return;
127
251
  }
128
252
  const child = state.process;
@@ -141,6 +265,7 @@ class DaemonProcessManager {
141
265
  meta: { agent },
142
266
  });
143
267
  }
268
+ this.notifySnapshotChange(state.snapshot);
144
269
  }
145
270
  async restartAgent(agent) {
146
271
  await this.stopAgent(agent);
@@ -179,6 +304,8 @@ class DaemonProcessManager {
179
304
  return;
180
305
  state.process = null;
181
306
  state.snapshot.pid = null;
307
+ state.snapshot.lastExitCode = code;
308
+ state.snapshot.lastSignal = signal;
182
309
  const crashed = !state.stopRequested && code !== 0;
183
310
  const now = this.now();
184
311
  const startedAt = state.snapshot.startedAt ? Date.parse(state.snapshot.startedAt) : now;
@@ -195,13 +322,45 @@ class DaemonProcessManager {
195
322
  if (runDuration >= this.stabilityThresholdMs) {
196
323
  state.snapshot.backoffMs = this.initialBackoffMs;
197
324
  }
325
+ this.notifySnapshotChange(state.snapshot);
198
326
  return;
199
327
  }
200
328
  state.snapshot.lastCrashAt = new Date(now).toISOString();
329
+ // Fast-crash detection: if the agent dies within 5 seconds of starting, it's likely
330
+ // a configuration issue (missing credentials, bad provider, etc.) not a transient failure.
331
+ // After 3 consecutive fast crashes, stop retrying and mark as config-failed.
332
+ const FAST_CRASH_THRESHOLD_MS = 5000;
333
+ const FAST_CRASH_MAX = 3;
334
+ if (runDuration < FAST_CRASH_THRESHOLD_MS) {
335
+ state.fastCrashCount = state.fastCrashCount + 1;
336
+ if (state.fastCrashCount >= FAST_CRASH_MAX) {
337
+ state.snapshot.status = "crashed";
338
+ // Capture the fast-crash diagnosis on the snapshot so it surfaces
339
+ // via the pulse. The error message is prescriptive: it tells the
340
+ // user (and their sibling agents) exactly what to do.
341
+ 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).`;
342
+ state.snapshot.fixHint = `Fix the config and run \`ouro up\` to restart, or check daemon logs for the underlying error.`;
343
+ (0, runtime_1.emitNervesEvent)({
344
+ level: "error",
345
+ component: "daemon",
346
+ event: "daemon.agent_config_failure",
347
+ 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.`,
348
+ meta: { agent: state.config.name, fastCrashCount: state.fastCrashCount, avgRunDurationMs: runDuration },
349
+ });
350
+ this.notifySnapshotChange(state.snapshot);
351
+ return; // Don't schedule cooldown recovery — this needs human/agent intervention
352
+ }
353
+ }
354
+ else {
355
+ // Reset fast-crash counter on a stable run
356
+ state.fastCrashCount = 0;
357
+ }
201
358
  state.crashTimestamps = state.crashTimestamps.filter((crashTs) => crashTs >= startOfHour(now));
202
359
  state.crashTimestamps.push(now);
203
360
  if (state.crashTimestamps.length > this.maxRestartsPerHour) {
204
361
  state.snapshot.status = "crashed";
362
+ state.snapshot.errorReason = `agent exceeded restart limit (${this.maxRestartsPerHour}/hr) — entering cooldown`;
363
+ state.snapshot.fixHint = "investigate why the agent keeps crashing; cooldown will retry shortly";
205
364
  (0, runtime_1.emitNervesEvent)({
206
365
  level: "error",
207
366
  component: "daemon",
@@ -209,6 +368,8 @@ class DaemonProcessManager {
209
368
  message: "managed agent exceeded restart limit and is marked crashed",
210
369
  meta: { agent: state.config.name, maxRestartsPerHour: this.maxRestartsPerHour },
211
370
  });
371
+ this.notifySnapshotChange(state.snapshot);
372
+ this.scheduleCooldownRecovery(state);
212
373
  return;
213
374
  }
214
375
  state.snapshot.status = "starting";
@@ -219,6 +380,7 @@ class DaemonProcessManager {
219
380
  state.restartTimer = this.setTimeoutFn(() => {
220
381
  void this.startAgent(state.config.name);
221
382
  }, waitMs);
383
+ this.notifySnapshotChange(state.snapshot);
222
384
  }
223
385
  clearRestartTimer(state) {
224
386
  if (state.restartTimer === null)
@@ -226,6 +388,45 @@ class DaemonProcessManager {
226
388
  this.clearTimeoutFn(state.restartTimer);
227
389
  state.restartTimer = null;
228
390
  }
391
+ scheduleCooldownRecovery(state) {
392
+ if (state.cooldownRetryCount >= this.maxCooldownRetries) {
393
+ (0, runtime_1.emitNervesEvent)({
394
+ level: "error",
395
+ component: "daemon",
396
+ event: "daemon.agent_permanent_failure",
397
+ message: "managed agent exhausted all cooldown retries — permanently stopped",
398
+ meta: { agent: state.config.name, cooldownRetryCount: state.cooldownRetryCount, maxCooldownRetries: this.maxCooldownRetries },
399
+ });
400
+ return;
401
+ }
402
+ this.clearCooldownTimer(state);
403
+ state.cooldownTimer = this.setTimeoutFn(() => {
404
+ state.cooldownRetryCount += 1;
405
+ state.crashTimestamps = [];
406
+ state.snapshot.backoffMs = this.initialBackoffMs;
407
+ state.snapshot.status = "starting";
408
+ state.snapshot.restartCount += 1;
409
+ (0, runtime_1.emitNervesEvent)({
410
+ component: "daemon",
411
+ event: "daemon.agent_cooldown_recovery",
412
+ message: "attempting cooldown recovery for managed agent",
413
+ meta: { agent: state.config.name, cooldownRetryCount: state.cooldownRetryCount },
414
+ });
415
+ void this.startAgent(state.config.name);
416
+ }, this.cooldownRecoveryMs);
417
+ (0, runtime_1.emitNervesEvent)({
418
+ component: "daemon",
419
+ event: "daemon.agent_cooldown_scheduled",
420
+ message: `scheduled cooldown recovery in ${this.cooldownRecoveryMs}ms`,
421
+ meta: { agent: state.config.name, cooldownRecoveryMs: this.cooldownRecoveryMs, cooldownRetryCount: state.cooldownRetryCount },
422
+ });
423
+ }
424
+ clearCooldownTimer(state) {
425
+ if (state.cooldownTimer === null)
426
+ return;
427
+ this.clearTimeoutFn(state.cooldownTimer);
428
+ state.cooldownTimer = null;
429
+ }
229
430
  requireAgent(agent) {
230
431
  const state = this.agents.get(agent);
231
432
  if (!state) {