@ouro.bot/cli 0.1.0-alpha.32 → 0.1.0-alpha.320

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 (303) hide show
  1. package/README.md +188 -190
  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 +1917 -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 +456 -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 +63 -30
  34. package/dist/heart/core.js +669 -195
  35. package/dist/heart/cross-chat-delivery.js +131 -0
  36. package/dist/heart/daemon/agent-config-check.js +149 -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/cadence.js +70 -0
  41. package/dist/heart/daemon/cli-defaults.js +596 -0
  42. package/dist/heart/daemon/cli-exec.js +2238 -0
  43. package/dist/heart/daemon/cli-help.js +306 -0
  44. package/dist/heart/daemon/cli-parse.js +824 -0
  45. package/dist/heart/daemon/cli-render-doctor.js +57 -0
  46. package/dist/heart/daemon/cli-render.js +506 -0
  47. package/dist/heart/daemon/cli-types.js +8 -0
  48. package/dist/heart/daemon/daemon-cli.js +29 -1171
  49. package/dist/heart/daemon/daemon-entry.js +333 -3
  50. package/dist/heart/daemon/daemon-health.js +137 -0
  51. package/dist/heart/daemon/daemon-runtime-sync.js +153 -12
  52. package/dist/heart/daemon/daemon-tombstone.js +236 -0
  53. package/dist/heart/daemon/daemon.js +751 -58
  54. package/dist/heart/daemon/doctor-types.js +8 -0
  55. package/dist/heart/daemon/doctor.js +322 -0
  56. package/dist/heart/daemon/health-monitor.js +66 -0
  57. package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
  58. package/dist/heart/daemon/hooks/bundle-meta.js +115 -1
  59. package/dist/heart/daemon/http-health-probe.js +80 -0
  60. package/dist/heart/daemon/inner-status.js +89 -0
  61. package/dist/heart/daemon/interactive-repair.js +69 -0
  62. package/dist/heart/daemon/launchd.js +46 -9
  63. package/dist/heart/daemon/log-tailer.js +82 -12
  64. package/dist/heart/daemon/logs-prune.js +105 -0
  65. package/dist/heart/daemon/message-router.js +17 -8
  66. package/dist/heart/daemon/os-cron-deps.js +134 -0
  67. package/dist/heart/daemon/ouro-bot-entry.js +1 -1
  68. package/dist/heart/daemon/process-manager.js +201 -0
  69. package/dist/heart/daemon/provider-discovery.js +105 -0
  70. package/dist/heart/daemon/pulse.js +463 -0
  71. package/dist/heart/daemon/run-hooks.js +2 -0
  72. package/dist/heart/daemon/runtime-logging.js +67 -16
  73. package/dist/heart/daemon/runtime-metadata.js +101 -0
  74. package/dist/heart/daemon/runtime-mode.js +67 -0
  75. package/dist/heart/daemon/safe-mode.js +161 -0
  76. package/dist/heart/daemon/sense-manager.js +72 -3
  77. package/dist/heart/daemon/session-id-resolver.js +131 -0
  78. package/dist/heart/daemon/skill-management-installer.js +94 -0
  79. package/dist/heart/daemon/socket-client.js +307 -0
  80. package/dist/heart/daemon/stale-bundle-prune.js +96 -0
  81. package/dist/heart/daemon/startup-tui.js +227 -0
  82. package/dist/heart/daemon/task-scheduler.js +3 -25
  83. package/dist/heart/daemon/thoughts.js +510 -0
  84. package/dist/heart/daemon/up-progress.js +135 -0
  85. package/dist/heart/delegation.js +62 -0
  86. package/dist/heart/habits/habit-migration.js +181 -0
  87. package/dist/heart/habits/habit-parser.js +140 -0
  88. package/dist/heart/habits/habit-scheduler.js +371 -0
  89. package/dist/heart/{daemon → hatch}/hatch-flow.js +30 -120
  90. package/dist/heart/{daemon → hatch}/hatch-specialist.js +3 -3
  91. package/dist/heart/{daemon → hatch}/specialist-prompt.js +10 -7
  92. package/dist/heart/{daemon → hatch}/specialist-tools.js +49 -3
  93. package/dist/heart/identity.js +163 -60
  94. package/dist/heart/kicks.js +2 -20
  95. package/dist/heart/mcp/mcp-server.js +653 -0
  96. package/dist/heart/migrate-config.js +127 -0
  97. package/dist/heart/model-capabilities.js +59 -0
  98. package/dist/heart/outlook/outlook-http.js +439 -0
  99. package/dist/heart/outlook/outlook-read.js +1595 -0
  100. package/dist/heart/outlook/outlook-render.js +1032 -0
  101. package/dist/heart/outlook/outlook-types.js +27 -0
  102. package/dist/heart/outlook/outlook-view.js +194 -0
  103. package/dist/heart/progress-story.js +42 -0
  104. package/dist/heart/provider-failover.js +88 -0
  105. package/dist/heart/provider-ping.js +162 -0
  106. package/dist/heart/providers/anthropic-token.js +163 -0
  107. package/dist/heart/providers/anthropic.js +169 -46
  108. package/dist/heart/providers/azure.js +98 -11
  109. package/dist/heart/providers/error-classification.js +63 -0
  110. package/dist/heart/providers/github-copilot.js +136 -0
  111. package/dist/heart/providers/minimax-vlm.js +189 -0
  112. package/dist/heart/providers/minimax.js +23 -5
  113. package/dist/heart/providers/openai-codex.js +33 -22
  114. package/dist/heart/session-activity.js +190 -0
  115. package/dist/heart/session-events.js +726 -0
  116. package/dist/heart/session-recall.js +162 -0
  117. package/dist/heart/start-of-turn-packet.js +341 -0
  118. package/dist/heart/streaming.js +36 -27
  119. package/dist/heart/sync.js +332 -0
  120. package/dist/heart/target-resolution.js +127 -0
  121. package/dist/heart/tempo.js +93 -0
  122. package/dist/heart/temporal-view.js +41 -0
  123. package/dist/heart/tool-activity-callbacks.js +36 -0
  124. package/dist/heart/tool-description.js +135 -0
  125. package/dist/heart/tool-friction.js +55 -0
  126. package/dist/heart/tool-loop.js +200 -0
  127. package/dist/heart/turn-context.js +358 -0
  128. package/dist/heart/turn-coordinator.js +28 -0
  129. package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +1 -1
  130. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
  131. package/dist/heart/{daemon → versioning}/ouro-path-installer.js +78 -35
  132. package/dist/heart/versioning/ouro-version-manager.js +295 -0
  133. package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
  134. package/dist/heart/{daemon → versioning}/update-checker.js +12 -2
  135. package/dist/heart/{daemon → versioning}/update-hooks.js +63 -59
  136. package/dist/mind/associative-recall.js +137 -66
  137. package/dist/mind/bundle-manifest.js +8 -1
  138. package/dist/mind/context.js +89 -93
  139. package/dist/mind/diary-integrity.js +60 -0
  140. package/dist/mind/{memory.js → diary.js} +84 -96
  141. package/dist/mind/embedding-provider.js +60 -0
  142. package/dist/mind/file-state.js +179 -0
  143. package/dist/mind/first-impressions.js +14 -1
  144. package/dist/mind/friends/channel.js +56 -0
  145. package/dist/mind/friends/group-context.js +144 -0
  146. package/dist/mind/friends/resolver.js +37 -0
  147. package/dist/mind/friends/store-file.js +58 -3
  148. package/dist/mind/friends/trust-explanation.js +74 -0
  149. package/dist/mind/friends/types.js +8 -0
  150. package/dist/mind/journal-index.js +161 -0
  151. package/dist/mind/obligation-steering.js +221 -0
  152. package/dist/mind/pending.js +76 -9
  153. package/dist/mind/prompt.js +950 -113
  154. package/dist/mind/provenance-trust.js +26 -0
  155. package/dist/mind/scrutiny.js +173 -0
  156. package/dist/mind/token-estimate.js +8 -12
  157. package/dist/nerves/cli-logging.js +7 -1
  158. package/dist/nerves/coverage/audit.js +1 -1
  159. package/dist/nerves/coverage/file-completeness.js +76 -5
  160. package/dist/nerves/coverage/run-artifacts.js +1 -1
  161. package/dist/nerves/event-buffer.js +111 -0
  162. package/dist/nerves/index.js +224 -4
  163. package/dist/nerves/observation.js +20 -0
  164. package/dist/nerves/redact.js +79 -0
  165. package/dist/nerves/runtime.js +5 -1
  166. package/dist/outlook-ui/assets/index-IuR4F6y6.js +61 -0
  167. package/dist/outlook-ui/assets/index-LwChZTgL.css +1 -0
  168. package/dist/outlook-ui/index.html +15 -0
  169. package/dist/repertoire/ado-client.js +15 -56
  170. package/dist/repertoire/ado-semantic.js +11 -10
  171. package/dist/repertoire/api-client.js +97 -0
  172. package/dist/repertoire/bitwarden-store.js +319 -0
  173. package/dist/repertoire/bundle-templates.js +72 -0
  174. package/dist/repertoire/bw-installer.js +79 -0
  175. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  176. package/dist/repertoire/coding/context-pack.js +330 -0
  177. package/dist/repertoire/coding/feedback.js +197 -30
  178. package/dist/repertoire/coding/manager.js +159 -11
  179. package/dist/repertoire/coding/spawner.js +55 -9
  180. package/dist/repertoire/coding/tools.js +170 -7
  181. package/dist/repertoire/commerce-errors.js +109 -0
  182. package/dist/repertoire/commerce-self-test.js +156 -0
  183. package/dist/repertoire/credential-access.js +527 -0
  184. package/dist/repertoire/duffel-client.js +185 -0
  185. package/dist/repertoire/github-client.js +14 -55
  186. package/dist/repertoire/graph-client.js +11 -52
  187. package/dist/repertoire/guardrails.js +375 -0
  188. package/dist/repertoire/mcp-client.js +255 -0
  189. package/dist/repertoire/mcp-manager.js +305 -0
  190. package/dist/repertoire/mcp-tools.js +63 -0
  191. package/dist/repertoire/shell-sessions.js +133 -0
  192. package/dist/repertoire/skills.js +14 -23
  193. package/dist/repertoire/stripe-client.js +131 -0
  194. package/dist/repertoire/tasks/board.js +43 -5
  195. package/dist/repertoire/tasks/fix.js +182 -0
  196. package/dist/repertoire/tasks/index.js +28 -10
  197. package/dist/repertoire/tasks/lifecycle.js +2 -2
  198. package/dist/repertoire/tasks/parser.js +3 -2
  199. package/dist/repertoire/tasks/scanner.js +194 -37
  200. package/dist/repertoire/tasks/transitions.js +16 -79
  201. package/dist/repertoire/tool-results.js +29 -0
  202. package/dist/repertoire/tools-attachments.js +316 -0
  203. package/dist/repertoire/tools-base.js +45 -771
  204. package/dist/repertoire/tools-bluebubbles.js +1 -0
  205. package/dist/repertoire/tools-bridge.js +141 -0
  206. package/dist/repertoire/tools-bundle.js +984 -0
  207. package/dist/repertoire/tools-config.js +185 -0
  208. package/dist/repertoire/tools-continuity.js +248 -0
  209. package/dist/repertoire/tools-credential.js +182 -0
  210. package/dist/repertoire/tools-files.js +342 -0
  211. package/dist/repertoire/tools-flight.js +224 -0
  212. package/dist/repertoire/tools-flow.js +105 -0
  213. package/dist/repertoire/tools-github.js +1 -7
  214. package/dist/repertoire/tools-memory.js +376 -0
  215. package/dist/repertoire/tools-session.js +739 -0
  216. package/dist/repertoire/tools-shell.js +120 -0
  217. package/dist/repertoire/tools-stripe.js +180 -0
  218. package/dist/repertoire/tools-surface.js +243 -0
  219. package/dist/repertoire/tools-teams.js +12 -62
  220. package/dist/repertoire/tools-travel.js +125 -0
  221. package/dist/repertoire/tools-user-profile.js +144 -0
  222. package/dist/repertoire/tools-vault.js +110 -0
  223. package/dist/repertoire/tools.js +144 -138
  224. package/dist/repertoire/travel-api-client.js +360 -0
  225. package/dist/repertoire/user-profile.js +118 -0
  226. package/dist/repertoire/vault-setup.js +241 -0
  227. package/dist/scripts/claude-code-hook.js +41 -0
  228. package/dist/scripts/claude-code-stop-hook.js +47 -0
  229. package/dist/senses/attention-queue.js +116 -0
  230. package/dist/senses/bluebubbles/attachment-cache.js +53 -0
  231. package/dist/senses/bluebubbles/attachment-download.js +137 -0
  232. package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +143 -9
  233. package/dist/senses/bluebubbles/entry.js +13 -0
  234. package/dist/senses/bluebubbles/inbound-log.js +113 -0
  235. package/dist/senses/bluebubbles/index.js +1436 -0
  236. package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -70
  237. package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +43 -12
  238. package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +46 -6
  239. package/dist/senses/bluebubbles/replay.js +129 -0
  240. package/dist/senses/bluebubbles/runtime-state.js +109 -0
  241. package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
  242. package/dist/senses/cli/bracketed-paste.js +82 -0
  243. package/dist/senses/cli/image-paste.js +287 -0
  244. package/dist/senses/cli/image-ref-navigation.js +75 -0
  245. package/dist/senses/cli/ink-app.js +156 -0
  246. package/dist/senses/cli/inline-diff.js +64 -0
  247. package/dist/senses/cli/input-keys.js +174 -0
  248. package/dist/senses/cli/kill-ring.js +86 -0
  249. package/dist/senses/cli/message-list.js +51 -0
  250. package/dist/senses/cli/ouro-tui.js +605 -0
  251. package/dist/senses/cli/spinner-imperative.js +135 -0
  252. package/dist/senses/cli/spinner.js +101 -0
  253. package/dist/senses/cli/status-line.js +60 -0
  254. package/dist/senses/cli/streaming-markdown.js +526 -0
  255. package/dist/senses/cli/tool-display.js +83 -0
  256. package/dist/senses/cli/tool-render.js +85 -0
  257. package/dist/senses/cli/tui-store.js +240 -0
  258. package/dist/senses/cli/virtual-list.js +35 -0
  259. package/dist/senses/cli-entry.js +1 -1
  260. package/dist/senses/cli-layout.js +187 -0
  261. package/dist/senses/cli.js +595 -246
  262. package/dist/senses/commands.js +65 -1
  263. package/dist/senses/continuity.js +94 -0
  264. package/dist/senses/habit-turn-message.js +108 -0
  265. package/dist/senses/inner-dialog-worker.js +112 -19
  266. package/dist/senses/inner-dialog.js +633 -86
  267. package/dist/senses/pipeline.js +565 -0
  268. package/dist/senses/shared-turn.js +199 -0
  269. package/dist/senses/surface-tool.js +68 -0
  270. package/dist/senses/teams.js +666 -166
  271. package/dist/senses/trust-gate.js +112 -2
  272. package/package.json +27 -7
  273. package/skills/agent-commerce.md +106 -0
  274. package/skills/browser-navigation.md +110 -0
  275. package/skills/commerce-setup-guide.md +116 -0
  276. package/skills/commerce-setup.md +84 -0
  277. package/skills/configure-dev-tools.md +81 -0
  278. package/skills/travel-planning.md +138 -0
  279. package/dist/heart/daemon/subagent-installer.js +0 -134
  280. package/dist/senses/bluebubbles-entry.js +0 -11
  281. package/dist/senses/bluebubbles.js +0 -544
  282. package/dist/senses/debug-activity.js +0 -108
  283. package/subagents/README.md +0 -73
  284. package/subagents/work-doer.md +0 -235
  285. package/subagents/work-merger.md +0 -618
  286. package/subagents/work-planner.md +0 -382
  287. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  288. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  289. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  290. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  291. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  292. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  293. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  294. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  295. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  296. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  297. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  298. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +0 -0
  299. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  300. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  301. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  302. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
  303. /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ /**
3
+ * Type definitions for the `ouro doctor` system health check.
4
+ *
5
+ * Describes the structure of health check results: individual checks,
6
+ * grouped categories, and the aggregated result.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,322 @@
1
+ "use strict";
2
+ /**
3
+ * System health check ("ouro doctor") — runs all diagnostic categories
4
+ * and aggregates results into a structured DoctorResult.
5
+ *
6
+ * Each category checker is isolated: if one throws, it produces a single
7
+ * "fail" check and the remaining categories still run.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.checkDaemon = checkDaemon;
11
+ exports.checkAgents = checkAgents;
12
+ exports.checkSenses = checkSenses;
13
+ exports.checkHabits = checkHabits;
14
+ exports.checkSecurity = checkSecurity;
15
+ exports.checkDisk = checkDisk;
16
+ exports.runDoctorChecks = runDoctorChecks;
17
+ const runtime_1 = require("../../nerves/runtime");
18
+ // ── Category checkers ──
19
+ async function checkDaemon(deps) {
20
+ const checks = [];
21
+ const socketExists = deps.existsSync(deps.socketPath);
22
+ checks.push({
23
+ label: "daemon socket exists",
24
+ status: socketExists ? "pass" : "fail",
25
+ detail: socketExists ? deps.socketPath : `not found at ${deps.socketPath}`,
26
+ });
27
+ if (socketExists) {
28
+ const alive = await deps.checkSocketAlive(deps.socketPath);
29
+ checks.push({
30
+ label: "daemon is responsive",
31
+ status: alive ? "pass" : "fail",
32
+ detail: alive ? "socket responded" : "socket exists but daemon unresponsive",
33
+ });
34
+ }
35
+ else {
36
+ checks.push({
37
+ label: "daemon is responsive",
38
+ status: "fail",
39
+ detail: "skipped — socket missing",
40
+ });
41
+ }
42
+ return { name: "Daemon", checks };
43
+ }
44
+ /** Discover all *.ouro directories under bundlesRoot. */
45
+ function discoverAgents(deps) {
46
+ if (!deps.existsSync(deps.bundlesRoot))
47
+ return [];
48
+ return deps.readdirSync(deps.bundlesRoot).filter((name) => name.endsWith(".ouro"));
49
+ }
50
+ function checkAgents(deps) {
51
+ const checks = [];
52
+ if (!deps.existsSync(deps.bundlesRoot)) {
53
+ checks.push({ label: "bundles directory", status: "fail", detail: `${deps.bundlesRoot} not found` });
54
+ return { name: "Agents", checks };
55
+ }
56
+ const agents = discoverAgents(deps);
57
+ if (agents.length === 0) {
58
+ checks.push({ label: "agent bundles", status: "warn", detail: "no *.ouro bundles found" });
59
+ return { name: "Agents", checks };
60
+ }
61
+ for (const agentDir of agents) {
62
+ const agentPath = `${deps.bundlesRoot}/${agentDir}`;
63
+ const configPath = `${agentPath}/agent.json`;
64
+ if (!deps.existsSync(configPath)) {
65
+ checks.push({ label: `${agentDir}/agent.json`, status: "fail", detail: "missing" });
66
+ continue;
67
+ }
68
+ let config;
69
+ try {
70
+ config = JSON.parse(deps.readFileSync(configPath));
71
+ }
72
+ catch {
73
+ checks.push({ label: `${agentDir}/agent.json`, status: "fail", detail: "unparseable JSON" });
74
+ continue;
75
+ }
76
+ const missing = [];
77
+ if (!config.version)
78
+ missing.push("version");
79
+ if (!config.humanFacing || typeof config.humanFacing !== "object") {
80
+ missing.push("humanFacing");
81
+ }
82
+ else {
83
+ const hf = config.humanFacing;
84
+ if (!hf.provider)
85
+ missing.push("humanFacing.provider");
86
+ if (!hf.model)
87
+ missing.push("humanFacing.model");
88
+ }
89
+ if (!config.agentFacing || typeof config.agentFacing !== "object") {
90
+ missing.push("agentFacing");
91
+ }
92
+ else {
93
+ const af = config.agentFacing;
94
+ if (!af.provider)
95
+ missing.push("agentFacing.provider");
96
+ if (!af.model)
97
+ missing.push("agentFacing.model");
98
+ }
99
+ if (missing.length > 0) {
100
+ checks.push({ label: `${agentDir}/agent.json`, status: "warn", detail: `missing fields: ${missing.join(", ")}` });
101
+ }
102
+ else {
103
+ checks.push({ label: `${agentDir}/agent.json`, status: "pass", detail: "valid" });
104
+ }
105
+ }
106
+ return { name: "Agents", checks };
107
+ }
108
+ function checkSenses(deps) {
109
+ const checks = [];
110
+ const agents = discoverAgents(deps);
111
+ for (const agentDir of agents) {
112
+ const configPath = `${deps.bundlesRoot}/${agentDir}/agent.json`;
113
+ if (!deps.existsSync(configPath))
114
+ continue;
115
+ let config;
116
+ try {
117
+ config = JSON.parse(deps.readFileSync(configPath));
118
+ }
119
+ catch {
120
+ checks.push({ label: `${agentDir} senses`, status: "fail", detail: "agent.json unparseable" });
121
+ continue;
122
+ }
123
+ if (!config.senses || typeof config.senses !== "object") {
124
+ checks.push({ label: `${agentDir} senses`, status: "warn", detail: "no senses config block" });
125
+ continue;
126
+ }
127
+ const senses = config.senses;
128
+ const senseNames = ["cli", "teams", "bluebubbles"];
129
+ for (const sense of senseNames) {
130
+ if (!(sense in senses))
131
+ continue;
132
+ const entry = senses[sense];
133
+ if (!entry || typeof entry !== "object") {
134
+ checks.push({ label: `${agentDir} ${sense}`, status: "fail", detail: "malformed sense entry" });
135
+ continue;
136
+ }
137
+ const senseObj = entry;
138
+ if (typeof senseObj.enabled !== "boolean") {
139
+ checks.push({ label: `${agentDir} ${sense}`, status: "warn", detail: "missing enabled boolean" });
140
+ }
141
+ else {
142
+ checks.push({
143
+ label: `${agentDir} ${sense}`,
144
+ status: "pass",
145
+ detail: senseObj.enabled ? "enabled" : "disabled",
146
+ });
147
+ }
148
+ }
149
+ }
150
+ if (checks.length === 0) {
151
+ checks.push({ label: "senses", status: "warn", detail: "no agents with senses config found" });
152
+ }
153
+ return { name: "Senses", checks };
154
+ }
155
+ function checkHabits(deps) {
156
+ const checks = [];
157
+ const agents = discoverAgents(deps);
158
+ for (const agentDir of agents) {
159
+ const agentName = agentDir.replace(/\.ouro$/, "");
160
+ const habitsDir = `${deps.bundlesRoot}/${agentDir}/habits`;
161
+ if (!deps.existsSync(habitsDir)) {
162
+ checks.push({ label: `${agentDir} habits dir`, status: "warn", detail: "no habits directory" });
163
+ continue;
164
+ }
165
+ checks.push({ label: `${agentDir} habits dir`, status: "pass", detail: habitsDir });
166
+ // Check for launchd plists on macOS
167
+ const launchAgentsDir = `${deps.homedir}/Library/LaunchAgents`;
168
+ if (deps.existsSync(launchAgentsDir)) {
169
+ const plists = deps.readdirSync(launchAgentsDir).filter((f) => f.startsWith(`bot.ouro.${agentName}.`) && f.endsWith(".plist"));
170
+ if (plists.length > 0) {
171
+ checks.push({ label: `${agentDir} launchd plists`, status: "pass", detail: `${plists.length} plist(s)` });
172
+ }
173
+ else {
174
+ checks.push({ label: `${agentDir} launchd plists`, status: "fail", detail: "no matching plists in LaunchAgents" });
175
+ }
176
+ }
177
+ }
178
+ if (checks.length === 0) {
179
+ checks.push({ label: "habits", status: "warn", detail: "no agents found" });
180
+ }
181
+ return { name: "Habits", checks };
182
+ }
183
+ function checkSecurity(deps) {
184
+ const checks = [];
185
+ const agents = discoverAgents(deps);
186
+ for (const agentDir of agents) {
187
+ const agentName = agentDir.replace(/\.ouro$/, "");
188
+ const secretsPath = `${deps.secretsRoot}/${agentName}/secrets.json`;
189
+ if (!deps.existsSync(secretsPath)) {
190
+ checks.push({ label: `${agentDir} secrets.json`, status: "fail", detail: "missing" });
191
+ continue;
192
+ }
193
+ // Check file permissions
194
+ const stat = deps.statSync(secretsPath);
195
+ const worldReadable = (stat.mode & 0o004) !== 0;
196
+ if (worldReadable) {
197
+ checks.push({ label: `${agentDir} secrets.json perms`, status: "warn", detail: "world-readable — consider chmod 600" });
198
+ }
199
+ else {
200
+ checks.push({ label: `${agentDir} secrets.json perms`, status: "pass", detail: "not world-readable" });
201
+ }
202
+ // Check agent.json for leaked credential keys
203
+ const configPath = `${deps.bundlesRoot}/${agentDir}/agent.json`;
204
+ if (deps.existsSync(configPath)) {
205
+ try {
206
+ const raw = deps.readFileSync(configPath);
207
+ const sensitiveKeys = ["apiKey", "token", "secret", "password"];
208
+ const found = sensitiveKeys.filter((key) => raw.includes(`"${key}"`));
209
+ if (found.length > 0) {
210
+ checks.push({ label: `${agentDir} credential leak`, status: "warn", detail: `agent.json contains keys: ${found.join(", ")}` });
211
+ }
212
+ else {
213
+ checks.push({ label: `${agentDir} credential leak`, status: "pass", detail: "no credential keys in agent.json" });
214
+ }
215
+ }
216
+ catch {
217
+ checks.push({ label: `${agentDir} credential leak`, status: "fail", detail: "could not read agent.json" });
218
+ }
219
+ }
220
+ }
221
+ if (checks.length === 0) {
222
+ checks.push({ label: "security", status: "warn", detail: "no agents found" });
223
+ }
224
+ return { name: "Security", checks };
225
+ }
226
+ function checkDisk(deps) {
227
+ const checks = [];
228
+ // Check daemon logs directory
229
+ const logsDir = `${deps.homedir}/.ouro-cli/logs`;
230
+ if (!deps.existsSync(logsDir)) {
231
+ checks.push({ label: "daemon logs dir", status: "warn", detail: `${logsDir} not found` });
232
+ }
233
+ else {
234
+ let totalSize = 0;
235
+ try {
236
+ const files = deps.readdirSync(logsDir);
237
+ for (const file of files) {
238
+ try {
239
+ const stat = deps.statSync(`${logsDir}/${file}`);
240
+ totalSize += stat.size;
241
+ }
242
+ catch {
243
+ // skip unreadable files
244
+ }
245
+ }
246
+ }
247
+ catch {
248
+ // readdirSync failure handled below
249
+ }
250
+ const sizeMB = totalSize / (1024 * 1024);
251
+ if (sizeMB > 500) {
252
+ checks.push({ label: "daemon log size", status: "fail", detail: `${sizeMB.toFixed(1)}MB — exceeds 500MB limit` });
253
+ }
254
+ else if (sizeMB > 100) {
255
+ checks.push({ label: "daemon log size", status: "warn", detail: `${sizeMB.toFixed(1)}MB — consider pruning with \`ouro logs prune\`` });
256
+ }
257
+ else {
258
+ checks.push({ label: "daemon log size", status: "pass", detail: `${sizeMB.toFixed(1)}MB` });
259
+ }
260
+ }
261
+ // Check AgentBundles root
262
+ if (deps.existsSync(deps.bundlesRoot)) {
263
+ checks.push({ label: "bundles root", status: "pass", detail: deps.bundlesRoot });
264
+ }
265
+ else {
266
+ checks.push({ label: "bundles root", status: "warn", detail: `${deps.bundlesRoot} not found` });
267
+ }
268
+ return { name: "Disk", checks };
269
+ }
270
+ // ── Orchestrator ──
271
+ function computeSummary(categories) {
272
+ let passed = 0;
273
+ let warnings = 0;
274
+ let failed = 0;
275
+ for (const cat of categories) {
276
+ for (const check of cat.checks) {
277
+ /* v8 ignore next 3 -- all three branches tested; v8 misreports compound if/else-if chain @preserve */
278
+ if (check.status === "pass")
279
+ passed++;
280
+ else if (check.status === "warn")
281
+ warnings++;
282
+ else
283
+ failed++;
284
+ }
285
+ }
286
+ return { passed, warnings, failed };
287
+ }
288
+ const CATEGORY_CHECKERS = [
289
+ { name: "Daemon", fn: checkDaemon },
290
+ { name: "Agents", fn: checkAgents },
291
+ { name: "Senses", fn: checkSenses },
292
+ { name: "Habits", fn: checkHabits },
293
+ { name: "Security", fn: checkSecurity },
294
+ { name: "Disk", fn: checkDisk },
295
+ ];
296
+ async function runDoctorChecks(deps) {
297
+ const categories = [];
298
+ for (const checker of CATEGORY_CHECKERS) {
299
+ try {
300
+ const category = await Promise.resolve(checker.fn(deps));
301
+ categories.push(category);
302
+ }
303
+ catch (error) {
304
+ (0, runtime_1.emitNervesEvent)({
305
+ level: "warn",
306
+ component: "daemon",
307
+ event: "daemon.doctor_check_error",
308
+ message: `doctor check ${checker.name} failed`,
309
+ meta: { category: checker.name, error: error instanceof Error ? error.message : String(error) },
310
+ });
311
+ categories.push({
312
+ name: checker.name,
313
+ checks: [{
314
+ label: checker.name.toLowerCase(),
315
+ status: "fail",
316
+ detail: `check crashed: ${error instanceof Error ? error.message : String(error)}`,
317
+ }],
318
+ });
319
+ }
320
+ }
321
+ return { categories, summary: computeSummary(categories) };
322
+ }
@@ -7,11 +7,36 @@ class HealthMonitor {
7
7
  scheduler;
8
8
  alertSink;
9
9
  diskUsagePercent;
10
+ onCriticalAgent;
11
+ senseProbes;
12
+ intervalHandle = null;
10
13
  constructor(options) {
11
14
  this.processManager = options.processManager;
12
15
  this.scheduler = options.scheduler;
13
16
  this.alertSink = options.alertSink ?? (() => undefined);
14
17
  this.diskUsagePercent = options.diskUsagePercent ?? (() => 0);
18
+ this.onCriticalAgent = options.onCriticalAgent ?? (() => undefined);
19
+ this.senseProbes = options.senseProbes ?? [];
20
+ }
21
+ startPeriodicChecks(intervalMs) {
22
+ if (this.intervalHandle !== null)
23
+ return;
24
+ (0, runtime_1.emitNervesEvent)({
25
+ level: "info",
26
+ component: "daemon",
27
+ event: "daemon.health_check_scheduled",
28
+ message: "periodic health checks started",
29
+ meta: { intervalMs },
30
+ });
31
+ this.intervalHandle = setInterval(() => {
32
+ void this.runChecks();
33
+ }, intervalMs);
34
+ }
35
+ stopPeriodicChecks() {
36
+ if (this.intervalHandle === null)
37
+ return;
38
+ clearInterval(this.intervalHandle);
39
+ this.intervalHandle = null;
15
40
  }
16
41
  async runChecks() {
17
42
  const results = [];
@@ -23,6 +48,21 @@ class HealthMonitor {
23
48
  status: "critical",
24
49
  message: `non-running agents: ${unhealthy.map((item) => item.name).join(", ")}`,
25
50
  });
51
+ for (const agent of unhealthy) {
52
+ try {
53
+ (0, runtime_1.emitNervesEvent)({
54
+ level: "warn",
55
+ component: "daemon",
56
+ event: "daemon.health_check_recovery_attempted",
57
+ message: "triggering recovery restart for non-running agent",
58
+ meta: { agentName: agent.name, agentStatus: agent.status },
59
+ });
60
+ this.onCriticalAgent(agent.name);
61
+ }
62
+ catch {
63
+ // Recovery is best-effort -- callback errors must not crash runChecks
64
+ }
65
+ }
26
66
  }
27
67
  else {
28
68
  results.push({ name: "agent-processes", status: "ok", message: "all managed agents running" });
@@ -61,6 +101,32 @@ class HealthMonitor {
61
101
  message: `disk usage healthy (${diskPercent}%)`,
62
102
  });
63
103
  }
104
+ for (const probe of this.senseProbes) {
105
+ try {
106
+ const outcome = await probe.check();
107
+ if (outcome.ok) {
108
+ results.push({
109
+ name: `sense-probe:${probe.name}`,
110
+ status: "ok",
111
+ message: `${probe.name} healthy`,
112
+ });
113
+ }
114
+ else {
115
+ results.push({
116
+ name: `sense-probe:${probe.name}`,
117
+ status: "critical",
118
+ message: `${probe.name} failed: ${outcome.detail ?? "unknown"}`,
119
+ });
120
+ }
121
+ }
122
+ catch (error) {
123
+ results.push({
124
+ name: `sense-probe:${probe.name}`,
125
+ status: "critical",
126
+ message: `${probe.name} error: ${error instanceof Error ? error.message : String(error)}`,
127
+ });
128
+ }
129
+ }
64
130
  for (const result of results) {
65
131
  (0, runtime_1.emitNervesEvent)({
66
132
  level: result.status === "critical" ? "error" : result.status === "warn" ? "warn" : "info",
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.agentConfigV2Hook = agentConfigV2Hook;
4
+ const runtime_1 = require("../../../nerves/runtime");
5
+ const migrate_config_1 = require("../../migrate-config");
6
+ async function agentConfigV2Hook(ctx) {
7
+ (0, runtime_1.emitNervesEvent)({
8
+ component: "daemon",
9
+ event: "daemon.agent_config_v2_hook_start",
10
+ message: "running agent-config-v2 update hook",
11
+ meta: { agentRoot: ctx.agentRoot, currentVersion: ctx.currentVersion },
12
+ });
13
+ try {
14
+ (0, migrate_config_1.migrateAgentConfigV1ToV2)(ctx.agentRoot);
15
+ }
16
+ catch (err) {
17
+ const errorMessage = err instanceof Error ? err.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(err);
18
+ (0, runtime_1.emitNervesEvent)({
19
+ component: "daemon",
20
+ event: "daemon.agent_config_v2_hook_error",
21
+ message: "agent-config-v2 hook migration failed",
22
+ meta: { agentRoot: ctx.agentRoot, error: errorMessage },
23
+ });
24
+ return { ok: false, error: errorMessage };
25
+ }
26
+ (0, runtime_1.emitNervesEvent)({
27
+ component: "daemon",
28
+ event: "daemon.agent_config_v2_hook_end",
29
+ message: "agent-config-v2 hook completed",
30
+ meta: { agentRoot: ctx.agentRoot },
31
+ });
32
+ return { ok: true };
33
+ }
@@ -37,6 +37,115 @@ exports.bundleMetaHook = bundleMetaHook;
37
37
  const fs = __importStar(require("fs"));
38
38
  const path = __importStar(require("path"));
39
39
  const runtime_1 = require("../../../nerves/runtime");
40
+ /**
41
+ * Migrate bundle from schema 1 to schema 2:
42
+ * - Move state/{episodes,obligations,cares,intentions}/* to arc/{name}/*
43
+ * - Move psyche/memory/* to diary/
44
+ * Idempotent: skips missing sources; on collision, newer mtime wins.
45
+ */
46
+ function migrateToSchema2(agentRoot) {
47
+ (0, runtime_1.emitNervesEvent)({
48
+ component: "daemon",
49
+ event: "daemon.bundle_migration_start",
50
+ message: "migrating bundle to schema 2",
51
+ meta: { agentRoot },
52
+ });
53
+ // Migrate arc entities
54
+ for (const name of ["episodes", "obligations", "cares", "intentions"]) {
55
+ const src = path.join(agentRoot, "state", name);
56
+ const dest = path.join(agentRoot, "arc", name);
57
+ migrateDirectory(src, dest);
58
+ }
59
+ // Migrate diary
60
+ const memorySrc = path.join(agentRoot, "psyche", "memory");
61
+ const diaryDest = path.join(agentRoot, "diary");
62
+ migrateDirectory(memorySrc, diaryDest);
63
+ // Update bundle .gitignore
64
+ updateBundleGitignore(agentRoot);
65
+ (0, runtime_1.emitNervesEvent)({
66
+ component: "daemon",
67
+ event: "daemon.bundle_migration_end",
68
+ message: "bundle migration to schema 2 complete",
69
+ meta: { agentRoot },
70
+ });
71
+ }
72
+ /**
73
+ * Ensure bundle .gitignore has state/ ignored and does NOT ignore arc/, diary/, journal/.
74
+ */
75
+ function updateBundleGitignore(agentRoot) {
76
+ const gitignorePath = path.join(agentRoot, ".gitignore");
77
+ let lines = [];
78
+ try {
79
+ if (fs.existsSync(gitignorePath)) {
80
+ lines = fs.readFileSync(gitignorePath, "utf-8").split("\n");
81
+ }
82
+ }
83
+ catch {
84
+ // If we can't read, start fresh
85
+ }
86
+ // Remove arc/, diary/, journal/ from ignore (they should be tracked)
87
+ const toRemove = new Set(["arc/", "diary/", "journal/"]);
88
+ lines = lines.filter((line) => !toRemove.has(line.trim()));
89
+ // Ensure state/ is in the ignore list
90
+ const hasState = lines.some((line) => line.trim() === "state/");
91
+ if (!hasState) {
92
+ lines.push("state/");
93
+ }
94
+ // Write back, trimming trailing empty lines and ensuring trailing newline
95
+ const content = lines.join("\n").replace(/\n+$/, "") + "\n";
96
+ try {
97
+ fs.writeFileSync(gitignorePath, content, "utf-8");
98
+ }
99
+ catch {
100
+ // Non-blocking: if we can't write .gitignore, migration still succeeds
101
+ }
102
+ }
103
+ /**
104
+ * Recursively copy files from src to dest.
105
+ * Creates destination directories as needed. Skips if source doesn't exist.
106
+ * When both source and destination exist, compares mtimes: newer file wins.
107
+ * Logs a warning either way when a collision is detected.
108
+ */
109
+ function migrateDirectory(src, dest) {
110
+ if (!fs.existsSync(src))
111
+ return;
112
+ fs.mkdirSync(dest, { recursive: true });
113
+ const entries = fs.readdirSync(src, { withFileTypes: true });
114
+ for (const entry of entries) {
115
+ const srcPath = path.join(src, entry.name);
116
+ const destPath = path.join(dest, entry.name);
117
+ if (entry.isDirectory()) {
118
+ migrateDirectory(srcPath, destPath);
119
+ }
120
+ else if (!fs.existsSync(destPath)) {
121
+ fs.copyFileSync(srcPath, destPath);
122
+ }
123
+ else {
124
+ // Collision: both source and destination exist — compare mtimes
125
+ const srcMtime = fs.statSync(srcPath).mtimeMs;
126
+ const destMtime = fs.statSync(destPath).mtimeMs;
127
+ if (srcMtime > destMtime) {
128
+ fs.copyFileSync(srcPath, destPath);
129
+ (0, runtime_1.emitNervesEvent)({
130
+ level: "warn",
131
+ component: "daemon",
132
+ event: "daemon.bundle_migration_collision",
133
+ message: `migration collision: source newer, overwriting destination`,
134
+ meta: { srcPath, destPath, srcMtime, destMtime },
135
+ });
136
+ }
137
+ else {
138
+ (0, runtime_1.emitNervesEvent)({
139
+ level: "warn",
140
+ component: "daemon",
141
+ event: "daemon.bundle_migration_collision",
142
+ message: `migration collision: destination newer or equal, keeping destination`,
143
+ meta: { srcPath, destPath, srcMtime, destMtime },
144
+ });
145
+ }
146
+ }
147
+ }
148
+ }
40
149
  async function bundleMetaHook(ctx) {
41
150
  (0, runtime_1.emitNervesEvent)({
42
151
  component: "daemon",
@@ -56,9 +165,14 @@ async function bundleMetaHook(ctx) {
56
165
  // Malformed JSON -- treat as missing, will overwrite with fresh
57
166
  existing = undefined;
58
167
  }
168
+ // Run schema-2 migration if needed
169
+ const currentSchema = existing?.bundleSchemaVersion ?? 1;
170
+ if (currentSchema < 2) {
171
+ migrateToSchema2(ctx.agentRoot);
172
+ }
59
173
  const updated = {
60
174
  runtimeVersion: ctx.currentVersion,
61
- bundleSchemaVersion: existing?.bundleSchemaVersion ?? 1,
175
+ bundleSchemaVersion: currentSchema < 2 ? 2 : currentSchema,
62
176
  lastUpdated: new Date().toISOString(),
63
177
  };
64
178
  // Save old runtimeVersion as previousRuntimeVersion (if there was one)
@@ -0,0 +1,80 @@
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.createHttpHealthProbe = createHttpHealthProbe;
37
+ const http = __importStar(require("node:http"));
38
+ function createHttpHealthProbe(name, port, timeoutMs = 5000) {
39
+ return {
40
+ name,
41
+ check: () => new Promise((resolve) => {
42
+ const req = http.get({
43
+ hostname: "127.0.0.1",
44
+ port,
45
+ path: "/health",
46
+ timeout: timeoutMs,
47
+ }, (res) => {
48
+ let body = "";
49
+ res.on("data", (chunk) => {
50
+ body += chunk.toString();
51
+ });
52
+ res.on("end", () => {
53
+ if (res.statusCode !== 200) {
54
+ resolve({ ok: false, detail: `HTTP ${res.statusCode}` });
55
+ return;
56
+ }
57
+ try {
58
+ const parsed = JSON.parse(body);
59
+ if (parsed.status === "ok") {
60
+ resolve({ ok: true });
61
+ }
62
+ else {
63
+ resolve({ ok: false, detail: `unexpected status: ${String(parsed.status)}` });
64
+ }
65
+ }
66
+ catch {
67
+ resolve({ ok: false, detail: "invalid JSON response" });
68
+ }
69
+ });
70
+ });
71
+ req.on("timeout", () => {
72
+ req.destroy();
73
+ resolve({ ok: false, detail: "timeout" });
74
+ });
75
+ req.on("error", (err) => {
76
+ resolve({ ok: false, detail: err.message });
77
+ });
78
+ }),
79
+ };
80
+ }