@ouro.bot/cli 0.1.0-alpha.19 → 0.1.0-alpha.191

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 (202) hide show
  1. package/README.md +187 -191
  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 +1200 -0
  5. package/dist/heart/active-work.js +832 -0
  6. package/dist/heart/bridges/manager.js +358 -0
  7. package/dist/heart/bridges/state-machine.js +135 -0
  8. package/dist/heart/bridges/store.js +123 -0
  9. package/dist/heart/cares.js +174 -0
  10. package/dist/heart/commitments.js +111 -0
  11. package/dist/heart/config.js +40 -30
  12. package/dist/heart/core.js +620 -124
  13. package/dist/heart/cross-chat-delivery.js +146 -0
  14. package/dist/heart/daemon/agent-discovery.js +81 -0
  15. package/dist/heart/daemon/agent-service.js +360 -0
  16. package/dist/heart/daemon/auth-flow.js +477 -0
  17. package/dist/heart/daemon/cadence.js +70 -0
  18. package/dist/heart/daemon/daemon-cli.js +2470 -210
  19. package/dist/heart/daemon/daemon-entry.js +157 -4
  20. package/dist/heart/daemon/daemon-health.js +136 -0
  21. package/dist/heart/daemon/daemon-runtime-sync.js +212 -0
  22. package/dist/heart/daemon/daemon-tombstone.js +123 -0
  23. package/dist/heart/daemon/daemon.js +449 -17
  24. package/dist/heart/daemon/habit-migration.js +181 -0
  25. package/dist/heart/daemon/habit-parser.js +119 -0
  26. package/dist/heart/daemon/habit-scheduler.js +369 -0
  27. package/dist/heart/daemon/hatch-flow.js +31 -112
  28. package/dist/heart/daemon/hatch-specialist.js +3 -3
  29. package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
  30. package/dist/heart/daemon/hooks/bundle-meta.js +92 -0
  31. package/dist/heart/daemon/inner-status.js +89 -0
  32. package/dist/heart/daemon/launchd.js +165 -0
  33. package/dist/heart/daemon/log-tailer.js +4 -3
  34. package/dist/heart/daemon/mcp-server.js +591 -0
  35. package/dist/heart/daemon/message-router.js +17 -8
  36. package/dist/heart/daemon/os-cron-deps.js +134 -0
  37. package/dist/heart/daemon/ouro-bot-global-installer.js +128 -0
  38. package/dist/heart/daemon/ouro-path-installer.js +78 -34
  39. package/dist/heart/daemon/ouro-version-manager.js +171 -0
  40. package/dist/heart/daemon/outlook-http.js +399 -0
  41. package/dist/heart/daemon/outlook-read.js +1331 -0
  42. package/dist/heart/daemon/outlook-render.js +1032 -0
  43. package/dist/heart/daemon/outlook-types.js +7 -0
  44. package/dist/heart/daemon/outlook-view.js +194 -0
  45. package/dist/heart/daemon/process-manager.js +75 -0
  46. package/dist/heart/daemon/run-hooks.js +39 -0
  47. package/dist/heart/daemon/runtime-logging.js +58 -15
  48. package/dist/heart/daemon/runtime-metadata.js +219 -0
  49. package/dist/heart/daemon/runtime-mode.js +67 -0
  50. package/dist/heart/daemon/safe-mode.js +161 -0
  51. package/dist/heart/daemon/sense-manager.js +50 -2
  52. package/dist/heart/daemon/session-id-resolver.js +131 -0
  53. package/dist/heart/daemon/skill-management-installer.js +94 -0
  54. package/dist/heart/daemon/socket-client.js +202 -0
  55. package/dist/heart/daemon/specialist-prompt.js +10 -7
  56. package/dist/heart/daemon/specialist-tools.js +49 -3
  57. package/dist/heart/daemon/staged-restart.js +114 -0
  58. package/dist/heart/daemon/task-scheduler.js +3 -25
  59. package/dist/heart/daemon/thoughts.js +507 -0
  60. package/dist/heart/daemon/update-checker.js +111 -0
  61. package/dist/heart/daemon/update-hooks.js +138 -0
  62. package/dist/heart/daemon/wrapper-publish-guard.js +86 -0
  63. package/dist/heart/delegation.js +62 -0
  64. package/dist/heart/identity.js +136 -55
  65. package/dist/heart/intentions.js +168 -0
  66. package/dist/heart/kicks.js +2 -20
  67. package/dist/heart/migrate-config.js +127 -0
  68. package/dist/heart/model-capabilities.js +48 -0
  69. package/dist/heart/obligations.js +220 -0
  70. package/dist/heart/presence.js +185 -0
  71. package/dist/heart/progress-story.js +42 -0
  72. package/dist/heart/provider-failover.js +88 -0
  73. package/dist/heart/provider-ping.js +172 -0
  74. package/dist/heart/providers/anthropic-token.js +163 -0
  75. package/dist/heart/providers/anthropic.js +187 -41
  76. package/dist/heart/providers/azure.js +116 -10
  77. package/dist/heart/providers/github-copilot.js +157 -0
  78. package/dist/heart/providers/minimax.js +33 -3
  79. package/dist/heart/providers/openai-codex.js +51 -16
  80. package/dist/heart/session-activity.js +172 -0
  81. package/dist/heart/session-recall.js +216 -0
  82. package/dist/heart/streaming.js +36 -27
  83. package/dist/heart/target-resolution.js +127 -0
  84. package/dist/heart/tempo.js +93 -0
  85. package/dist/heart/temporal-view.js +41 -0
  86. package/dist/heart/tool-activity-callbacks.js +36 -0
  87. package/dist/heart/tool-description.js +135 -0
  88. package/dist/heart/tool-loop.js +200 -0
  89. package/dist/heart/turn-coordinator.js +28 -0
  90. package/dist/heart/wake-packet.js +226 -0
  91. package/dist/mind/associative-recall.js +122 -32
  92. package/dist/mind/bundle-manifest.js +14 -0
  93. package/dist/mind/context.js +125 -14
  94. package/dist/mind/{memory.js → diary.js} +70 -56
  95. package/dist/mind/episodes.js +116 -0
  96. package/dist/mind/file-state.js +179 -0
  97. package/dist/mind/first-impressions.js +16 -2
  98. package/dist/mind/friends/channel.js +56 -0
  99. package/dist/mind/friends/group-context.js +144 -0
  100. package/dist/mind/friends/store-file.js +36 -0
  101. package/dist/mind/friends/trust-explanation.js +74 -0
  102. package/dist/mind/friends/types.js +8 -0
  103. package/dist/mind/journal-index.js +161 -0
  104. package/dist/mind/obligation-steering.js +221 -0
  105. package/dist/mind/obligations.js +134 -0
  106. package/dist/mind/pending.js +76 -9
  107. package/dist/mind/phrases.js +1 -0
  108. package/dist/mind/prompt.js +779 -102
  109. package/dist/mind/scrutiny.js +173 -0
  110. package/dist/mind/token-estimate.js +8 -12
  111. package/dist/nerves/coverage/audit.js +1 -1
  112. package/dist/nerves/coverage/run-artifacts.js +1 -1
  113. package/dist/nerves/event-buffer.js +111 -0
  114. package/dist/nerves/index.js +63 -3
  115. package/dist/nerves/observation.js +20 -0
  116. package/dist/nerves/runtime.js +5 -1
  117. package/dist/outlook-ui/assets/index-DN6vVYPm.css +1 -0
  118. package/dist/outlook-ui/assets/index-M1l1tDTX.js +61 -0
  119. package/dist/outlook-ui/index.html +15 -0
  120. package/dist/repertoire/ado-semantic.js +11 -5
  121. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  122. package/dist/repertoire/coding/context-pack.js +330 -0
  123. package/dist/repertoire/coding/feedback.js +197 -30
  124. package/dist/repertoire/coding/manager.js +149 -2
  125. package/dist/repertoire/coding/spawner.js +55 -9
  126. package/dist/repertoire/coding/tools.js +170 -7
  127. package/dist/repertoire/guardrails.js +291 -0
  128. package/dist/repertoire/mcp-client.js +254 -0
  129. package/dist/repertoire/mcp-manager.js +251 -0
  130. package/dist/repertoire/shell-sessions.js +133 -0
  131. package/dist/repertoire/skills.js +14 -23
  132. package/dist/repertoire/tasks/board.js +43 -5
  133. package/dist/repertoire/tasks/fix.js +182 -0
  134. package/dist/repertoire/tasks/index.js +28 -10
  135. package/dist/repertoire/tasks/lifecycle.js +2 -2
  136. package/dist/repertoire/tasks/parser.js +3 -2
  137. package/dist/repertoire/tasks/scanner.js +194 -37
  138. package/dist/repertoire/tasks/transitions.js +11 -28
  139. package/dist/repertoire/tools-base.js +1345 -320
  140. package/dist/repertoire/tools-bluebubbles.js +94 -0
  141. package/dist/repertoire/tools-github.js +1 -6
  142. package/dist/repertoire/tools-teams.js +12 -59
  143. package/dist/repertoire/tools.js +318 -124
  144. package/dist/scripts/claude-code-hook.js +41 -0
  145. package/dist/scripts/claude-code-stop-hook.js +47 -0
  146. package/dist/senses/attention-queue.js +97 -0
  147. package/dist/senses/bluebubbles-client.js +102 -3
  148. package/dist/senses/bluebubbles-entry.js +2 -0
  149. package/dist/senses/bluebubbles-inbound-log.js +109 -0
  150. package/dist/senses/bluebubbles-media.js +118 -23
  151. package/dist/senses/bluebubbles-model.js +12 -4
  152. package/dist/senses/bluebubbles-mutation-log.js +45 -5
  153. package/dist/senses/bluebubbles-runtime-state.js +109 -0
  154. package/dist/senses/bluebubbles-session-cleanup.js +72 -0
  155. package/dist/senses/bluebubbles.js +1008 -102
  156. package/dist/senses/cli/adapter.js +274 -0
  157. package/dist/senses/cli/ink-app.js +156 -0
  158. package/dist/senses/cli/inline-diff.js +64 -0
  159. package/dist/senses/cli/message-list.js +51 -0
  160. package/dist/senses/cli/ouro-tui.js +349 -0
  161. package/dist/senses/cli/spinner-imperative.js +135 -0
  162. package/dist/senses/cli/spinner.js +101 -0
  163. package/dist/senses/cli/status-line.js +60 -0
  164. package/dist/senses/cli/streaming-markdown.js +526 -0
  165. package/dist/senses/cli/tool-display.js +83 -0
  166. package/dist/senses/cli/tool-render.js +85 -0
  167. package/dist/senses/cli/tui-store.js +198 -0
  168. package/dist/senses/cli/virtual-list.js +35 -0
  169. package/dist/senses/cli-entry.js +1 -1
  170. package/dist/senses/cli-layout.js +187 -0
  171. package/dist/senses/cli.js +508 -243
  172. package/dist/senses/commands.js +65 -1
  173. package/dist/senses/continuity.js +94 -0
  174. package/dist/senses/habit-turn-message.js +108 -0
  175. package/dist/senses/inner-dialog-worker.js +112 -19
  176. package/dist/senses/inner-dialog.js +592 -84
  177. package/dist/senses/pipeline.js +633 -0
  178. package/dist/senses/shared-turn.js +196 -0
  179. package/dist/senses/surface-tool.js +95 -0
  180. package/dist/senses/teams.js +667 -120
  181. package/dist/senses/trust-gate.js +112 -2
  182. package/package.json +16 -6
  183. package/skills/configure-dev-tools.md +81 -0
  184. package/subagents/README.md +4 -70
  185. package/dist/heart/daemon/subagent-installer.js +0 -134
  186. package/dist/senses/debug-activity.js +0 -107
  187. package/subagents/work-doer.md +0 -235
  188. package/subagents/work-merger.md +0 -618
  189. package/subagents/work-planner.md +0 -382
  190. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  191. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  192. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  193. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  194. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  195. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  196. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  197. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  198. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  199. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  200. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  201. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +0 -0
  202. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OUTLOOK_DEFAULT_PORT = exports.OUTLOOK_DEFAULT_INNER_VISIBILITY = exports.OUTLOOK_RELEASE_INTERACTION_MODEL = exports.OUTLOOK_PRODUCT_NAME = void 0;
4
+ exports.OUTLOOK_PRODUCT_NAME = "Ouro Outlook";
5
+ exports.OUTLOOK_RELEASE_INTERACTION_MODEL = "read-only";
6
+ exports.OUTLOOK_DEFAULT_INNER_VISIBILITY = "summary";
7
+ exports.OUTLOOK_DEFAULT_PORT = 6876;
@@ -0,0 +1,194 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildOutlookMachineView = buildOutlookMachineView;
4
+ exports.buildOutlookAgentView = buildOutlookAgentView;
5
+ const runtime_1 = require("../../nerves/runtime");
6
+ const outlook_types_1 = require("./outlook-types");
7
+ function deriveAttention(agent) {
8
+ if (agent.degraded.status === "degraded") {
9
+ return { level: "degraded", label: "Degraded" };
10
+ }
11
+ if (agent.freshness.status === "stale") {
12
+ return { level: "stale", label: "Stale" };
13
+ }
14
+ if (agent.tasks.blockedCount > 0 || agent.coding.blockedCount > 0) {
15
+ return { level: "blocked", label: "Blocked" };
16
+ }
17
+ if (agent.tasks.liveCount > 0 || agent.coding.activeCount > 0 || agent.obligations.openCount > 0) {
18
+ return { level: "active", label: "Active" };
19
+ }
20
+ return { level: "idle", label: "Idle" };
21
+ }
22
+ function deriveAgentAttention(agent) {
23
+ return deriveAttention({
24
+ agentName: agent.agentName,
25
+ enabled: agent.enabled,
26
+ freshness: agent.freshness,
27
+ degraded: agent.degraded,
28
+ tasks: {
29
+ liveCount: agent.tasks.liveCount,
30
+ blockedCount: agent.tasks.blockedCount,
31
+ },
32
+ obligations: {
33
+ openCount: agent.obligations.openCount,
34
+ },
35
+ coding: {
36
+ activeCount: agent.coding.activeCount,
37
+ blockedCount: agent.coding.blockedCount,
38
+ },
39
+ });
40
+ }
41
+ function buildTotals(machine) {
42
+ return machine.agents.reduce((totals, agent) => ({
43
+ agents: totals.agents + 1,
44
+ enabledAgents: totals.enabledAgents + (agent.enabled ? 1 : 0),
45
+ degradedAgents: totals.degradedAgents + (agent.degraded.status === "degraded" ? 1 : 0),
46
+ staleAgents: totals.staleAgents + (agent.freshness.status === "stale" ? 1 : 0),
47
+ liveTasks: totals.liveTasks + agent.tasks.liveCount,
48
+ blockedTasks: totals.blockedTasks + agent.tasks.blockedCount,
49
+ openObligations: totals.openObligations + agent.obligations.openCount,
50
+ activeCodingAgents: totals.activeCodingAgents + (agent.coding.activeCount > 0 ? 1 : 0),
51
+ blockedCodingAgents: totals.blockedCodingAgents + (agent.coding.blockedCount > 0 ? 1 : 0),
52
+ }), {
53
+ agents: 0,
54
+ enabledAgents: 0,
55
+ degradedAgents: 0,
56
+ staleAgents: 0,
57
+ liveTasks: 0,
58
+ blockedTasks: 0,
59
+ openObligations: 0,
60
+ activeCodingAgents: 0,
61
+ blockedCodingAgents: 0,
62
+ });
63
+ }
64
+ function deriveMood(machine, daemon) {
65
+ if (machine.degraded.status === "degraded" || daemon.health === "warn") {
66
+ return "strained";
67
+ }
68
+ if (machine.freshness.status === "stale") {
69
+ return "watchful";
70
+ }
71
+ return "calm";
72
+ }
73
+ function buildOutlookMachineView(input) {
74
+ const totals = buildTotals(input.machine);
75
+ const agents = input.machine.agents
76
+ .map((agent) => ({
77
+ ...agent,
78
+ attention: deriveAttention(agent),
79
+ }))
80
+ .sort((left, right) => {
81
+ const leftAt = left.freshness.latestActivityAt ?? "";
82
+ const rightAt = right.freshness.latestActivityAt ?? "";
83
+ return rightAt.localeCompare(leftAt);
84
+ });
85
+ return {
86
+ overview: {
87
+ productName: outlook_types_1.OUTLOOK_PRODUCT_NAME,
88
+ observedAt: input.machine.observedAt,
89
+ primaryEntryPoint: input.daemon.outlookUrl,
90
+ daemon: input.daemon,
91
+ runtime: input.machine.runtime,
92
+ freshness: input.machine.freshness,
93
+ degraded: input.machine.degraded,
94
+ totals,
95
+ mood: deriveMood(input.machine, input.daemon),
96
+ entrypoints: [
97
+ { kind: "web", label: "Open Outlook", target: input.daemon.outlookUrl },
98
+ { kind: "cli", label: "CLI JSON", target: "ouro outlook --json" },
99
+ ],
100
+ },
101
+ agents,
102
+ };
103
+ }
104
+ function normalizeViewer(viewer) {
105
+ return {
106
+ kind: viewer?.kind ?? "human",
107
+ agentName: viewer?.agentName,
108
+ innerDetail: viewer?.innerDetail ?? "summary",
109
+ };
110
+ }
111
+ function buildInnerView(inner, viewer) {
112
+ if (viewer.innerDetail === "deep") {
113
+ return {
114
+ mode: "deep",
115
+ status: inner.status,
116
+ summary: inner.surfacedSummary,
117
+ hasPending: inner.hasPending,
118
+ origin: inner.origin,
119
+ obligationStatus: inner.obligationStatus,
120
+ };
121
+ }
122
+ return {
123
+ mode: "summary",
124
+ status: inner.status,
125
+ summary: inner.surfacedSummary,
126
+ hasPending: inner.hasPending,
127
+ };
128
+ }
129
+ function buildRecentActivity(agent) {
130
+ const items = [
131
+ ...agent.coding.items.map((item) => ({
132
+ kind: "coding",
133
+ at: item.lastActivityAt,
134
+ label: item.checkpoint ?? `${item.runner} ${item.status}`,
135
+ detail: item.workdir,
136
+ })),
137
+ ...agent.sessions.items.map((item) => ({
138
+ kind: "session",
139
+ at: item.lastActivityAt,
140
+ label: `${item.friendName} via ${item.channel}`,
141
+ detail: item.key,
142
+ })),
143
+ ...agent.obligations.items.map((item) => ({
144
+ kind: "obligation",
145
+ at: item.updatedAt,
146
+ label: item.content,
147
+ detail: item.nextAction ?? item.status,
148
+ })),
149
+ ];
150
+ if (agent.inner.latestActivityAt) {
151
+ items.push({
152
+ kind: "inner",
153
+ at: agent.inner.latestActivityAt,
154
+ label: agent.inner.surfacedSummary ?? agent.inner.status,
155
+ detail: agent.inner.hasPending ? "pending inner work" : agent.inner.obligationStatus ?? "no linked obligation",
156
+ });
157
+ }
158
+ return items
159
+ .filter((item) => Number.isFinite(Date.parse(item.at)))
160
+ .sort((left, right) => right.at.localeCompare(left.at))
161
+ .slice(0, 4);
162
+ }
163
+ function buildOutlookAgentView(input) {
164
+ /* v8 ignore next */
165
+ (0, runtime_1.emitNervesEvent)({ component: "daemon", event: "daemon.outlook_view_agent", message: `building outlook view for ${input.agent.agentName}`, meta: { agent: input.agent.agentName } });
166
+ const viewer = normalizeViewer(input.viewer);
167
+ return {
168
+ productName: outlook_types_1.OUTLOOK_PRODUCT_NAME,
169
+ interactionModel: outlook_types_1.OUTLOOK_RELEASE_INTERACTION_MODEL,
170
+ viewer,
171
+ agent: {
172
+ agentName: input.agent.agentName,
173
+ agentRoot: input.agent.agentRoot,
174
+ enabled: input.agent.enabled,
175
+ provider: input.agent.provider,
176
+ senses: input.agent.senses,
177
+ freshness: input.agent.freshness,
178
+ degraded: input.agent.degraded,
179
+ attention: deriveAgentAttention(input.agent),
180
+ },
181
+ work: {
182
+ tasks: input.agent.tasks,
183
+ obligations: input.agent.obligations,
184
+ sessions: input.agent.sessions,
185
+ coding: input.agent.coding,
186
+ bridges: input.agent.tasks.activeBridges,
187
+ },
188
+ inner: buildInnerView(input.agent.inner, viewer),
189
+ activity: {
190
+ freshness: input.agent.freshness,
191
+ recent: buildRecentActivity(input.agent),
192
+ },
193
+ };
194
+ }
@@ -51,15 +51,21 @@ class DaemonProcessManager {
51
51
  now;
52
52
  setTimeoutFn;
53
53
  clearTimeoutFn;
54
+ cooldownRecoveryMs;
55
+ maxCooldownRetries;
56
+ existsSyncFn;
54
57
  constructor(options) {
55
58
  this.maxRestartsPerHour = options.maxRestartsPerHour ?? 10;
56
59
  this.stabilityThresholdMs = options.stabilityThresholdMs ?? 60_000;
57
60
  this.initialBackoffMs = options.initialBackoffMs ?? 1_000;
58
61
  this.maxBackoffMs = options.maxBackoffMs ?? 60_000;
62
+ this.cooldownRecoveryMs = options.cooldownRecoveryMs ?? 5 * 60 * 1_000;
63
+ this.maxCooldownRetries = options.maxCooldownRetries ?? 3;
59
64
  this.spawnFn = options.spawn ?? ((command, args, spawnOptions) => (0, child_process_1.spawn)(command, args, spawnOptions));
60
65
  this.now = options.now ?? (() => Date.now());
61
66
  this.setTimeoutFn = options.setTimeoutFn ?? ((cb, delay) => setTimeout(cb, delay));
62
67
  this.clearTimeoutFn = options.clearTimeoutFn ?? ((timer) => clearTimeout(timer));
68
+ this.existsSyncFn = options.existsSync ?? null;
63
69
  for (const agent of options.agents) {
64
70
  this.agents.set(agent.name, {
65
71
  config: agent,
@@ -67,6 +73,8 @@ class DaemonProcessManager {
67
73
  restartTimer: null,
68
74
  crashTimestamps: [],
69
75
  stopRequested: false,
76
+ cooldownTimer: null,
77
+ cooldownRetryCount: 0,
70
78
  snapshot: {
71
79
  name: agent.name,
72
80
  channel: agent.channel,
@@ -76,6 +84,8 @@ class DaemonProcessManager {
76
84
  startedAt: null,
77
85
  lastCrashAt: null,
78
86
  backoffMs: this.initialBackoffMs,
87
+ lastExitCode: null,
88
+ lastSignal: null,
79
89
  },
80
90
  });
81
91
  }
@@ -96,6 +106,17 @@ class DaemonProcessManager {
96
106
  state.snapshot.status = "starting";
97
107
  const runCwd = (0, identity_1.getRepoRoot)();
98
108
  const entryScript = path.join((0, identity_1.getRepoRoot)(), "dist", state.config.entry);
109
+ if (this.existsSyncFn && !this.existsSyncFn(entryScript)) {
110
+ state.snapshot.status = "crashed";
111
+ (0, runtime_1.emitNervesEvent)({
112
+ level: "error",
113
+ component: "daemon",
114
+ event: "daemon.agent_entry_missing",
115
+ message: "agent entry script does not exist — cannot spawn. Run 'ouro daemon install' from the correct location.",
116
+ meta: { agent, entryScript },
117
+ });
118
+ return;
119
+ }
99
120
  const args = [entryScript, "--agent", state.config.agentArg ?? agent, ...(state.config.args ?? [])];
100
121
  const child = this.spawnFn("node", args, {
101
122
  cwd: runCwd,
@@ -112,6 +133,17 @@ class DaemonProcessManager {
112
133
  message: "daemon started managed agent process",
113
134
  meta: { agent, pid: child.pid ?? null, cwd: runCwd },
114
135
  });
136
+ /* v8 ignore start — child process error handler; requires real spawn to trigger */
137
+ child.on("error", (err) => {
138
+ (0, runtime_1.emitNervesEvent)({
139
+ level: "warn",
140
+ component: "daemon",
141
+ event: "daemon.agent_process_error",
142
+ message: "managed agent process emitted error",
143
+ meta: { agent, error: err.message },
144
+ });
145
+ });
146
+ /* v8 ignore stop */
115
147
  child.once("exit", (code, signal) => {
116
148
  this.onExit(state, code, signal);
117
149
  });
@@ -119,6 +151,7 @@ class DaemonProcessManager {
119
151
  async stopAgent(agent) {
120
152
  const state = this.requireAgent(agent);
121
153
  this.clearRestartTimer(state);
154
+ this.clearCooldownTimer(state);
122
155
  state.stopRequested = true;
123
156
  if (!state.process) {
124
157
  state.snapshot.status = "stopped";
@@ -179,6 +212,8 @@ class DaemonProcessManager {
179
212
  return;
180
213
  state.process = null;
181
214
  state.snapshot.pid = null;
215
+ state.snapshot.lastExitCode = code;
216
+ state.snapshot.lastSignal = signal;
182
217
  const crashed = !state.stopRequested && code !== 0;
183
218
  const now = this.now();
184
219
  const startedAt = state.snapshot.startedAt ? Date.parse(state.snapshot.startedAt) : now;
@@ -209,6 +244,7 @@ class DaemonProcessManager {
209
244
  message: "managed agent exceeded restart limit and is marked crashed",
210
245
  meta: { agent: state.config.name, maxRestartsPerHour: this.maxRestartsPerHour },
211
246
  });
247
+ this.scheduleCooldownRecovery(state);
212
248
  return;
213
249
  }
214
250
  state.snapshot.status = "starting";
@@ -226,6 +262,45 @@ class DaemonProcessManager {
226
262
  this.clearTimeoutFn(state.restartTimer);
227
263
  state.restartTimer = null;
228
264
  }
265
+ scheduleCooldownRecovery(state) {
266
+ if (state.cooldownRetryCount >= this.maxCooldownRetries) {
267
+ (0, runtime_1.emitNervesEvent)({
268
+ level: "error",
269
+ component: "daemon",
270
+ event: "daemon.agent_permanent_failure",
271
+ message: "managed agent exhausted all cooldown retries — permanently stopped",
272
+ meta: { agent: state.config.name, cooldownRetryCount: state.cooldownRetryCount, maxCooldownRetries: this.maxCooldownRetries },
273
+ });
274
+ return;
275
+ }
276
+ this.clearCooldownTimer(state);
277
+ state.cooldownTimer = this.setTimeoutFn(() => {
278
+ state.cooldownRetryCount += 1;
279
+ state.crashTimestamps = [];
280
+ state.snapshot.backoffMs = this.initialBackoffMs;
281
+ state.snapshot.status = "starting";
282
+ state.snapshot.restartCount += 1;
283
+ (0, runtime_1.emitNervesEvent)({
284
+ component: "daemon",
285
+ event: "daemon.agent_cooldown_recovery",
286
+ message: "attempting cooldown recovery for managed agent",
287
+ meta: { agent: state.config.name, cooldownRetryCount: state.cooldownRetryCount },
288
+ });
289
+ void this.startAgent(state.config.name);
290
+ }, this.cooldownRecoveryMs);
291
+ (0, runtime_1.emitNervesEvent)({
292
+ component: "daemon",
293
+ event: "daemon.agent_cooldown_scheduled",
294
+ message: `scheduled cooldown recovery in ${this.cooldownRecoveryMs}ms`,
295
+ meta: { agent: state.config.name, cooldownRecoveryMs: this.cooldownRecoveryMs, cooldownRetryCount: state.cooldownRetryCount },
296
+ });
297
+ }
298
+ clearCooldownTimer(state) {
299
+ if (state.cooldownTimer === null)
300
+ return;
301
+ this.clearTimeoutFn(state.cooldownTimer);
302
+ state.cooldownTimer = null;
303
+ }
229
304
  requireAgent(agent) {
230
305
  const state = this.agents.get(agent);
231
306
  if (!state) {
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runHooks = runHooks;
4
+ const runtime_1 = require("../../nerves/runtime");
5
+ const bundle_meta_1 = require("./hooks/bundle-meta");
6
+ const agent_config_v2_1 = require("./hooks/agent-config-v2");
7
+ async function runHooks(deps) {
8
+ (0, runtime_1.emitNervesEvent)({
9
+ component: "daemon",
10
+ event: "daemon.run_hooks_start",
11
+ message: "running update hooks",
12
+ meta: { bundlesRoot: deps.bundlesRoot },
13
+ });
14
+ try {
15
+ deps.registerUpdateHook(bundle_meta_1.bundleMetaHook);
16
+ deps.registerUpdateHook(agent_config_v2_1.agentConfigV2Hook);
17
+ const currentVersion = deps.getPackageVersion();
18
+ await deps.applyPendingUpdates(deps.bundlesRoot, currentVersion);
19
+ (0, runtime_1.emitNervesEvent)({
20
+ component: "daemon",
21
+ event: "daemon.run_hooks_success",
22
+ message: "update hooks completed successfully",
23
+ meta: { bundlesRoot: deps.bundlesRoot },
24
+ });
25
+ return 0;
26
+ }
27
+ catch (err) {
28
+ (0, runtime_1.emitNervesEvent)({
29
+ component: "daemon",
30
+ event: "daemon.run_hooks_error",
31
+ message: "update hooks failed",
32
+ meta: {
33
+ bundlesRoot: deps.bundlesRoot,
34
+ error: err instanceof Error ? err.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(err),
35
+ },
36
+ });
37
+ return 1;
38
+ }
39
+ }
@@ -35,52 +35,95 @@ 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);
79
122
  const sinks = config.sinks.map((sinkName) => {
80
123
  if (sinkName === "terminal") {
81
124
  return (0, nerves_1.createTerminalSink)();
82
125
  }
83
- const ndjsonPath = path.join(homeDir, ".agentstate", "daemon", "logs", `${processName}.ndjson`);
126
+ const ndjsonPath = path.join(logsDir, `${processName}.ndjson`);
84
127
  return (0, nerves_1.createNdjsonFileSink)(ndjsonPath);
85
128
  });
86
129
  const logger = (0, nerves_1.createLogger)({