@ouro.bot/cli 0.1.0-alpha.528 → 0.1.0-alpha.529

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.
package/changelog.json CHANGED
@@ -1,6 +1,14 @@
1
1
  {
2
2
  "_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
3
3
  "versions": [
4
+ {
5
+ "version": "0.1.0-alpha.529",
6
+ "changes": [
7
+ "MCP agent status now asks the daemon for proof-bearing runtime health, including per-agent worker rows and enabled sense rows with proof method, proof age, failure layer, and recovery counters.",
8
+ "MCP status reports daemon/socket unreachability as an explicit status line instead of hiding it behind otherwise successful diary/session facts.",
9
+ "MCP status now includes its own package version and flags MCP-vs-daemon version mismatches so stale long-lived MCP servers are visible during reliability checks."
10
+ ]
11
+ },
4
12
  {
5
13
  "version": "0.1.0-alpha.528",
6
14
  "changes": [
@@ -58,6 +58,7 @@ const path = __importStar(require("path"));
58
58
  const identity_1 = require("../identity");
59
59
  const diary_1 = require("../../mind/diary");
60
60
  const runtime_1 = require("../../nerves/runtime");
61
+ const socket_client_1 = require("./socket-client");
61
62
  /** Format diary hits the same way the search_notes tool does. */
62
63
  function formatDiaryHits(hits) {
63
64
  return hits.map((f) => `[diary] ${f.text} (source=${f.source}, createdAt=${f.createdAt})`);
@@ -149,6 +150,152 @@ function listTaskFiles(agent) {
149
150
  function emit(event, message, meta) {
150
151
  (0, runtime_1.emitNervesEvent)({ component: "daemon", event, message, meta });
151
152
  }
153
+ function objectRecord(value) {
154
+ return value && typeof value === "object" && !Array.isArray(value)
155
+ ? value
156
+ : null;
157
+ }
158
+ function stringValue(value) {
159
+ return typeof value === "string" ? value : null;
160
+ }
161
+ function booleanValue(value) {
162
+ return typeof value === "boolean" ? value : null;
163
+ }
164
+ function numberValue(value) {
165
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
166
+ }
167
+ function readMcpRuntimeVersion() {
168
+ const packagePath = path.resolve(__dirname, "..", "..", "..", "package.json");
169
+ try {
170
+ const parsed = JSON.parse(fs.readFileSync(packagePath, "utf-8"));
171
+ return stringValue(parsed.version);
172
+ }
173
+ catch {
174
+ return null;
175
+ }
176
+ }
177
+ function summarizeRuntimeStatus(data, agent) {
178
+ const payload = objectRecord(data);
179
+ if (!payload)
180
+ return null;
181
+ const overview = objectRecord(payload.overview);
182
+ const workers = Array.isArray(payload.workers) ? payload.workers : [];
183
+ const senses = Array.isArray(payload.senses) ? payload.senses : [];
184
+ return {
185
+ daemonReachable: true,
186
+ overview: overview
187
+ ? {
188
+ daemon: stringValue(overview.daemon) ?? "unknown",
189
+ health: stringValue(overview.health) ?? "unknown",
190
+ version: stringValue(overview.version),
191
+ mode: stringValue(overview.mode),
192
+ }
193
+ : undefined,
194
+ workers: workers.flatMap((row) => {
195
+ const record = objectRecord(row);
196
+ if (!record || stringValue(record.agent) !== agent)
197
+ return [];
198
+ const worker = stringValue(record.worker);
199
+ const status = stringValue(record.status);
200
+ return worker && status ? [{ worker, status }] : [];
201
+ }),
202
+ senses: senses.flatMap((row) => {
203
+ const record = objectRecord(row);
204
+ if (!record || stringValue(record.agent) !== agent)
205
+ return [];
206
+ const sense = stringValue(record.sense);
207
+ const status = stringValue(record.status);
208
+ const enabled = booleanValue(record.enabled);
209
+ if (!sense || !status || enabled === null)
210
+ return [];
211
+ return [{
212
+ sense,
213
+ status,
214
+ enabled,
215
+ detail: stringValue(record.detail),
216
+ proofMethod: stringValue(record.proofMethod),
217
+ lastProofAt: stringValue(record.lastProofAt),
218
+ proofAgeMs: numberValue(record.proofAgeMs),
219
+ pendingRecoveryCount: numberValue(record.pendingRecoveryCount),
220
+ failedRecoveryCount: numberValue(record.failedRecoveryCount),
221
+ failureLayer: stringValue(record.failureLayer),
222
+ lastFailure: stringValue(record.lastFailure),
223
+ recoveryAction: stringValue(record.recoveryAction),
224
+ }];
225
+ }),
226
+ };
227
+ }
228
+ async function readRuntimeStatus(socketPath, agent) {
229
+ if (!socketPath)
230
+ return null;
231
+ try {
232
+ const response = await (0, socket_client_1.sendDaemonCommand)(socketPath, { kind: "daemon.status" });
233
+ if (!response.ok) {
234
+ return {
235
+ daemonReachable: false,
236
+ workers: [],
237
+ senses: [],
238
+ error: response.error ?? response.message ?? "daemon status did not answer cleanly",
239
+ };
240
+ }
241
+ return summarizeRuntimeStatus(response.data, agent);
242
+ }
243
+ catch (error) {
244
+ return {
245
+ daemonReachable: false,
246
+ workers: [],
247
+ senses: [],
248
+ error: error instanceof Error ? error.message : String(error),
249
+ };
250
+ }
251
+ }
252
+ function formatRuntimeStatusLines(runtime, mcpVersion) {
253
+ if (!runtime)
254
+ return mcpVersion ? [`mcpVersion=${mcpVersion}`] : [];
255
+ if (!runtime.daemonReachable) {
256
+ return [
257
+ `daemon=unreachable${runtime.error ? `\terror=${runtime.error}` : ""}`,
258
+ ...(mcpVersion ? [`mcpVersion=${mcpVersion}`] : []),
259
+ ];
260
+ }
261
+ const lines = [];
262
+ if (runtime.overview) {
263
+ const versionPart = runtime.overview.version ? `\tdaemonVersion=${runtime.overview.version}` : "";
264
+ const modePart = runtime.overview.mode ? `\tmode=${runtime.overview.mode}` : "";
265
+ const mcpVersionPart = mcpVersion ? `\tmcpVersion=${mcpVersion}` : "";
266
+ const mismatchPart = mcpVersion && runtime.overview.version && mcpVersion !== runtime.overview.version
267
+ ? `\tversionMismatch=mcp:${mcpVersion},daemon:${runtime.overview.version}`
268
+ : "";
269
+ lines.push(`daemon=${runtime.overview.daemon}\thealth=${runtime.overview.health}${versionPart}${modePart}${mcpVersionPart}${mismatchPart}`);
270
+ }
271
+ for (const worker of runtime.workers) {
272
+ lines.push(`worker=${worker.worker}:${worker.status}`);
273
+ }
274
+ for (const sense of runtime.senses) {
275
+ const detailPart = sense.detail ? `\tdetail=${sense.detail}` : "";
276
+ const proofPart = sense.proofMethod ? `\tproof=${sense.proofMethod}` : "";
277
+ const lastProofPart = sense.lastProofAt ? `\tlastProofAt=${sense.lastProofAt}` : "";
278
+ const proofAgePart = sense.proofAgeMs !== null ? `\tproofAgeMs=${sense.proofAgeMs}` : "";
279
+ const pendingPart = sense.pendingRecoveryCount !== null ? `\tpendingRecovery=${sense.pendingRecoveryCount}` : "";
280
+ const failedPart = sense.failedRecoveryCount !== null ? `\tfailedRecovery=${sense.failedRecoveryCount}` : "";
281
+ const failureLayerPart = sense.failureLayer ? `\tfailureLayer=${sense.failureLayer}` : "";
282
+ const failurePart = sense.lastFailure ? `\tlastFailure=${sense.lastFailure}` : "";
283
+ const recoveryPart = sense.recoveryAction ? `\trecovery=${sense.recoveryAction}` : "";
284
+ lines.push(`sense=${sense.sense}:${sense.enabled ? sense.status : "disabled"}`
285
+ + detailPart
286
+ + proofPart
287
+ + lastProofPart
288
+ + proofAgePart
289
+ + pendingPart
290
+ + failedPart
291
+ + failureLayerPart
292
+ + failurePart
293
+ + recoveryPart);
294
+ }
295
+ if (lines.length === 0 && mcpVersion)
296
+ lines.push(`mcpVersion=${mcpVersion}`);
297
+ return lines;
298
+ }
152
299
  // ─── Handlers ────────────────────────────────────────────────────────────────
153
300
  async function handleAgentStatus(params) {
154
301
  (0, runtime_1.emitNervesEvent)({ component: "daemon", event: "daemon.agent_service_start", message: "handling agent.status", meta: { agent: params.agent } });
@@ -156,16 +303,20 @@ async function handleAgentStatus(params) {
156
303
  const facts = (0, diary_1.readDiaryEntries)(diaryRoot);
157
304
  const innerStatus = readInnerDialogStatus(params.agent);
158
305
  const sessions = enumerateSessions(params.agent);
306
+ const mcpVersion = readMcpRuntimeVersion();
307
+ const runtime = await readRuntimeStatus(params.socketPath ?? socket_client_1.DEFAULT_DAEMON_SOCKET_PATH, params.agent);
159
308
  emit("daemon.agent_service_end", "completed agent.status", { agent: params.agent });
160
309
  const innerStatusValue = innerStatus?.status ?? "unknown";
161
310
  const lastThoughtAt = innerStatus?.lastCompletedAt ?? null;
162
- const message = [
311
+ const agentLine = [
163
312
  `agent=${params.agent}`,
164
313
  `innerStatus=${innerStatusValue}`,
165
314
  `lastThoughtAt=${lastThoughtAt ?? "never"}`,
166
315
  `sessionCount=${sessions.length}`,
167
316
  `diaryEntries=${facts.length}`,
168
317
  ].join("\t");
318
+ const runtimeLines = formatRuntimeStatusLines(runtime, mcpVersion);
319
+ const message = [agentLine, ...runtimeLines].join("\n");
169
320
  return {
170
321
  ok: true,
171
322
  message,
@@ -176,6 +327,8 @@ async function handleAgentStatus(params) {
176
327
  sessionCount: sessions.length,
177
328
  hasDiaryEntries: facts.length > 0,
178
329
  factCount: facts.length,
330
+ runtime,
331
+ mcpVersion,
179
332
  },
180
333
  };
181
334
  }
@@ -425,7 +425,7 @@ function createMcpServer(options) {
425
425
  if (serviceHandler && typeof agentService[serviceHandler] === "function") {
426
426
  const handlerFn = agentService[serviceHandler];
427
427
  try {
428
- response = await handlerFn({ agent, friendId, ...toolArgs });
428
+ response = await handlerFn({ agent, friendId, socketPath, ...toolArgs });
429
429
  }
430
430
  catch (error) {
431
431
  const errorMessage = error instanceof Error ? error.message : String(error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.528",
3
+ "version": "0.1.0-alpha.529",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",