@ouro.bot/cli 0.1.0-alpha.12 → 0.1.0-alpha.121

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 (130) hide show
  1. package/AdoptionSpecialist.ouro/psyche/SOUL.md +2 -2
  2. package/AdoptionSpecialist.ouro/psyche/identities/monty.md +2 -2
  3. package/README.md +147 -205
  4. package/assets/ouroboros.png +0 -0
  5. package/changelog.json +737 -0
  6. package/dist/heart/active-work.js +622 -0
  7. package/dist/heart/bridges/manager.js +358 -0
  8. package/dist/heart/bridges/state-machine.js +135 -0
  9. package/dist/heart/bridges/store.js +123 -0
  10. package/dist/heart/commitments.js +105 -0
  11. package/dist/heart/config.js +68 -23
  12. package/dist/heart/core.js +528 -100
  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/auth-flow.js +430 -0
  16. package/dist/heart/daemon/daemon-cli.js +1601 -207
  17. package/dist/heart/daemon/daemon-entry.js +43 -2
  18. package/dist/heart/daemon/daemon-runtime-sync.js +212 -0
  19. package/dist/heart/daemon/daemon.js +226 -1
  20. package/dist/heart/daemon/hatch-animation.js +10 -3
  21. package/dist/heart/daemon/hatch-flow.js +7 -82
  22. package/dist/heart/daemon/hooks/bundle-meta.js +92 -0
  23. package/dist/heart/daemon/launchd.js +159 -0
  24. package/dist/heart/daemon/log-tailer.js +4 -3
  25. package/dist/heart/daemon/message-router.js +17 -8
  26. package/dist/heart/daemon/ouro-bot-entry.js +0 -0
  27. package/dist/heart/daemon/ouro-bot-global-installer.js +128 -0
  28. package/dist/heart/daemon/ouro-entry.js +0 -0
  29. package/dist/heart/daemon/ouro-path-installer.js +59 -15
  30. package/dist/heart/daemon/ouro-uti.js +11 -2
  31. package/dist/heart/daemon/ouro-version-manager.js +171 -0
  32. package/dist/heart/daemon/process-manager.js +13 -0
  33. package/dist/heart/daemon/run-hooks.js +37 -0
  34. package/dist/heart/daemon/runtime-logging.js +58 -15
  35. package/dist/heart/daemon/runtime-metadata.js +219 -0
  36. package/dist/heart/daemon/runtime-mode.js +67 -0
  37. package/dist/heart/daemon/sense-manager.js +43 -2
  38. package/dist/heart/daemon/skill-management-installer.js +94 -0
  39. package/dist/heart/daemon/socket-client.js +202 -0
  40. package/dist/heart/daemon/specialist-orchestrator.js +37 -94
  41. package/dist/heart/daemon/specialist-prompt.js +50 -12
  42. package/dist/heart/daemon/specialist-tools.js +211 -60
  43. package/dist/heart/daemon/staged-restart.js +114 -0
  44. package/dist/heart/daemon/thoughts.js +507 -0
  45. package/dist/heart/daemon/update-checker.js +111 -0
  46. package/dist/heart/daemon/update-hooks.js +138 -0
  47. package/dist/heart/daemon/wrapper-publish-guard.js +86 -0
  48. package/dist/heart/delegation.js +62 -0
  49. package/dist/heart/identity.js +64 -21
  50. package/dist/heart/kicks.js +1 -19
  51. package/dist/heart/model-capabilities.js +48 -0
  52. package/dist/heart/obligations.js +197 -0
  53. package/dist/heart/progress-story.js +42 -0
  54. package/dist/heart/provider-failover.js +88 -0
  55. package/dist/heart/provider-ping.js +151 -0
  56. package/dist/heart/providers/anthropic.js +107 -20
  57. package/dist/heart/providers/azure.js +115 -9
  58. package/dist/heart/providers/github-copilot.js +157 -0
  59. package/dist/heart/providers/minimax.js +33 -3
  60. package/dist/heart/providers/openai-codex.js +49 -14
  61. package/dist/heart/safe-workspace.js +381 -0
  62. package/dist/heart/session-activity.js +169 -0
  63. package/dist/heart/session-recall.js +216 -0
  64. package/dist/heart/streaming.js +108 -24
  65. package/dist/heart/target-resolution.js +123 -0
  66. package/dist/heart/tool-loop.js +194 -0
  67. package/dist/heart/turn-coordinator.js +28 -0
  68. package/dist/mind/associative-recall.js +14 -2
  69. package/dist/mind/bundle-manifest.js +70 -0
  70. package/dist/mind/context.js +60 -14
  71. package/dist/mind/first-impressions.js +16 -2
  72. package/dist/mind/friends/channel.js +35 -0
  73. package/dist/mind/friends/group-context.js +144 -0
  74. package/dist/mind/friends/store-file.js +19 -0
  75. package/dist/mind/friends/trust-explanation.js +74 -0
  76. package/dist/mind/friends/types.js +8 -0
  77. package/dist/mind/memory.js +27 -26
  78. package/dist/mind/obligation-steering.js +221 -0
  79. package/dist/mind/pending.js +76 -9
  80. package/dist/mind/phrases.js +1 -0
  81. package/dist/mind/prompt.js +459 -77
  82. package/dist/mind/token-estimate.js +8 -12
  83. package/dist/nerves/cli-logging.js +15 -2
  84. package/dist/nerves/coverage/run-artifacts.js +1 -1
  85. package/dist/nerves/index.js +12 -0
  86. package/dist/repertoire/ado-client.js +4 -2
  87. package/dist/repertoire/coding/context-pack.js +254 -0
  88. package/dist/repertoire/coding/feedback.js +301 -0
  89. package/dist/repertoire/coding/index.js +4 -1
  90. package/dist/repertoire/coding/manager.js +210 -4
  91. package/dist/repertoire/coding/spawner.js +39 -9
  92. package/dist/repertoire/coding/tools.js +171 -4
  93. package/dist/repertoire/data/ado-endpoints.json +188 -0
  94. package/dist/repertoire/guardrails.js +290 -0
  95. package/dist/repertoire/mcp-client.js +254 -0
  96. package/dist/repertoire/mcp-manager.js +195 -0
  97. package/dist/repertoire/skills.js +3 -26
  98. package/dist/repertoire/tasks/board.js +12 -0
  99. package/dist/repertoire/tasks/index.js +23 -9
  100. package/dist/repertoire/tasks/transitions.js +1 -2
  101. package/dist/repertoire/tools-base.js +925 -250
  102. package/dist/repertoire/tools-bluebubbles.js +93 -0
  103. package/dist/repertoire/tools-teams.js +58 -25
  104. package/dist/repertoire/tools.js +106 -53
  105. package/dist/senses/bluebubbles-client.js +210 -5
  106. package/dist/senses/bluebubbles-entry.js +2 -0
  107. package/dist/senses/bluebubbles-inbound-log.js +109 -0
  108. package/dist/senses/bluebubbles-media.js +339 -0
  109. package/dist/senses/bluebubbles-model.js +12 -4
  110. package/dist/senses/bluebubbles-mutation-log.js +45 -5
  111. package/dist/senses/bluebubbles-runtime-state.js +109 -0
  112. package/dist/senses/bluebubbles-session-cleanup.js +72 -0
  113. package/dist/senses/bluebubbles.js +912 -45
  114. package/dist/senses/cli-layout.js +187 -0
  115. package/dist/senses/cli.js +477 -170
  116. package/dist/senses/continuity.js +94 -0
  117. package/dist/senses/debug-activity.js +154 -0
  118. package/dist/senses/inner-dialog-worker.js +47 -18
  119. package/dist/senses/inner-dialog.js +388 -83
  120. package/dist/senses/pipeline.js +444 -0
  121. package/dist/senses/teams.js +607 -129
  122. package/dist/senses/trust-gate.js +112 -2
  123. package/package.json +14 -3
  124. package/subagents/README.md +4 -70
  125. package/dist/heart/daemon/specialist-session.js +0 -177
  126. package/dist/heart/daemon/subagent-installer.js +0 -134
  127. package/dist/inner-worker-entry.js +0 -4
  128. package/subagents/work-doer.md +0 -233
  129. package/subagents/work-merger.md +0 -624
  130. package/subagents/work-planner.md +0 -373
@@ -0,0 +1,219 @@
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.getRuntimeMetadata = getRuntimeMetadata;
37
+ const crypto_1 = require("crypto");
38
+ const fs = __importStar(require("fs"));
39
+ const os = __importStar(require("os"));
40
+ const path = __importStar(require("path"));
41
+ const childProcess = __importStar(require("child_process"));
42
+ const identity_1 = require("../identity");
43
+ const runtime_1 = require("../../nerves/runtime");
44
+ const UNKNOWN_METADATA = "unknown";
45
+ function optionalFunction(target, key) {
46
+ try {
47
+ const candidate = target[key];
48
+ return typeof candidate === "function" ? candidate : null;
49
+ }
50
+ catch {
51
+ return null;
52
+ }
53
+ }
54
+ function readVersion(packageJsonPath, readFileSyncImpl) {
55
+ try {
56
+ const parsed = JSON.parse(readFileSyncImpl(packageJsonPath, "utf-8"));
57
+ return typeof parsed.version === "string" && parsed.version.trim().length > 0
58
+ ? parsed.version.trim()
59
+ : UNKNOWN_METADATA;
60
+ }
61
+ catch {
62
+ return UNKNOWN_METADATA;
63
+ }
64
+ }
65
+ function readLastUpdated(repoRoot, packageJsonPath, statSyncImpl, execFileSyncImpl) {
66
+ try {
67
+ const raw = execFileSyncImpl("git", ["log", "-1", "--format=%cI"], {
68
+ cwd: repoRoot,
69
+ encoding: "utf-8",
70
+ stdio: ["ignore", "pipe", "ignore"],
71
+ }).trim();
72
+ if (raw.length > 0) {
73
+ return { value: raw, source: "git" };
74
+ }
75
+ }
76
+ catch {
77
+ // fall through to mtime fallback
78
+ }
79
+ try {
80
+ const stats = statSyncImpl(packageJsonPath);
81
+ return {
82
+ value: stats.mtime.toISOString(),
83
+ source: "package-json-mtime",
84
+ };
85
+ }
86
+ catch {
87
+ return { value: UNKNOWN_METADATA, source: "unknown" };
88
+ }
89
+ }
90
+ function readHomeDir() {
91
+ const homedirImpl = optionalFunction(os, "homedir");
92
+ if (!homedirImpl) {
93
+ return null;
94
+ }
95
+ try {
96
+ return homedirImpl.call(os);
97
+ }
98
+ catch {
99
+ return null;
100
+ }
101
+ }
102
+ function listConfigTargets(bundlesRoot, secretsRoot, daemonLoggingPath, readdirSyncImpl) {
103
+ if (!readdirSyncImpl)
104
+ return [];
105
+ const targets = new Set();
106
+ if (daemonLoggingPath) {
107
+ targets.add(daemonLoggingPath);
108
+ }
109
+ try {
110
+ const bundleEntries = readdirSyncImpl(bundlesRoot, { withFileTypes: true });
111
+ for (const entry of bundleEntries) {
112
+ if (!entry.isDirectory() || !entry.name.endsWith(".ouro"))
113
+ continue;
114
+ targets.add(path.join(bundlesRoot, entry.name, "agent.json"));
115
+ }
116
+ }
117
+ catch {
118
+ // ignore unreadable bundle roots
119
+ }
120
+ if (secretsRoot) {
121
+ try {
122
+ const secretEntries = readdirSyncImpl(secretsRoot, { withFileTypes: true });
123
+ for (const entry of secretEntries) {
124
+ if (!entry.isDirectory())
125
+ continue;
126
+ targets.add(path.join(secretsRoot, entry.name, "secrets.json"));
127
+ }
128
+ }
129
+ catch {
130
+ // ignore unreadable secrets roots
131
+ }
132
+ }
133
+ return [...targets].sort();
134
+ }
135
+ function readConfigFingerprint(targets, readFileSyncImpl, existsSyncImpl) {
136
+ if (!readFileSyncImpl || !existsSyncImpl) {
137
+ return {
138
+ value: UNKNOWN_METADATA,
139
+ source: "unknown",
140
+ trackedFiles: targets.length,
141
+ presentFiles: 0,
142
+ };
143
+ }
144
+ const hash = (0, crypto_1.createHash)("sha256");
145
+ let presentFiles = 0;
146
+ for (const target of targets) {
147
+ hash.update(target);
148
+ hash.update("\0");
149
+ if (!existsSyncImpl(target)) {
150
+ hash.update("missing");
151
+ hash.update("\0");
152
+ continue;
153
+ }
154
+ presentFiles += 1;
155
+ hash.update("present");
156
+ hash.update("\0");
157
+ try {
158
+ hash.update(readFileSyncImpl(target, "utf-8"));
159
+ }
160
+ catch {
161
+ hash.update("unreadable");
162
+ }
163
+ hash.update("\0");
164
+ }
165
+ return {
166
+ value: hash.digest("hex"),
167
+ source: "content-hash",
168
+ trackedFiles: targets.length,
169
+ presentFiles,
170
+ };
171
+ }
172
+ function getRuntimeMetadata(deps = {}) {
173
+ const repoRoot = deps.repoRoot ?? (0, identity_1.getRepoRoot)();
174
+ const bundlesRoot = deps.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
175
+ const homeDir = readHomeDir();
176
+ const secretsRoot = deps.secretsRoot ?? (homeDir ? path.join(homeDir, ".agentsecrets") : null);
177
+ const daemonLoggingPath = deps.daemonLoggingPath ?? (0, identity_1.getAgentDaemonLoggingConfigPath)();
178
+ const readFileSyncImpl = deps.readFileSync ?? optionalFunction(fs, "readFileSync")?.bind(fs) ?? null;
179
+ const statSyncImpl = deps.statSync ?? optionalFunction(fs, "statSync")?.bind(fs) ?? null;
180
+ const readdirSyncImpl = deps.readdirSync ?? optionalFunction(fs, "readdirSync")?.bind(fs) ?? null;
181
+ const existsSyncImpl = deps.existsSync ?? optionalFunction(fs, "existsSync")?.bind(fs) ?? null;
182
+ const execFileSyncImpl = deps.execFileSync
183
+ ?? optionalFunction(childProcess, "execFileSync")?.bind(childProcess)
184
+ ?? null;
185
+ const packageJsonPath = path.join(repoRoot, "package.json");
186
+ const version = readFileSyncImpl
187
+ ? readVersion(packageJsonPath, readFileSyncImpl)
188
+ : UNKNOWN_METADATA;
189
+ const lastUpdated = statSyncImpl
190
+ ? readLastUpdated(repoRoot, packageJsonPath, statSyncImpl, execFileSyncImpl ?? (() => {
191
+ throw new Error("git unavailable");
192
+ }))
193
+ : { value: UNKNOWN_METADATA, source: "unknown" };
194
+ const configTargets = listConfigTargets(bundlesRoot, secretsRoot, daemonLoggingPath, readdirSyncImpl);
195
+ const configFingerprint = readConfigFingerprint(configTargets, readFileSyncImpl, existsSyncImpl);
196
+ (0, runtime_1.emitNervesEvent)({
197
+ component: "daemon",
198
+ event: "daemon.runtime_metadata_read",
199
+ message: "read runtime metadata",
200
+ meta: {
201
+ version,
202
+ lastUpdated: lastUpdated.value,
203
+ lastUpdatedSource: lastUpdated.source,
204
+ repoRoot,
205
+ configFingerprint: configFingerprint.value === UNKNOWN_METADATA
206
+ ? UNKNOWN_METADATA
207
+ : configFingerprint.value.slice(0, 12),
208
+ configFingerprintSource: configFingerprint.source,
209
+ configTrackedFiles: configFingerprint.trackedFiles,
210
+ configPresentFiles: configFingerprint.presentFiles,
211
+ },
212
+ });
213
+ return {
214
+ version,
215
+ lastUpdated: lastUpdated.value,
216
+ repoRoot,
217
+ configFingerprint: configFingerprint.value,
218
+ };
219
+ }
@@ -0,0 +1,67 @@
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.detectRuntimeMode = detectRuntimeMode;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const runtime_1 = require("../../nerves/runtime");
40
+ function detectRuntimeMode(rootPath, deps = {}) {
41
+ const checkExists = deps.existsSync ?? fs.existsSync;
42
+ // 1. Production: installed via npm
43
+ if (rootPath.includes("node_modules/@ouro.bot/cli") ||
44
+ rootPath.includes("node_modules/ouro.bot")) {
45
+ (0, runtime_1.emitNervesEvent)({
46
+ component: "daemon",
47
+ event: "daemon.runtime_mode_detected",
48
+ message: "detected runtime mode",
49
+ meta: { rootPath, mode: "production" },
50
+ });
51
+ return "production";
52
+ }
53
+ // 2-4. Everything else is dev: worktrees, git repos, unknown paths
54
+ // (conservative default: assume dev unless proven production)
55
+ const reason = rootPath.includes(".claude/worktrees/")
56
+ ? "worktree"
57
+ : checkExists(path.join(rootPath, ".git"))
58
+ ? "git-repo"
59
+ : "unknown";
60
+ (0, runtime_1.emitNervesEvent)({
61
+ component: "daemon",
62
+ event: "daemon.runtime_mode_detected",
63
+ message: "detected runtime mode",
64
+ meta: { rootPath, mode: "dev", reason },
65
+ });
66
+ return "dev";
67
+ }
@@ -38,12 +38,14 @@ const fs = __importStar(require("fs"));
38
38
  const os = __importStar(require("os"));
39
39
  const path = __importStar(require("path"));
40
40
  const runtime_1 = require("../../nerves/runtime");
41
+ const bluebubbles_runtime_state_1 = require("../../senses/bluebubbles-runtime-state");
41
42
  const identity_1 = require("../identity");
42
43
  const sense_truth_1 = require("../sense-truth");
43
44
  const process_manager_1 = require("./process-manager");
44
45
  const DEFAULT_TEAMS_PORT = 3978;
45
46
  const DEFAULT_BLUEBUBBLES_PORT = 18790;
46
47
  const DEFAULT_BLUEBUBBLES_WEBHOOK_PATH = "/bluebubbles-webhook";
48
+ const BLUEBUBBLES_RUNTIME_FRESHNESS_WINDOW_MS = 90_000;
47
49
  function defaultSenses() {
48
50
  return {
49
51
  cli: { ...identity_1.DEFAULT_AGENT_SENSES.cli },
@@ -175,12 +177,45 @@ function runtimeInfoFor(status) {
175
177
  return { runtime: "running" };
176
178
  return { runtime: "error" };
177
179
  }
180
+ function blueBubblesRuntimeStateIsFresh(lastCheckedAt, now = Date.now()) {
181
+ if (!lastCheckedAt) {
182
+ return false;
183
+ }
184
+ const checkedAt = Date.parse(lastCheckedAt);
185
+ if (!Number.isFinite(checkedAt)) {
186
+ return false;
187
+ }
188
+ return checkedAt >= now - BLUEBUBBLES_RUNTIME_FRESHNESS_WINDOW_MS;
189
+ }
190
+ function readBlueBubblesRuntimeFacts(agent, bundlesRoot, snapshot) {
191
+ const agentRoot = path.join(bundlesRoot, `${agent}.ouro`);
192
+ const runtimePath = path.join(agentRoot, "state", "senses", "bluebubbles", "runtime.json");
193
+ if (!fs.existsSync(runtimePath)) {
194
+ return { runtime: snapshot?.runtime };
195
+ }
196
+ const state = (0, bluebubbles_runtime_state_1.readBlueBubblesRuntimeState)(agent, agentRoot);
197
+ if (!blueBubblesRuntimeStateIsFresh(state.lastCheckedAt)) {
198
+ return { runtime: snapshot?.runtime };
199
+ }
200
+ if (state.upstreamStatus === "error") {
201
+ return {
202
+ runtime: "error",
203
+ detail: state.detail,
204
+ };
205
+ }
206
+ if (state.upstreamStatus === "ok") {
207
+ return { runtime: "running" };
208
+ }
209
+ return { runtime: snapshot?.runtime };
210
+ }
178
211
  class DaemonSenseManager {
179
212
  processManager;
180
213
  contexts;
214
+ bundlesRoot;
181
215
  constructor(options) {
182
216
  const bundlesRoot = options.bundlesRoot ?? path.join(os.homedir(), "AgentBundles");
183
217
  const secretsRoot = options.secretsRoot ?? path.join(os.homedir(), ".agentsecrets");
218
+ this.bundlesRoot = bundlesRoot;
184
219
  this.contexts = new Map(options.agents.map((agent) => {
185
220
  const senses = readAgentSenses(path.join(bundlesRoot, `${agent}.ouro`, "agent.json"));
186
221
  const facts = senseFactsFromSecrets(agent, senses, path.join(secretsRoot, agent, "secrets.json"));
@@ -227,6 +262,7 @@ class DaemonSenseManager {
227
262
  runtime.set(parsed.agent, current);
228
263
  }
229
264
  const rows = [...this.contexts.entries()].flatMap(([agent, context]) => {
265
+ const blueBubblesRuntimeFacts = readBlueBubblesRuntimeFacts(agent, this.bundlesRoot, runtime.get(agent)?.bluebubbles);
230
266
  const runtimeInfo = {
231
267
  cli: { configured: true },
232
268
  teams: {
@@ -235,7 +271,7 @@ class DaemonSenseManager {
235
271
  },
236
272
  bluebubbles: {
237
273
  configured: context.facts.bluebubbles.configured,
238
- ...(runtime.get(agent)?.bluebubbles ?? {}),
274
+ ...blueBubblesRuntimeFacts,
239
275
  },
240
276
  };
241
277
  const inventory = (0, sense_truth_1.getSenseInventory)({ senses: context.senses }, runtimeInfo);
@@ -245,7 +281,12 @@ class DaemonSenseManager {
245
281
  label: entry.label,
246
282
  enabled: entry.enabled,
247
283
  status: entry.status,
248
- detail: entry.enabled ? context.facts[entry.sense].detail : "not enabled in agent.json",
284
+ detail: entry.enabled
285
+ ? entry.sense === "bluebubbles"
286
+ ? blueBubblesRuntimeFacts.detail
287
+ ?? context.facts[entry.sense].detail
288
+ : context.facts[entry.sense].detail
289
+ : "not enabled in agent.json",
249
290
  }));
250
291
  });
251
292
  (0, runtime_1.emitNervesEvent)({
@@ -0,0 +1,94 @@
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.ensureSkillManagement = ensureSkillManagement;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const runtime_1 = require("../../nerves/runtime");
40
+ const identity_1 = require("../identity");
41
+ const SKILL_MANAGEMENT_URL = "https://raw.githubusercontent.com/ouroborosbot/ouroboros-skills/main/skills/skill-management/SKILL.md";
42
+ async function ensureSkillManagement() {
43
+ const bundlesRoot = (0, identity_1.getAgentBundlesRoot)();
44
+ if (!fs.existsSync(bundlesRoot))
45
+ return;
46
+ // Find all agent bundles
47
+ const entries = fs.readdirSync(bundlesRoot).filter(e => e.endsWith(".ouro"));
48
+ if (entries.length === 0)
49
+ return;
50
+ // Check if ANY bundle is missing the skill
51
+ const missing = entries.filter(e => {
52
+ const targetPath = path.join(bundlesRoot, e, "skills", "skill-management.md");
53
+ return !fs.existsSync(targetPath);
54
+ });
55
+ if (missing.length === 0)
56
+ return;
57
+ // eslint-disable-next-line no-console -- terminal UX: visible install status
58
+ console.log("installing skill-management from ouroboros-skills...");
59
+ try {
60
+ const response = await fetch(SKILL_MANAGEMENT_URL);
61
+ if (!response.ok) {
62
+ // eslint-disable-next-line no-console -- terminal UX: visible install status
63
+ console.error(`✗ failed to fetch skill-management (HTTP ${response.status})`);
64
+ (0, runtime_1.emitNervesEvent)({
65
+ level: "warn",
66
+ component: "daemon",
67
+ event: "daemon.skill_management_install_error",
68
+ message: "failed to fetch skill-management from GitHub",
69
+ meta: { status: response.status, url: SKILL_MANAGEMENT_URL },
70
+ });
71
+ return;
72
+ }
73
+ const content = await response.text();
74
+ for (const bundle of missing) {
75
+ const skillsDir = path.join(bundlesRoot, bundle, "skills");
76
+ const targetPath = path.join(skillsDir, "skill-management.md");
77
+ fs.mkdirSync(skillsDir, { recursive: true });
78
+ fs.writeFileSync(targetPath, content, "utf-8");
79
+ }
80
+ // eslint-disable-next-line no-console -- terminal UX: visible install status
81
+ console.log(`✓ installed skill-management (${missing.length} agent${missing.length > 1 ? "s" : ""})`);
82
+ }
83
+ catch (error) {
84
+ // eslint-disable-next-line no-console -- terminal UX: visible install status
85
+ console.error(`✗ failed to install skill-management: ${error instanceof Error ? error.message : String(error)}`);
86
+ (0, runtime_1.emitNervesEvent)({
87
+ level: "warn",
88
+ component: "daemon",
89
+ event: "daemon.skill_management_install_error",
90
+ message: "failed to install skill-management skill",
91
+ meta: { error: error instanceof Error ? error.message : String(error) },
92
+ });
93
+ }
94
+ }
@@ -0,0 +1,202 @@
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.DEFAULT_DAEMON_SOCKET_PATH = void 0;
37
+ exports.sendDaemonCommand = sendDaemonCommand;
38
+ exports.checkDaemonSocketAlive = checkDaemonSocketAlive;
39
+ exports.requestInnerWake = requestInnerWake;
40
+ const fs = __importStar(require("fs"));
41
+ const net = __importStar(require("net"));
42
+ const runtime_1 = require("../../nerves/runtime");
43
+ exports.DEFAULT_DAEMON_SOCKET_PATH = "/tmp/ouroboros-daemon.sock";
44
+ function sendDaemonCommand(socketPath, command) {
45
+ (0, runtime_1.emitNervesEvent)({
46
+ component: "daemon",
47
+ event: "daemon.socket_command_start",
48
+ message: "sending daemon command over socket",
49
+ meta: { socketPath, kind: command.kind },
50
+ });
51
+ return new Promise((resolve, reject) => {
52
+ const client = net.createConnection(socketPath);
53
+ let raw = "";
54
+ client.on("connect", () => {
55
+ client.write(JSON.stringify(command));
56
+ client.end();
57
+ });
58
+ client.on("data", (chunk) => {
59
+ raw += chunk.toString("utf-8");
60
+ });
61
+ client.on("error", (error) => {
62
+ (0, runtime_1.emitNervesEvent)({
63
+ level: "error",
64
+ component: "daemon",
65
+ event: "daemon.socket_command_error",
66
+ message: "daemon socket command failed",
67
+ meta: {
68
+ socketPath,
69
+ kind: command.kind,
70
+ error: error.message,
71
+ },
72
+ });
73
+ reject(error);
74
+ });
75
+ client.on("end", () => {
76
+ const trimmed = raw.trim();
77
+ if (trimmed.length === 0 && command.kind === "daemon.stop") {
78
+ (0, runtime_1.emitNervesEvent)({
79
+ component: "daemon",
80
+ event: "daemon.socket_command_end",
81
+ message: "daemon socket command completed",
82
+ meta: { socketPath, kind: command.kind, ok: true },
83
+ });
84
+ resolve({ ok: true, message: "daemon stopped" });
85
+ return;
86
+ }
87
+ if (trimmed.length === 0) {
88
+ const error = new Error("Daemon returned empty response.");
89
+ (0, runtime_1.emitNervesEvent)({
90
+ level: "error",
91
+ component: "daemon",
92
+ event: "daemon.socket_command_error",
93
+ message: "daemon socket command returned empty response",
94
+ meta: {
95
+ socketPath,
96
+ kind: command.kind,
97
+ error: error.message,
98
+ },
99
+ });
100
+ reject(error);
101
+ return;
102
+ }
103
+ try {
104
+ const parsed = JSON.parse(trimmed);
105
+ (0, runtime_1.emitNervesEvent)({
106
+ component: "daemon",
107
+ event: "daemon.socket_command_end",
108
+ message: "daemon socket command completed",
109
+ meta: {
110
+ socketPath,
111
+ kind: command.kind,
112
+ ok: parsed.ok,
113
+ },
114
+ });
115
+ resolve(parsed);
116
+ }
117
+ catch (error) {
118
+ (0, runtime_1.emitNervesEvent)({
119
+ level: "error",
120
+ component: "daemon",
121
+ event: "daemon.socket_command_error",
122
+ message: "daemon socket command returned invalid JSON",
123
+ meta: {
124
+ socketPath,
125
+ kind: command.kind,
126
+ error: error instanceof Error ? error.message : String(error),
127
+ },
128
+ });
129
+ reject(error);
130
+ }
131
+ });
132
+ });
133
+ }
134
+ function checkDaemonSocketAlive(socketPath) {
135
+ (0, runtime_1.emitNervesEvent)({
136
+ component: "daemon",
137
+ event: "daemon.socket_alive_check_start",
138
+ message: "checking daemon socket health",
139
+ meta: { socketPath },
140
+ });
141
+ return new Promise((resolve) => {
142
+ const client = net.createConnection(socketPath);
143
+ let raw = "";
144
+ let done = false;
145
+ const finalize = (alive) => {
146
+ if (done)
147
+ return;
148
+ done = true;
149
+ (0, runtime_1.emitNervesEvent)({
150
+ component: "daemon",
151
+ event: "daemon.socket_alive_check_end",
152
+ message: "daemon socket health check completed",
153
+ meta: { socketPath, alive },
154
+ });
155
+ resolve(alive);
156
+ };
157
+ if ("setTimeout" in client && typeof client.setTimeout === "function") {
158
+ client.setTimeout(800, () => {
159
+ client.destroy();
160
+ finalize(false);
161
+ });
162
+ }
163
+ client.on("connect", () => {
164
+ client.write(JSON.stringify({ kind: "daemon.status" }));
165
+ client.end();
166
+ });
167
+ client.on("data", (chunk) => {
168
+ raw += chunk.toString("utf-8");
169
+ });
170
+ client.on("error", () => finalize(false));
171
+ client.on("end", () => {
172
+ if (raw.trim().length === 0) {
173
+ finalize(false);
174
+ return;
175
+ }
176
+ try {
177
+ JSON.parse(raw);
178
+ finalize(true);
179
+ }
180
+ catch {
181
+ finalize(false);
182
+ }
183
+ });
184
+ });
185
+ }
186
+ async function requestInnerWake(agent, socketPath = exports.DEFAULT_DAEMON_SOCKET_PATH) {
187
+ const socketAvailable = fs.existsSync(socketPath);
188
+ (0, runtime_1.emitNervesEvent)({
189
+ component: "daemon",
190
+ event: "daemon.inner_wake_request",
191
+ message: "requesting daemon-managed inner wake",
192
+ meta: {
193
+ agent,
194
+ socketPath,
195
+ socketAvailable,
196
+ },
197
+ });
198
+ if (!socketAvailable) {
199
+ return null;
200
+ }
201
+ return sendDaemonCommand(socketPath, { kind: "inner.wake", agent });
202
+ }