@ouro.bot/cli 0.1.0-alpha.34 → 0.1.0-alpha.340

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 (314) 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/changelog.json +2031 -0
  5. package/dist/arc/attention-types.js +8 -0
  6. package/dist/arc/cares.js +140 -0
  7. package/dist/arc/episodes.js +117 -0
  8. package/dist/arc/intentions.js +133 -0
  9. package/dist/arc/json-store.js +117 -0
  10. package/dist/arc/obligations.js +237 -0
  11. package/dist/arc/packets.js +193 -0
  12. package/dist/arc/presence.js +185 -0
  13. package/dist/arc/task-lifecycle.js +65 -0
  14. package/dist/heart/active-work.js +832 -0
  15. package/dist/heart/agent-entry.js +37 -2
  16. package/dist/heart/attachments/image-normalize.js +194 -0
  17. package/dist/heart/attachments/materialize.js +97 -0
  18. package/dist/heart/attachments/originals.js +88 -0
  19. package/dist/heart/attachments/render.js +29 -0
  20. package/dist/heart/attachments/sources/adapter.js +2 -0
  21. package/dist/heart/attachments/sources/bluebubbles.js +156 -0
  22. package/dist/heart/attachments/sources/cli-local-file.js +78 -0
  23. package/dist/heart/attachments/sources/index.js +16 -0
  24. package/dist/heart/attachments/store.js +103 -0
  25. package/dist/heart/attachments/types.js +93 -0
  26. package/dist/heart/auth/auth-flow.js +463 -0
  27. package/dist/heart/bridges/manager.js +358 -0
  28. package/dist/heart/bridges/state-machine.js +135 -0
  29. package/dist/heart/bridges/store.js +123 -0
  30. package/dist/heart/bundle-state.js +168 -0
  31. package/dist/heart/commitments.js +111 -0
  32. package/dist/heart/config-registry.js +304 -0
  33. package/dist/heart/config.js +53 -21
  34. package/dist/heart/core.js +695 -195
  35. package/dist/heart/cross-chat-delivery.js +131 -0
  36. package/dist/heart/daemon/agent-config-check.js +292 -0
  37. package/dist/heart/daemon/agent-discovery.js +79 -3
  38. package/dist/heart/daemon/agent-service.js +360 -0
  39. package/dist/heart/daemon/agentic-repair.js +170 -0
  40. package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
  41. package/dist/heart/daemon/cadence.js +70 -0
  42. package/dist/heart/daemon/cli-defaults.js +591 -0
  43. package/dist/heart/daemon/cli-exec.js +2297 -0
  44. package/dist/heart/daemon/cli-help.js +306 -0
  45. package/dist/heart/daemon/cli-parse.js +824 -0
  46. package/dist/heart/daemon/cli-render-doctor.js +57 -0
  47. package/dist/heart/daemon/cli-render.js +512 -0
  48. package/dist/heart/daemon/cli-types.js +8 -0
  49. package/dist/heart/daemon/daemon-cli.js +30 -1171
  50. package/dist/heart/daemon/daemon-entry.js +358 -3
  51. package/dist/heart/daemon/daemon-health.js +141 -0
  52. package/dist/heart/daemon/daemon-runtime-sync.js +157 -12
  53. package/dist/heart/daemon/daemon-tombstone.js +236 -0
  54. package/dist/heart/daemon/daemon.js +751 -58
  55. package/dist/heart/daemon/doctor-types.js +8 -0
  56. package/dist/heart/daemon/doctor.js +401 -0
  57. package/dist/heart/daemon/health-monitor.js +79 -1
  58. package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
  59. package/dist/heart/daemon/hooks/bundle-meta.js +115 -1
  60. package/dist/heart/daemon/http-health-probe.js +80 -0
  61. package/dist/heart/daemon/inner-status.js +89 -0
  62. package/dist/heart/daemon/interactive-repair.js +91 -0
  63. package/dist/heart/daemon/launchd.js +46 -9
  64. package/dist/heart/daemon/log-tailer.js +82 -12
  65. package/dist/heart/daemon/logs-prune.js +105 -0
  66. package/dist/heart/daemon/message-router.js +17 -8
  67. package/dist/heart/daemon/os-cron-deps.js +134 -0
  68. package/dist/heart/daemon/ouro-bot-entry.js +1 -1
  69. package/dist/heart/daemon/process-manager.js +201 -0
  70. package/dist/heart/daemon/provider-discovery.js +105 -0
  71. package/dist/heart/daemon/pulse.js +463 -0
  72. package/dist/heart/daemon/run-hooks.js +2 -0
  73. package/dist/heart/daemon/runtime-logging.js +67 -16
  74. package/dist/heart/daemon/runtime-metadata.js +101 -0
  75. package/dist/heart/daemon/runtime-mode.js +67 -0
  76. package/dist/heart/daemon/safe-mode.js +161 -0
  77. package/dist/heart/daemon/sense-manager.js +72 -3
  78. package/dist/heart/daemon/session-id-resolver.js +131 -0
  79. package/dist/heart/daemon/skill-management-installer.js +94 -0
  80. package/dist/heart/daemon/socket-client.js +307 -0
  81. package/dist/heart/daemon/stale-bundle-prune.js +96 -0
  82. package/dist/heart/daemon/startup-tui.js +237 -0
  83. package/dist/heart/daemon/task-scheduler.js +3 -25
  84. package/dist/heart/daemon/thoughts.js +510 -0
  85. package/dist/heart/daemon/up-progress.js +135 -0
  86. package/dist/heart/delegation.js +62 -0
  87. package/dist/heart/habits/habit-migration.js +181 -0
  88. package/dist/heart/habits/habit-parser.js +140 -0
  89. package/dist/heart/habits/habit-scheduler.js +371 -0
  90. package/dist/heart/{daemon → hatch}/hatch-flow.js +32 -120
  91. package/dist/heart/{daemon → hatch}/hatch-specialist.js +3 -3
  92. package/dist/heart/{daemon → hatch}/specialist-prompt.js +10 -7
  93. package/dist/heart/{daemon → hatch}/specialist-tools.js +49 -3
  94. package/dist/heart/identity.js +154 -59
  95. package/dist/heart/kicks.js +2 -20
  96. package/dist/heart/mcp/mcp-server.js +653 -0
  97. package/dist/heart/migrate-config.js +127 -0
  98. package/dist/heart/model-capabilities.js +59 -0
  99. package/dist/heart/outlook/outlook-http-hooks.js +64 -0
  100. package/dist/heart/outlook/outlook-http-response.js +7 -0
  101. package/dist/heart/outlook/outlook-http-routes.js +232 -0
  102. package/dist/heart/outlook/outlook-http-static.js +99 -0
  103. package/dist/heart/outlook/outlook-http-transport.js +116 -0
  104. package/dist/heart/outlook/outlook-http.js +99 -0
  105. package/dist/heart/outlook/outlook-read.js +28 -0
  106. package/dist/heart/outlook/outlook-types.js +27 -0
  107. package/dist/heart/outlook/outlook-view.js +194 -0
  108. package/dist/heart/outlook/readers/agent-machine.js +355 -0
  109. package/dist/heart/outlook/readers/continuity-readers.js +332 -0
  110. package/dist/heart/outlook/readers/runtime-readers.js +660 -0
  111. package/dist/heart/outlook/readers/sessions.js +231 -0
  112. package/dist/heart/outlook/readers/shared.js +111 -0
  113. package/dist/heart/progress-story.js +42 -0
  114. package/dist/heart/provider-failover.js +135 -0
  115. package/dist/heart/provider-models.js +81 -0
  116. package/dist/heart/provider-ping.js +234 -0
  117. package/dist/heart/providers/anthropic-token.js +163 -0
  118. package/dist/heart/providers/anthropic.js +171 -50
  119. package/dist/heart/providers/azure.js +97 -11
  120. package/dist/heart/providers/error-classification.js +63 -0
  121. package/dist/heart/providers/github-copilot.js +135 -0
  122. package/dist/heart/providers/minimax-vlm.js +189 -0
  123. package/dist/heart/providers/minimax.js +23 -6
  124. package/dist/heart/providers/openai-codex.js +33 -23
  125. package/dist/heart/session-activity.js +190 -0
  126. package/dist/heart/session-events.js +726 -0
  127. package/dist/heart/session-recall.js +162 -0
  128. package/dist/heart/start-of-turn-packet.js +341 -0
  129. package/dist/heart/streaming.js +36 -27
  130. package/dist/heart/sync.js +332 -0
  131. package/dist/heart/target-resolution.js +127 -0
  132. package/dist/heart/tempo.js +93 -0
  133. package/dist/heart/temporal-view.js +41 -0
  134. package/dist/heart/tool-activity-callbacks.js +36 -0
  135. package/dist/heart/tool-description.js +135 -0
  136. package/dist/heart/tool-friction.js +55 -0
  137. package/dist/heart/tool-loop.js +200 -0
  138. package/dist/heart/turn-context.js +358 -0
  139. package/dist/heart/turn-coordinator.js +28 -0
  140. package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +1 -1
  141. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
  142. package/dist/heart/{daemon → versioning}/ouro-path-installer.js +78 -35
  143. package/dist/heart/versioning/ouro-version-manager.js +295 -0
  144. package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
  145. package/dist/heart/{daemon → versioning}/update-checker.js +12 -2
  146. package/dist/heart/{daemon → versioning}/update-hooks.js +63 -59
  147. package/dist/mind/associative-recall.js +137 -66
  148. package/dist/mind/bundle-manifest.js +7 -1
  149. package/dist/mind/context.js +89 -93
  150. package/dist/mind/diary-integrity.js +60 -0
  151. package/dist/mind/{memory.js → diary.js} +84 -96
  152. package/dist/mind/embedding-provider.js +60 -0
  153. package/dist/mind/file-state.js +179 -0
  154. package/dist/mind/first-impressions.js +14 -1
  155. package/dist/mind/friends/channel.js +56 -0
  156. package/dist/mind/friends/group-context.js +144 -0
  157. package/dist/mind/friends/resolver.js +37 -0
  158. package/dist/mind/friends/store-file.js +58 -3
  159. package/dist/mind/friends/trust-explanation.js +74 -0
  160. package/dist/mind/friends/types.js +8 -0
  161. package/dist/mind/journal-index.js +161 -0
  162. package/dist/mind/obligation-steering.js +221 -0
  163. package/dist/mind/pending.js +74 -7
  164. package/dist/mind/prompt.js +999 -111
  165. package/dist/mind/provenance-trust.js +26 -0
  166. package/dist/mind/scrutiny.js +173 -0
  167. package/dist/mind/token-estimate.js +8 -12
  168. package/dist/nerves/cli-logging.js +7 -1
  169. package/dist/nerves/coverage/audit.js +1 -1
  170. package/dist/nerves/coverage/file-completeness.js +83 -5
  171. package/dist/nerves/coverage/run-artifacts.js +1 -1
  172. package/dist/nerves/event-buffer.js +111 -0
  173. package/dist/nerves/index.js +224 -4
  174. package/dist/nerves/observation.js +20 -0
  175. package/dist/nerves/redact.js +79 -0
  176. package/dist/nerves/runtime.js +5 -1
  177. package/dist/outlook-ui/assets/index-DC7sZefn.js +61 -0
  178. package/dist/outlook-ui/assets/index-LwChZTgL.css +1 -0
  179. package/dist/outlook-ui/index.html +15 -0
  180. package/dist/repertoire/ado-client.js +15 -56
  181. package/dist/repertoire/ado-semantic.js +11 -10
  182. package/dist/repertoire/api-client.js +97 -0
  183. package/dist/repertoire/bitwarden-store.js +319 -0
  184. package/dist/repertoire/bundle-templates.js +72 -0
  185. package/dist/repertoire/bw-installer.js +79 -0
  186. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  187. package/dist/repertoire/coding/context-pack.js +330 -0
  188. package/dist/repertoire/coding/feedback.js +197 -30
  189. package/dist/repertoire/coding/manager.js +158 -9
  190. package/dist/repertoire/coding/spawner.js +55 -9
  191. package/dist/repertoire/coding/tools.js +170 -7
  192. package/dist/repertoire/commerce-errors.js +109 -0
  193. package/dist/repertoire/commerce-self-test.js +156 -0
  194. package/dist/repertoire/credential-access.js +527 -0
  195. package/dist/repertoire/duffel-client.js +185 -0
  196. package/dist/repertoire/github-client.js +14 -55
  197. package/dist/repertoire/graph-client.js +11 -52
  198. package/dist/repertoire/guardrails.js +375 -0
  199. package/dist/repertoire/mcp-client.js +255 -0
  200. package/dist/repertoire/mcp-manager.js +305 -0
  201. package/dist/repertoire/mcp-tools.js +63 -0
  202. package/dist/repertoire/shell-sessions.js +133 -0
  203. package/dist/repertoire/skills.js +14 -23
  204. package/dist/repertoire/stripe-client.js +131 -0
  205. package/dist/repertoire/tasks/board.js +43 -5
  206. package/dist/repertoire/tasks/fix.js +182 -0
  207. package/dist/repertoire/tasks/index.js +28 -10
  208. package/dist/repertoire/tasks/lifecycle.js +2 -2
  209. package/dist/repertoire/tasks/parser.js +3 -2
  210. package/dist/repertoire/tasks/scanner.js +194 -37
  211. package/dist/repertoire/tasks/transitions.js +16 -79
  212. package/dist/repertoire/tool-results.js +29 -0
  213. package/dist/repertoire/tools-attachments.js +316 -0
  214. package/dist/repertoire/tools-base.js +45 -771
  215. package/dist/repertoire/tools-bluebubbles.js +1 -0
  216. package/dist/repertoire/tools-bridge.js +141 -0
  217. package/dist/repertoire/tools-bundle.js +984 -0
  218. package/dist/repertoire/tools-config.js +185 -0
  219. package/dist/repertoire/tools-continuity.js +248 -0
  220. package/dist/repertoire/tools-credential.js +182 -0
  221. package/dist/repertoire/tools-files.js +342 -0
  222. package/dist/repertoire/tools-flight.js +224 -0
  223. package/dist/repertoire/tools-flow.js +105 -0
  224. package/dist/repertoire/tools-github.js +1 -7
  225. package/dist/repertoire/tools-memory.js +376 -0
  226. package/dist/repertoire/tools-session.js +739 -0
  227. package/dist/repertoire/tools-shell.js +120 -0
  228. package/dist/repertoire/tools-stripe.js +180 -0
  229. package/dist/repertoire/tools-surface.js +243 -0
  230. package/dist/repertoire/tools-teams.js +12 -62
  231. package/dist/repertoire/tools-travel.js +125 -0
  232. package/dist/repertoire/tools-user-profile.js +144 -0
  233. package/dist/repertoire/tools-vault.js +110 -0
  234. package/dist/repertoire/tools.js +144 -138
  235. package/dist/repertoire/travel-api-client.js +360 -0
  236. package/dist/repertoire/user-profile.js +118 -0
  237. package/dist/repertoire/vault-setup.js +241 -0
  238. package/dist/scripts/claude-code-hook.js +41 -0
  239. package/dist/scripts/claude-code-stop-hook.js +47 -0
  240. package/dist/senses/attention-queue.js +116 -0
  241. package/dist/senses/bluebubbles/attachment-cache.js +53 -0
  242. package/dist/senses/bluebubbles/attachment-download.js +137 -0
  243. package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +225 -9
  244. package/dist/senses/bluebubbles/entry.js +13 -0
  245. package/dist/senses/bluebubbles/inbound-log.js +113 -0
  246. package/dist/senses/bluebubbles/index.js +1590 -0
  247. package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -70
  248. package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +43 -12
  249. package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +46 -6
  250. package/dist/senses/bluebubbles/replay.js +129 -0
  251. package/dist/senses/bluebubbles/runtime-state.js +109 -0
  252. package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
  253. package/dist/senses/cli/bracketed-paste.js +82 -0
  254. package/dist/senses/cli/image-paste.js +287 -0
  255. package/dist/senses/cli/image-ref-navigation.js +75 -0
  256. package/dist/senses/cli/ink-app.js +156 -0
  257. package/dist/senses/cli/inline-diff.js +64 -0
  258. package/dist/senses/cli/input-keys.js +174 -0
  259. package/dist/senses/cli/kill-ring.js +86 -0
  260. package/dist/senses/cli/message-list.js +51 -0
  261. package/dist/senses/cli/ouro-tui.js +605 -0
  262. package/dist/senses/cli/spinner-imperative.js +135 -0
  263. package/dist/senses/cli/spinner.js +101 -0
  264. package/dist/senses/cli/status-line.js +60 -0
  265. package/dist/senses/cli/streaming-markdown.js +526 -0
  266. package/dist/senses/cli/tool-display.js +83 -0
  267. package/dist/senses/cli/tool-render.js +85 -0
  268. package/dist/senses/cli/tui-store.js +240 -0
  269. package/dist/senses/cli/virtual-list.js +35 -0
  270. package/dist/senses/cli-entry.js +1 -1
  271. package/dist/senses/cli-layout.js +187 -0
  272. package/dist/senses/cli.js +595 -246
  273. package/dist/senses/commands.js +65 -1
  274. package/dist/senses/continuity.js +94 -0
  275. package/dist/senses/habit-turn-message.js +108 -0
  276. package/dist/senses/inner-dialog-worker.js +112 -19
  277. package/dist/senses/inner-dialog.js +633 -86
  278. package/dist/senses/pipeline.js +567 -0
  279. package/dist/senses/shared-turn.js +199 -0
  280. package/dist/senses/surface-tool.js +68 -0
  281. package/dist/senses/teams.js +665 -160
  282. package/dist/senses/trust-gate.js +112 -2
  283. package/package.json +29 -7
  284. package/skills/agent-commerce.md +106 -0
  285. package/skills/browser-navigation.md +110 -0
  286. package/skills/commerce-setup-guide.md +116 -0
  287. package/skills/commerce-setup.md +84 -0
  288. package/skills/configure-dev-tools.md +81 -0
  289. package/skills/travel-planning.md +138 -0
  290. package/dist/heart/daemon/subagent-installer.js +0 -134
  291. package/dist/senses/bluebubbles-entry.js +0 -11
  292. package/dist/senses/bluebubbles.js +0 -547
  293. package/dist/senses/debug-activity.js +0 -124
  294. package/subagents/README.md +0 -73
  295. package/subagents/work-doer.md +0 -235
  296. package/subagents/work-merger.md +0 -618
  297. package/subagents/work-planner.md +0 -382
  298. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  299. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  300. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  301. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  302. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  303. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  304. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  305. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  306. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  307. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  308. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  309. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +0 -0
  310. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  311. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  312. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  313. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
  314. /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
@@ -0,0 +1,463 @@
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.getPulsePath = getPulsePath;
37
+ exports.getPulseDeliveredPath = getPulseDeliveredPath;
38
+ exports.buildAlertId = buildAlertId;
39
+ exports.readAgentActivity = readAgentActivity;
40
+ exports.buildPulseState = buildPulseState;
41
+ exports.findNovelBrokenAgents = findNovelBrokenAgents;
42
+ exports.findRecoveredAgents = findRecoveredAgents;
43
+ exports.buildRecoveryAlertId = buildRecoveryAlertId;
44
+ exports.pickWakeRecipient = pickWakeRecipient;
45
+ exports.writePulse = writePulse;
46
+ exports.readPulse = readPulse;
47
+ exports.readDeliveredState = readDeliveredState;
48
+ exports.writeDeliveredState = writeDeliveredState;
49
+ exports.pruneDeliveredState = pruneDeliveredState;
50
+ exports.flushPulse = flushPulse;
51
+ const fs = __importStar(require("fs"));
52
+ const os = __importStar(require("os"));
53
+ const path = __importStar(require("path"));
54
+ const runtime_1 = require("../../nerves/runtime");
55
+ /* v8 ignore next 3 -- path defaults: tests always inject @preserve */
56
+ function defaultPulsePath() {
57
+ return path.join(os.homedir(), ".ouro-cli", "pulse.json");
58
+ }
59
+ /* v8 ignore next 3 -- path defaults: tests always inject @preserve */
60
+ function defaultDeliveredPath() {
61
+ return path.join(os.homedir(), ".ouro-cli", "pulse-delivered.json");
62
+ }
63
+ function getPulsePath() {
64
+ return defaultPulsePath();
65
+ }
66
+ function getPulseDeliveredPath() {
67
+ return defaultDeliveredPath();
68
+ }
69
+ /**
70
+ * Hash an error reason into a short stable identifier. We use a simple
71
+ * non-cryptographic hash because we only need stability across daemon
72
+ * restarts on the same machine, not collision resistance against
73
+ * adversaries. djb2 is fine.
74
+ */
75
+ function hashErrorReason(reason) {
76
+ let h = 5381;
77
+ for (let i = 0; i < reason.length; i++) {
78
+ h = ((h << 5) + h + reason.charCodeAt(i)) >>> 0;
79
+ }
80
+ return h.toString(16);
81
+ }
82
+ /**
83
+ * Build a stable alert ID for a (agent, error) pair. Same agent + same
84
+ * error → same ID, even across daemon restarts. Different error → new ID.
85
+ */
86
+ function buildAlertId(agent, errorReason) {
87
+ return `${agent}:${hashErrorReason(errorReason)}`;
88
+ }
89
+ /**
90
+ * Read an agent's inner-dialog runtime state and format it as a short
91
+ * activity hint string suitable for display in another agent's pulse
92
+ * section. Returns null when the file is missing or malformed — both
93
+ * cases are silent (the pulse just doesn't include activity for that
94
+ * agent).
95
+ *
96
+ * Pure: takes a file reader so tests can inject content without touching
97
+ * fs. Defaults to fs.readFileSync for the production daemon caller.
98
+ */
99
+ function readAgentActivity(bundlePath, readFile = (p) => fs.readFileSync(p, "utf-8")) {
100
+ const runtimePath = path.join(bundlePath, "state", "sessions", "self", "inner", "runtime.json");
101
+ let raw;
102
+ try {
103
+ raw = readFile(runtimePath);
104
+ }
105
+ catch {
106
+ return null;
107
+ }
108
+ let parsed;
109
+ try {
110
+ parsed = JSON.parse(raw);
111
+ }
112
+ catch {
113
+ return null;
114
+ }
115
+ const status = typeof parsed.status === "string" ? parsed.status : null;
116
+ const reason = typeof parsed.reason === "string" ? parsed.reason : null;
117
+ const startedAt = typeof parsed.startedAt === "string" ? parsed.startedAt : null;
118
+ if (!status)
119
+ return null;
120
+ // Format compactly. Examples:
121
+ // "running (instinct since 23:44)"
122
+ // "idle since 21:30"
123
+ // "running"
124
+ const sinceStr = startedAt ? ` since ${startedAt.slice(11, 16)}` : "";
125
+ if (status === "running" && reason) {
126
+ return `running (${reason}${sinceStr})`;
127
+ }
128
+ return `${status}${sinceStr}`;
129
+ }
130
+ /**
131
+ * Convert daemon process-manager snapshots into the pulse state shape.
132
+ * Pure: no fs side effects (activity reading goes through the injected
133
+ * `readActivity` callback so tests can pin everything).
134
+ *
135
+ * The bundlePath comes from the bundlesRoot — we don't read the bundle
136
+ * directly, we just compute where it lives so sibling agents have a
137
+ * starting point if they want to navigate there manually.
138
+ */
139
+ function buildPulseState(snapshots, bundlesRoot, daemonVersion, now, readActivity = readAgentActivity) {
140
+ const agents = snapshots.map((snap) => {
141
+ const errorReason = snap.errorReason;
142
+ const bundlePath = path.join(bundlesRoot, `${snap.name}.ouro`);
143
+ return {
144
+ name: snap.name,
145
+ bundlePath,
146
+ status: snap.status,
147
+ lastSeenAt: snap.startedAt,
148
+ errorReason,
149
+ fixHint: snap.fixHint,
150
+ alertId: errorReason ? buildAlertId(snap.name, errorReason) : null,
151
+ // Only read activity for agents that are actually running. For
152
+ // crashed/stopped agents, the runtime.json is stale at best.
153
+ currentActivity: snap.status === "running" ? readActivity(bundlePath) : null,
154
+ };
155
+ });
156
+ return {
157
+ generatedAt: now.toISOString(),
158
+ daemonVersion,
159
+ agents,
160
+ };
161
+ }
162
+ /**
163
+ * A "novel broken transition" is an agent that is currently broken AND
164
+ * either (a) wasn't broken in the previous pulse state, OR (b) was broken
165
+ * but with a different alertId. Used to decide when to fire an inner.wake
166
+ * for proactive notification.
167
+ *
168
+ * Pure: takes prev and next states, returns the list of newly-broken
169
+ * agents that warrant a wake.
170
+ */
171
+ function findNovelBrokenAgents(prev, next) {
172
+ const novel = [];
173
+ for (const agent of next.agents) {
174
+ if (!agent.alertId)
175
+ continue;
176
+ const prevAgent = prev?.agents.find((a) => a.name === agent.name);
177
+ const wasBrokenWithSameAlert = prevAgent?.alertId === agent.alertId;
178
+ if (wasBrokenWithSameAlert)
179
+ continue;
180
+ novel.push(agent);
181
+ }
182
+ return novel;
183
+ }
184
+ /**
185
+ * A "recovery transition" is an agent that was broken in the previous
186
+ * pulse state but is healthy now. The pulse fires a wake on these too,
187
+ * so the user gets a positive notification when their fix takes effect.
188
+ *
189
+ * Pure: takes prev and next states, returns the list of newly-recovered
190
+ * agents that warrant a wake.
191
+ */
192
+ function findRecoveredAgents(prev, next) {
193
+ if (!prev)
194
+ return [];
195
+ const recovered = [];
196
+ for (const agent of next.agents) {
197
+ // Healthy in next.
198
+ if (agent.alertId !== null)
199
+ continue;
200
+ if (agent.status !== "running")
201
+ continue;
202
+ // Was broken in prev.
203
+ const prevAgent = prev.agents.find((a) => a.name === agent.name);
204
+ if (!prevAgent || prevAgent.alertId === null)
205
+ continue;
206
+ recovered.push(agent);
207
+ }
208
+ return recovered;
209
+ }
210
+ /**
211
+ * Build a stable alert ID for a recovery event. Used so the recovery
212
+ * wake is also at-most-once (no re-paging on every snapshot change after
213
+ * recovery). Includes the recovery timestamp so a later break+heal cycle
214
+ * generates a fresh ID.
215
+ */
216
+ function buildRecoveryAlertId(agent, recoveredAt) {
217
+ return `recovery:${agent}:${recoveredAt}`;
218
+ }
219
+ /**
220
+ * Pick which agent should receive an inner.wake for a fleet alert. Heuristic:
221
+ * the most-recently-active running agent (by `lastSeenAt`), excluding the
222
+ * broken agent itself and any agents that aren't currently running. Returns
223
+ * null if there's no eligible recipient (e.g., the only other agent on the
224
+ * machine is also broken).
225
+ *
226
+ * Pure: takes the pulse state and the alert target, returns the chosen
227
+ * recipient name or null.
228
+ */
229
+ function pickWakeRecipient(state, brokenAgent) {
230
+ const candidates = state.agents
231
+ .filter((a) => a.name !== brokenAgent)
232
+ .filter((a) => a.status === "running")
233
+ .filter((a) => a.lastSeenAt !== null)
234
+ .sort((a, b) => {
235
+ // Most-recent first. lastSeenAt is non-null per the filter above.
236
+ const aMs = Date.parse(a.lastSeenAt);
237
+ const bMs = Date.parse(b.lastSeenAt);
238
+ return bMs - aMs;
239
+ });
240
+ return candidates[0]?.name ?? null;
241
+ }
242
+ /**
243
+ * Write the pulse state to disk. Best-effort: if the write fails, emit a
244
+ * nerves event but do not throw. The pulse is a notification mechanism;
245
+ * the daemon's primary work should not be blocked by it.
246
+ */
247
+ function writePulse(state, deps = {}) {
248
+ /* v8 ignore start -- dep defaults: production-only paths; tests inject all four explicitly @preserve */
249
+ const filePath = deps.pulsePath ?? defaultPulsePath();
250
+ const writeFile = deps.writeFile ?? ((p, c) => fs.writeFileSync(p, c, "utf-8"));
251
+ const mkdirp = deps.mkdirp ?? ((d) => fs.mkdirSync(d, { recursive: true }));
252
+ /* v8 ignore stop */
253
+ try {
254
+ mkdirp(path.dirname(filePath));
255
+ writeFile(filePath, JSON.stringify(state, null, 2) + "\n");
256
+ (0, runtime_1.emitNervesEvent)({
257
+ component: "daemon",
258
+ event: "daemon.pulse_written",
259
+ message: "wrote machine pulse state",
260
+ meta: { filePath, agentCount: state.agents.length },
261
+ });
262
+ }
263
+ catch (error) {
264
+ (0, runtime_1.emitNervesEvent)({
265
+ level: "warn",
266
+ component: "daemon",
267
+ event: "daemon.pulse_write_error",
268
+ message: "failed to write pulse state",
269
+ meta: {
270
+ filePath,
271
+ error: error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error),
272
+ },
273
+ });
274
+ }
275
+ }
276
+ /**
277
+ * Read the pulse state from disk. Returns null if the file doesn't exist
278
+ * or is malformed — pulse readers (like the prompt assembler) should
279
+ * gracefully degrade to "no pulse, render nothing" rather than crash.
280
+ */
281
+ function readPulse(deps = {}) {
282
+ const filePath = deps.pulsePath ?? defaultPulsePath();
283
+ /* v8 ignore next -- dep default: tests always inject @preserve */
284
+ const readFile = deps.readFile ?? ((p) => fs.readFileSync(p, "utf-8"));
285
+ try {
286
+ const raw = readFile(filePath);
287
+ const parsed = JSON.parse(raw);
288
+ if (typeof parsed.generatedAt !== "string")
289
+ return null;
290
+ if (typeof parsed.daemonVersion !== "string")
291
+ return null;
292
+ if (!Array.isArray(parsed.agents))
293
+ return null;
294
+ return {
295
+ generatedAt: parsed.generatedAt,
296
+ daemonVersion: parsed.daemonVersion,
297
+ agents: parsed.agents.filter(isValidPulseAgentEntry),
298
+ };
299
+ }
300
+ catch {
301
+ return null;
302
+ }
303
+ }
304
+ function isValidPulseAgentEntry(value) {
305
+ if (value === null || typeof value !== "object")
306
+ return false;
307
+ const v = value;
308
+ return (typeof v.name === "string"
309
+ && typeof v.bundlePath === "string"
310
+ && (v.status === "running" || v.status === "starting" || v.status === "stopped" || v.status === "crashed")
311
+ && (v.lastSeenAt === null || typeof v.lastSeenAt === "string")
312
+ && (v.errorReason === null || typeof v.errorReason === "string")
313
+ && (v.fixHint === null || typeof v.fixHint === "string")
314
+ && (v.alertId === null || typeof v.alertId === "string"));
315
+ }
316
+ /**
317
+ * Read the persistent delivered-alerts state. Returns an empty set if the
318
+ * file doesn't exist or is malformed.
319
+ */
320
+ function readDeliveredState(deps = {}) {
321
+ /* v8 ignore start -- dep defaults: production-only paths; tests inject all explicitly @preserve */
322
+ const filePath = deps.deliveredPath ?? defaultDeliveredPath();
323
+ const readFile = deps.readFile ?? ((p) => fs.readFileSync(p, "utf-8"));
324
+ /* v8 ignore stop */
325
+ try {
326
+ const raw = readFile(filePath);
327
+ const parsed = JSON.parse(raw);
328
+ if (!Array.isArray(parsed.delivered))
329
+ return new Set();
330
+ return new Set(parsed.delivered.filter((id) => typeof id === "string"));
331
+ }
332
+ catch {
333
+ return new Set();
334
+ }
335
+ }
336
+ /**
337
+ * Persist the delivered-alerts state to disk. Best-effort.
338
+ */
339
+ function writeDeliveredState(delivered, deps = {}) {
340
+ /* v8 ignore start -- dep defaults: production-only paths; tests inject all four explicitly @preserve */
341
+ const filePath = deps.deliveredPath ?? defaultDeliveredPath();
342
+ const writeFile = deps.writeFile ?? ((p, c) => fs.writeFileSync(p, c, "utf-8"));
343
+ const mkdirp = deps.mkdirp ?? ((d) => fs.mkdirSync(d, { recursive: true }));
344
+ /* v8 ignore stop */
345
+ try {
346
+ mkdirp(path.dirname(filePath));
347
+ const state = { delivered: [...delivered].sort() };
348
+ writeFile(filePath, JSON.stringify(state, null, 2) + "\n");
349
+ }
350
+ catch (error) {
351
+ (0, runtime_1.emitNervesEvent)({
352
+ level: "warn",
353
+ component: "daemon",
354
+ event: "daemon.pulse_delivered_write_error",
355
+ message: "failed to write pulse delivered state",
356
+ meta: {
357
+ filePath,
358
+ error: error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error),
359
+ },
360
+ });
361
+ }
362
+ }
363
+ /**
364
+ * Prune delivered alert IDs that no longer correspond to an active broken
365
+ * agent. When the user fixes ouroboros's config, the next pulse state has
366
+ * no alertId for ouroboros, so we drop the old delivered entry — meaning
367
+ * if ouroboros breaks AGAIN later (with the same error), we re-page.
368
+ */
369
+ function pruneDeliveredState(delivered, state) {
370
+ const liveAlertIds = new Set();
371
+ for (const agent of state.agents) {
372
+ if (agent.alertId)
373
+ liveAlertIds.add(agent.alertId);
374
+ }
375
+ const pruned = new Set();
376
+ for (const id of delivered) {
377
+ if (liveAlertIds.has(id))
378
+ pruned.add(id);
379
+ }
380
+ return pruned;
381
+ }
382
+ /**
383
+ * Single entry point the daemon's onSnapshotChange callback uses. Builds
384
+ * the new pulse state, diffs against the previous, writes the file, and
385
+ * fires inner.wake on novel broken transitions to the most-recently-active
386
+ * sibling. Persistent at-most-once delivery via the delivered state file.
387
+ *
388
+ * Pure-ish: all I/O goes through dep callbacks so tests can pin every
389
+ * input and assert every effect. Defaults wire to the real fs functions
390
+ * for production callers.
391
+ */
392
+ function flushPulse(deps) {
393
+ const state = buildPulseState(deps.snapshots, deps.bundlesRoot, deps.daemonVersion, deps.now);
394
+ /* v8 ignore start -- dep defaults: production daemon path; the arrow functions only fire when the corresponding dep is omitted, which only happens in production code paths. Tests inject all deps explicitly. @preserve */
395
+ const readPrev = deps.readPrev ?? (() => readPulse());
396
+ const writeNext = deps.writeNext ?? ((s) => writePulse(s));
397
+ const readDelivered = deps.readDelivered ?? (() => readDeliveredState());
398
+ const writeDelivered = deps.writeDelivered ?? ((d) => writeDeliveredState(d));
399
+ const fireInnerWake = deps.fireInnerWake ?? null;
400
+ /* v8 ignore stop */
401
+ const prev = readPrev();
402
+ let delivered = readDelivered();
403
+ // Write the new pulse state first so any reader (including the
404
+ // wake-recipient agent's prompt assembly on its next turn) sees the
405
+ // current state. Doing this before firing the wake matters: if the
406
+ // wake races against a fast prompt build on the recipient, we want
407
+ // the recipient to read the NEW state, not the old one.
408
+ writeNext(state);
409
+ // Find agents that newly transitioned to broken (or to a different
410
+ // error than before).
411
+ const novelBroken = findNovelBrokenAgents(prev, state);
412
+ // Of those, the ones we haven't already delivered an alert for.
413
+ const undeliveredBroken = novelBroken.filter((a) => a.alertId !== null && !delivered.has(a.alertId));
414
+ // Find agents that newly recovered (were broken in prev, healthy now).
415
+ const recovered = findRecoveredAgents(prev, state);
416
+ // For recovery wakes, build a fresh alert ID per recovery event so
417
+ // we don't re-page on every subsequent flush.
418
+ const recoveryAlertIds = recovered.map((a) => buildRecoveryAlertId(a.name, state.generatedAt));
419
+ const undeliveredRecovered = recovered.filter((_, i) => !delivered.has(recoveryAlertIds[i]));
420
+ // Fire wakes and update the delivered set.
421
+ const wakeFiredFor = [];
422
+ const newlyDelivered = [];
423
+ for (const broken of undeliveredBroken) {
424
+ /* v8 ignore next -- defensive: undeliveredBroken already filtered to non-null alertId; this is a TS narrowing helper @preserve */
425
+ if (broken.alertId === null)
426
+ continue;
427
+ const recipient = pickWakeRecipient(state, broken.name);
428
+ if (recipient !== null && fireInnerWake !== null) {
429
+ fireInnerWake(recipient);
430
+ wakeFiredFor.push(recipient);
431
+ }
432
+ // Mark delivered even if no recipient was available — otherwise
433
+ // the daemon will keep trying to wake every time the snapshot
434
+ // changes, which would spam logs without producing any user value.
435
+ // The passive prompt section still surfaces the broken state to
436
+ // the recipient when they eventually have a turn for any reason.
437
+ delivered.add(broken.alertId);
438
+ newlyDelivered.push(broken.alertId);
439
+ }
440
+ for (let i = 0; i < undeliveredRecovered.length; i++) {
441
+ const recoveredAgent = undeliveredRecovered[i];
442
+ const recoveryAlertId = buildRecoveryAlertId(recoveredAgent.name, state.generatedAt);
443
+ const recipient = pickWakeRecipient(state, recoveredAgent.name);
444
+ if (recipient !== null && fireInnerWake !== null) {
445
+ fireInnerWake(recipient);
446
+ wakeFiredFor.push(recipient);
447
+ }
448
+ delivered.add(recoveryAlertId);
449
+ newlyDelivered.push(recoveryAlertId);
450
+ }
451
+ // Drop delivered ids for agents that have healed since (this drops
452
+ // the original `agent_config_failure:...` IDs once the agent recovers,
453
+ // so a future relapse re-pages). Recovery alert IDs are NOT pruned by
454
+ // pruneDeliveredState because they're tied to a healthy state, not a
455
+ // broken state — they live in the delivered set until the same agent
456
+ // breaks and recovers again, at which point a new recovery ID is
457
+ // generated and the old one becomes harmless cruft. We could clean
458
+ // them up too, but the cost is bounded by the number of recovery
459
+ // cycles, which is small.
460
+ delivered = pruneDeliveredState(delivered, state);
461
+ writeDelivered(delivered);
462
+ return { state, wakeFiredFor, newlyDelivered };
463
+ }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.runHooks = runHooks;
4
4
  const runtime_1 = require("../../nerves/runtime");
5
5
  const bundle_meta_1 = require("./hooks/bundle-meta");
6
+ const agent_config_v2_1 = require("./hooks/agent-config-v2");
6
7
  async function runHooks(deps) {
7
8
  (0, runtime_1.emitNervesEvent)({
8
9
  component: "daemon",
@@ -12,6 +13,7 @@ async function runHooks(deps) {
12
13
  });
13
14
  try {
14
15
  deps.registerUpdateHook(bundle_meta_1.bundleMetaHook);
16
+ deps.registerUpdateHook(agent_config_v2_1.agentConfigV2Hook);
15
17
  const currentVersion = deps.getPackageVersion();
16
18
  await deps.applyPendingUpdates(deps.bundlesRoot, currentVersion);
17
19
  (0, runtime_1.emitNervesEvent)({
@@ -35,53 +35,104 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.configureDaemonRuntimeLogger = configureDaemonRuntimeLogger;
37
37
  const fs = __importStar(require("fs"));
38
- const os = __importStar(require("os"));
39
38
  const path = __importStar(require("path"));
40
39
  const nerves_1 = require("../../nerves");
41
40
  const runtime_1 = require("../../nerves/runtime");
42
- const DEFAULT_RUNTIME_LOGGING = {
41
+ const identity_1 = require("../identity");
42
+ const LEGACY_SHARED_RUNTIME_LOGGING = {
43
43
  level: "info",
44
44
  sinks: ["terminal", "ndjson"],
45
45
  };
46
- function defaultLevelForProcess(processName) {
47
- return processName === "daemon" ? "info" : "warn";
46
+ function defaultLoggingForProcess(processName) {
47
+ if (processName === "ouro" || processName === "ouro-bot") {
48
+ return {
49
+ level: "info",
50
+ sinks: ["ndjson"],
51
+ };
52
+ }
53
+ if (processName === "bluebubbles") {
54
+ return {
55
+ level: "warn",
56
+ sinks: ["terminal", "ndjson"],
57
+ };
58
+ }
59
+ return { ...LEGACY_SHARED_RUNTIME_LOGGING };
48
60
  }
49
61
  function isLogLevel(value) {
50
62
  return value === "debug" || value === "info" || value === "warn" || value === "error";
51
63
  }
64
+ function normalizeSinks(value, fallback) {
65
+ if (!Array.isArray(value)) {
66
+ return [...fallback];
67
+ }
68
+ const sinks = value.filter((entry) => entry === "terminal" || entry === "ndjson");
69
+ return sinks.length > 0 ? [...new Set(sinks)] : [...fallback];
70
+ }
71
+ function isLegacySharedDefaultConfig(candidate, normalizedLevel, normalizedSinks) {
72
+ return normalizedLevel === LEGACY_SHARED_RUNTIME_LOGGING.level
73
+ && normalizedSinks.length === LEGACY_SHARED_RUNTIME_LOGGING.sinks.length
74
+ && LEGACY_SHARED_RUNTIME_LOGGING.sinks.every((sink) => normalizedSinks.includes(sink))
75
+ && Object.keys(candidate).every((key) => key === "level" || key === "sinks");
76
+ }
52
77
  function resolveRuntimeLoggingConfig(configPath, processName) {
53
- const defaultLevel = defaultLevelForProcess(processName);
78
+ const processDefault = defaultLoggingForProcess(processName);
54
79
  let parsed = null;
55
80
  try {
56
81
  const raw = fs.readFileSync(configPath, "utf-8");
57
82
  parsed = JSON.parse(raw);
58
83
  }
59
84
  catch {
60
- return { ...DEFAULT_RUNTIME_LOGGING, level: defaultLevel };
85
+ return { ...processDefault };
61
86
  }
62
87
  if (!parsed || typeof parsed !== "object") {
63
- return { ...DEFAULT_RUNTIME_LOGGING, level: defaultLevel };
88
+ return { ...processDefault };
64
89
  }
65
90
  const candidate = parsed;
66
- const level = isLogLevel(candidate.level) ? candidate.level : defaultLevel;
67
- const sinks = Array.isArray(candidate.sinks)
68
- ? candidate.sinks.filter((entry) => entry === "terminal" || entry === "ndjson")
69
- : DEFAULT_RUNTIME_LOGGING.sinks;
91
+ const level = isLogLevel(candidate.level) ? candidate.level : processDefault.level;
92
+ const sinks = normalizeSinks(candidate.sinks, processDefault.sinks);
93
+ if ((processName === "ouro" || processName === "ouro-bot")
94
+ && isLegacySharedDefaultConfig(candidate, level, sinks)) {
95
+ return { ...processDefault };
96
+ }
70
97
  return {
71
98
  level,
72
- sinks: sinks.length > 0 ? [...new Set(sinks)] : [...DEFAULT_RUNTIME_LOGGING.sinks],
99
+ sinks,
73
100
  };
74
101
  }
102
+ function resolveAgentNameForPaths(explicit) {
103
+ if (explicit && explicit.trim().length > 0)
104
+ return explicit.trim();
105
+ try {
106
+ return (0, identity_1.getAgentName)();
107
+ }
108
+ catch {
109
+ return "slugger";
110
+ }
111
+ }
75
112
  function configureDaemonRuntimeLogger(processName, options = {}) {
76
- const homeDir = options.homeDir ?? os.homedir();
77
- const configPath = options.configPath ?? path.join(homeDir, ".agentstate", "daemon", "logging.json");
113
+ const agentName = resolveAgentNameForPaths(options.agentName);
114
+ const configPath = options.configPath
115
+ ?? (options.homeDir
116
+ ? path.join(options.homeDir, "AgentBundles", `${agentName}.ouro`, "state", "daemon", "logging.json")
117
+ : (0, identity_1.getAgentDaemonLoggingConfigPath)(agentName));
78
118
  const config = resolveRuntimeLoggingConfig(configPath, processName);
119
+ const logsDir = options.homeDir
120
+ ? path.join(options.homeDir, "AgentBundles", `${agentName}.ouro`, "state", "daemon", "logs")
121
+ : (0, identity_1.getAgentDaemonLogsDir)(agentName);
122
+ // Rotation policy per daemon ndjson stream (Unit 1c):
123
+ // 25 MB threshold x 5 gzipped generations = ~30 MB peak per stream.
124
+ // Call sites previously relied on the implicit 50 MB default; we now pass
125
+ // the options object explicitly so the policy is visible at the call site.
79
126
  const sinks = config.sinks.map((sinkName) => {
80
127
  if (sinkName === "terminal") {
81
128
  return (0, nerves_1.createTerminalSink)();
82
129
  }
83
- const ndjsonPath = path.join(homeDir, ".agentstate", "daemon", "logs", `${processName}.ndjson`);
84
- return (0, nerves_1.createNdjsonFileSink)(ndjsonPath);
130
+ const ndjsonPath = path.join(logsDir, `${processName}.ndjson`);
131
+ return (0, nerves_1.createNdjsonFileSink)(ndjsonPath, {
132
+ maxSizeBytes: 25 * 1024 * 1024,
133
+ maxGenerations: 5,
134
+ compress: true,
135
+ });
85
136
  });
86
137
  const logger = (0, nerves_1.createLogger)({
87
138
  level: config.level,