@ouro.bot/cli 0.1.0-alpha.715 → 0.1.0-alpha.717
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,18 @@
|
|
|
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.717",
|
|
6
|
+
"changes": [
|
|
7
|
+
"Raise MCP bridge canary timeout for slow runtime status paths."
|
|
8
|
+
]
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"version": "0.1.0-alpha.716",
|
|
12
|
+
"changes": [
|
|
13
|
+
"Add durable MCP bridge doctor and repair guidance."
|
|
14
|
+
]
|
|
15
|
+
},
|
|
4
16
|
{
|
|
5
17
|
"version": "0.1.0-alpha.715",
|
|
6
18
|
"changes": [
|
|
@@ -60,6 +60,7 @@ const diary_1 = require("../../mind/diary");
|
|
|
60
60
|
const record_paths_1 = require("../../mind/record-paths");
|
|
61
61
|
const runtime_1 = require("../../nerves/runtime");
|
|
62
62
|
const socket_client_1 = require("./socket-client");
|
|
63
|
+
const mcp_canary_1 = require("./mcp-canary");
|
|
63
64
|
/** Format diary hits the same way the search_facts tool does. */
|
|
64
65
|
function formatDiaryHits(hits) {
|
|
65
66
|
return hits.map((f) => `[diary] ${f.text} (source=${f.source}, createdAt=${f.createdAt})`);
|
|
@@ -250,7 +251,7 @@ async function readRuntimeStatus(socketPath, agent) {
|
|
|
250
251
|
};
|
|
251
252
|
}
|
|
252
253
|
}
|
|
253
|
-
function formatRuntimeStatusLines(runtime, mcpVersion) {
|
|
254
|
+
function formatRuntimeStatusLines(runtime, mcpVersion, agent) {
|
|
254
255
|
if (!runtime)
|
|
255
256
|
return mcpVersion ? [`mcpVersion=${mcpVersion}`] : [];
|
|
256
257
|
if (!runtime.daemonReachable) {
|
|
@@ -264,10 +265,16 @@ function formatRuntimeStatusLines(runtime, mcpVersion) {
|
|
|
264
265
|
const versionPart = runtime.overview.version ? `\tdaemonVersion=${runtime.overview.version}` : "";
|
|
265
266
|
const modePart = runtime.overview.mode ? `\tmode=${runtime.overview.mode}` : "";
|
|
266
267
|
const mcpVersionPart = mcpVersion ? `\tmcpVersion=${mcpVersion}` : "";
|
|
267
|
-
const
|
|
268
|
+
const hasVersionMismatch = !!(mcpVersion && runtime.overview.version && mcpVersion !== runtime.overview.version);
|
|
269
|
+
const mismatchPart = hasVersionMismatch
|
|
268
270
|
? `\tversionMismatch=mcp:${mcpVersion},daemon:${runtime.overview.version}`
|
|
269
271
|
: "";
|
|
270
272
|
lines.push(`daemon=${runtime.overview.daemon}\thealth=${runtime.overview.health}${versionPart}${modePart}${mcpVersionPart}${mismatchPart}`);
|
|
273
|
+
if (hasVersionMismatch) {
|
|
274
|
+
const repair = (0, mcp_canary_1.buildMcpBridgeRepairGuidance)(agent);
|
|
275
|
+
lines.push(`mcpRepair=${repair.actor}\tcodex=${repair.commands[0]}\tclaudeCode=${repair.commands[1]}`);
|
|
276
|
+
lines.push(`mcpReload=required\treason=${repair.reload}`);
|
|
277
|
+
}
|
|
271
278
|
}
|
|
272
279
|
for (const worker of runtime.workers) {
|
|
273
280
|
lines.push(`worker=${worker.worker}:${worker.status}`);
|
|
@@ -297,6 +304,16 @@ function formatRuntimeStatusLines(runtime, mcpVersion) {
|
|
|
297
304
|
lines.push(`mcpVersion=${mcpVersion}`);
|
|
298
305
|
return lines;
|
|
299
306
|
}
|
|
307
|
+
function summarizeMcpBridgeHealth(agent, runtime, mcpVersion) {
|
|
308
|
+
const daemonVersion = runtime?.daemonReachable ? runtime.overview?.version ?? null : null;
|
|
309
|
+
const versionMismatch = !!(mcpVersion && daemonVersion && mcpVersion !== daemonVersion);
|
|
310
|
+
return {
|
|
311
|
+
mcpVersion,
|
|
312
|
+
daemonVersion,
|
|
313
|
+
versionMismatch,
|
|
314
|
+
...(versionMismatch ? { repair: (0, mcp_canary_1.buildMcpBridgeRepairGuidance)(agent) } : {}),
|
|
315
|
+
};
|
|
316
|
+
}
|
|
300
317
|
// ─── Handlers ────────────────────────────────────────────────────────────────
|
|
301
318
|
async function handleAgentStatus(params) {
|
|
302
319
|
(0, runtime_1.emitNervesEvent)({ component: "daemon", event: "daemon.agent_service_start", message: "handling agent.status", meta: { agent: params.agent } });
|
|
@@ -316,7 +333,8 @@ async function handleAgentStatus(params) {
|
|
|
316
333
|
`sessionCount=${sessions.length}`,
|
|
317
334
|
`diaryEntries=${facts.length}`,
|
|
318
335
|
].join("\t");
|
|
319
|
-
const runtimeLines = formatRuntimeStatusLines(runtime, mcpVersion);
|
|
336
|
+
const runtimeLines = formatRuntimeStatusLines(runtime, mcpVersion, params.agent);
|
|
337
|
+
const mcpHealth = summarizeMcpBridgeHealth(params.agent, runtime, mcpVersion);
|
|
320
338
|
const message = [agentLine, ...runtimeLines].join("\n");
|
|
321
339
|
return {
|
|
322
340
|
ok: true,
|
|
@@ -330,6 +348,7 @@ async function handleAgentStatus(params) {
|
|
|
330
348
|
factCount: facts.length,
|
|
331
349
|
runtime,
|
|
332
350
|
mcpVersion,
|
|
351
|
+
mcpHealth,
|
|
333
352
|
},
|
|
334
353
|
};
|
|
335
354
|
}
|
|
@@ -125,6 +125,10 @@ const DEFAULT_DAEMON_STARTUP_TIMEOUT_MS = 60_000;
|
|
|
125
125
|
const DEFAULT_DAEMON_STARTUP_POLL_INTERVAL_MS = 500;
|
|
126
126
|
const DEFAULT_DAEMON_STARTUP_STABILITY_WINDOW_MS = 1_500;
|
|
127
127
|
const DEFAULT_DAEMON_STARTUP_RETRY_LIMIT = 1;
|
|
128
|
+
function setupReloadNotice(tool) {
|
|
129
|
+
const host = tool === "codex" ? "Codex" : "Claude Code";
|
|
130
|
+
return ` reload required: open a fresh ${host} session so the host launches the newly registered MCP bridge; existing MCP processes keep their old runtime`;
|
|
131
|
+
}
|
|
128
132
|
const DEFAULT_DAEMON_STARTUP_LOG_LINES = 10;
|
|
129
133
|
const RSVP_CLI_KINDS = new Set([
|
|
130
134
|
"rsvp.doctor",
|
|
@@ -6940,7 +6944,7 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
6940
6944
|
if (!existingClaudeMd.includes("Agent conversations (ouro)")) {
|
|
6941
6945
|
fs.writeFileSync(claudeMdPath, existingClaudeMd + agentInstructions);
|
|
6942
6946
|
}
|
|
6943
|
-
const message = `setup complete: claude-code + ${setupAgent}\n MCP server registered\n hooks configured\n conversation formatting instructions added`;
|
|
6947
|
+
const message = `setup complete: claude-code + ${setupAgent}\n MCP server registered\n hooks configured\n conversation formatting instructions added\n${setupReloadNotice("claude-code")}`;
|
|
6944
6948
|
deps.writeStdout(message);
|
|
6945
6949
|
return message;
|
|
6946
6950
|
}
|
|
@@ -6954,7 +6958,7 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
6954
6958
|
message: "dev tool setup complete",
|
|
6955
6959
|
meta: { tool, agent: setupAgent, runtimeMode, platform },
|
|
6956
6960
|
});
|
|
6957
|
-
const message = `setup complete: codex + ${setupAgent}\n MCP server registered`;
|
|
6961
|
+
const message = `setup complete: codex + ${setupAgent}\n MCP server registered\n${setupReloadNotice("codex")}`;
|
|
6958
6962
|
deps.writeStdout(message);
|
|
6959
6963
|
return message;
|
|
6960
6964
|
}
|
|
@@ -6981,6 +6985,32 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
6981
6985
|
deps.writeStdout(message);
|
|
6982
6986
|
return message;
|
|
6983
6987
|
}
|
|
6988
|
+
if (command.kind === "mcp.doctor") {
|
|
6989
|
+
const canarySocketPath = command.socketOverride ?? deps.socketPath;
|
|
6990
|
+
const result = await (0, mcp_canary_1.runMcpStatusCanary)({
|
|
6991
|
+
agent: command.agent,
|
|
6992
|
+
socketPath: canarySocketPath,
|
|
6993
|
+
command: process.execPath,
|
|
6994
|
+
commandArgs: [
|
|
6995
|
+
path.join(__dirname, "ouro-bot-entry.js"),
|
|
6996
|
+
"mcp-serve",
|
|
6997
|
+
"--agent",
|
|
6998
|
+
command.agent,
|
|
6999
|
+
"--socket",
|
|
7000
|
+
canarySocketPath,
|
|
7001
|
+
],
|
|
7002
|
+
});
|
|
7003
|
+
if (!result.ok)
|
|
7004
|
+
deps.setExitCode?.(1);
|
|
7005
|
+
const jsonPayload = result.ok || result.repair
|
|
7006
|
+
? { ...result, repair: result.repair ?? (0, mcp_canary_1.buildMcpBridgeRepairGuidance)(command.agent) }
|
|
7007
|
+
: { ...result, nextSteps: (0, mcp_canary_1.buildMcpDoctorNextSteps)(result, command.agent) };
|
|
7008
|
+
const message = command.json
|
|
7009
|
+
? JSON.stringify(jsonPayload, null, 2)
|
|
7010
|
+
: (0, mcp_canary_1.formatMcpStatusDoctorResult)(result, command.agent);
|
|
7011
|
+
deps.writeStdout(message);
|
|
7012
|
+
return message;
|
|
7013
|
+
}
|
|
6984
7014
|
/* v8 ignore start — mcp-serve block binds to process.stdin/stdout; tested via mcp-server unit tests */
|
|
6985
7015
|
// ── mcp-serve: start MCP server in-process on stdin/stdout ──
|
|
6986
7016
|
if (command.kind === "mcp-serve") {
|
|
@@ -335,8 +335,8 @@ exports.COMMAND_REGISTRY = {
|
|
|
335
335
|
category: "System",
|
|
336
336
|
description: "Interact with MCP servers",
|
|
337
337
|
usage: "ouro mcp <subcommand>",
|
|
338
|
-
example: "ouro mcp
|
|
339
|
-
subcommands: ["list", "call", "canary"],
|
|
338
|
+
example: "ouro mcp doctor --agent ouroboros",
|
|
339
|
+
subcommands: ["list", "call", "canary", "doctor"],
|
|
340
340
|
},
|
|
341
341
|
"mcp-serve": {
|
|
342
342
|
category: "System",
|
|
@@ -134,6 +134,7 @@ function usage() {
|
|
|
134
134
|
" ouro session list [--agent <name>]",
|
|
135
135
|
" ouro mcp list",
|
|
136
136
|
" ouro mcp call <server> <tool> [--args '{...}']",
|
|
137
|
+
" ouro mcp doctor --agent <name> [--json]",
|
|
137
138
|
" ouro rollback [<version>]",
|
|
138
139
|
" ouro versions",
|
|
139
140
|
" ouro clone <remote> [--agent <name>]",
|
|
@@ -1458,6 +1459,31 @@ function parseMcpCommand(args) {
|
|
|
1458
1459
|
...(json ? { json: true } : {}),
|
|
1459
1460
|
};
|
|
1460
1461
|
}
|
|
1462
|
+
if (sub === "doctor") {
|
|
1463
|
+
let socketOverride;
|
|
1464
|
+
let json = false;
|
|
1465
|
+
for (let i = 0; i < rest.length; i++) {
|
|
1466
|
+
if (rest[i] === "--socket") {
|
|
1467
|
+
if (!rest[i + 1])
|
|
1468
|
+
throw new Error("mcp doctor requires a value after --socket");
|
|
1469
|
+
socketOverride = rest[++i];
|
|
1470
|
+
continue;
|
|
1471
|
+
}
|
|
1472
|
+
if (rest[i] === "--json") {
|
|
1473
|
+
json = true;
|
|
1474
|
+
continue;
|
|
1475
|
+
}
|
|
1476
|
+
throw new Error(`Unknown mcp doctor flag: ${rest[i]}`);
|
|
1477
|
+
}
|
|
1478
|
+
if (!agent)
|
|
1479
|
+
throw new Error("mcp doctor requires --agent <name>");
|
|
1480
|
+
return {
|
|
1481
|
+
kind: "mcp.doctor",
|
|
1482
|
+
agent,
|
|
1483
|
+
...(socketOverride ? { socketOverride } : {}),
|
|
1484
|
+
...(json ? { json: true } : {}),
|
|
1485
|
+
};
|
|
1486
|
+
}
|
|
1461
1487
|
throw new Error(`Usage\n${usage()}`);
|
|
1462
1488
|
}
|
|
1463
1489
|
function inferAgentNameFromRemote(remote) {
|
|
@@ -33,14 +33,20 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.DEFAULT_CANARY_TIMEOUT_MS = void 0;
|
|
37
|
+
exports.buildMcpBridgeRepairGuidance = buildMcpBridgeRepairGuidance;
|
|
38
|
+
exports.formatMcpBridgeRepairDetails = formatMcpBridgeRepairDetails;
|
|
39
|
+
exports.buildMcpDoctorNextSteps = buildMcpDoctorNextSteps;
|
|
40
|
+
exports.formatMcpDoctorNextStepDetails = formatMcpDoctorNextStepDetails;
|
|
36
41
|
exports.parseMcpStatusText = parseMcpStatusText;
|
|
37
42
|
exports.runMcpStatusCanary = runMcpStatusCanary;
|
|
38
43
|
exports.formatMcpStatusCanaryResult = formatMcpStatusCanaryResult;
|
|
44
|
+
exports.formatMcpStatusDoctorResult = formatMcpStatusDoctorResult;
|
|
39
45
|
exports.createMcpStatusCanaryProbe = createMcpStatusCanaryProbe;
|
|
40
46
|
const child_process_1 = require("child_process");
|
|
41
47
|
const path = __importStar(require("path"));
|
|
42
48
|
const runtime_1 = require("../../nerves/runtime");
|
|
43
|
-
|
|
49
|
+
exports.DEFAULT_CANARY_TIMEOUT_MS = 60_000;
|
|
44
50
|
const MCP_PROTOCOL_VERSION = "2024-11-05";
|
|
45
51
|
function defaultCommandArgs(agent, socketPath) {
|
|
46
52
|
const entryPath = path.join(__dirname, "ouro-bot-entry.js");
|
|
@@ -75,6 +81,65 @@ function parseFields(line) {
|
|
|
75
81
|
}
|
|
76
82
|
return parsed;
|
|
77
83
|
}
|
|
84
|
+
function shellArg(value) {
|
|
85
|
+
return /^[A-Za-z0-9._/-]+$/.test(value)
|
|
86
|
+
? value
|
|
87
|
+
: `'${value.replace(/'/g, "'\\''")}'`;
|
|
88
|
+
}
|
|
89
|
+
function buildMcpBridgeRepairGuidance(agent) {
|
|
90
|
+
const agentArg = shellArg(agent);
|
|
91
|
+
return {
|
|
92
|
+
actor: "agent-runnable",
|
|
93
|
+
commands: [
|
|
94
|
+
`ouro setup --tool codex --agent ${agentArg}`,
|
|
95
|
+
`ouro setup --tool claude-code --agent ${agentArg}`,
|
|
96
|
+
],
|
|
97
|
+
reload: "open a fresh dev-tool session after setup; existing MCP processes keep their old runtime",
|
|
98
|
+
verify: `ouro mcp doctor --agent ${agentArg}`,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function formatMcpBridgeRepairDetails(repair) {
|
|
102
|
+
return [
|
|
103
|
+
`repair actor=${repair.actor}`,
|
|
104
|
+
...repair.commands.map((command) => `repair command=${command}`),
|
|
105
|
+
`reload required: ${repair.reload}`,
|
|
106
|
+
`verify command=${repair.verify}`,
|
|
107
|
+
];
|
|
108
|
+
}
|
|
109
|
+
function buildMcpDoctorNextSteps(result, agent) {
|
|
110
|
+
const agentArg = shellArg(agent);
|
|
111
|
+
const text = [result.summary, ...result.details].join("\n");
|
|
112
|
+
const daemonUnreachable = text.includes("daemon=unreachable")
|
|
113
|
+
|| text.includes("ECONNREFUSED")
|
|
114
|
+
|| text.includes("ENOENT");
|
|
115
|
+
if (daemonUnreachable) {
|
|
116
|
+
return {
|
|
117
|
+
actor: "agent-runnable",
|
|
118
|
+
commands: [
|
|
119
|
+
"ouro up",
|
|
120
|
+
`ouro mcp doctor --agent ${agentArg}`,
|
|
121
|
+
],
|
|
122
|
+
note: "start or refresh the daemon before trusting bridge status",
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
actor: "agent-runnable",
|
|
127
|
+
commands: [
|
|
128
|
+
"ouro doctor",
|
|
129
|
+
`ouro status --agent ${agentArg}`,
|
|
130
|
+
`ouro repair --agent ${agentArg}`,
|
|
131
|
+
`ouro mcp doctor --agent ${agentArg}`,
|
|
132
|
+
],
|
|
133
|
+
note: "fix reported daemon or sense health before rerunning bridge registration repair",
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
function formatMcpDoctorNextStepDetails(nextSteps) {
|
|
137
|
+
return [
|
|
138
|
+
`next actor=${nextSteps.actor}`,
|
|
139
|
+
...nextSteps.commands.map((command) => `next command=${command}`),
|
|
140
|
+
`next note=${nextSteps.note}`,
|
|
141
|
+
];
|
|
142
|
+
}
|
|
78
143
|
function parseMcpStatusText(text) {
|
|
79
144
|
const daemon = {};
|
|
80
145
|
const senses = {};
|
|
@@ -105,9 +170,10 @@ function validateMcpStatus(parsed, requiredSenses, options = {}) {
|
|
|
105
170
|
if (!options.ignoreOverviewHealth && parsed.daemon.health !== "ok") {
|
|
106
171
|
failures.push(`health=${parsed.daemon.health ?? "missing"}`);
|
|
107
172
|
}
|
|
108
|
-
|
|
173
|
+
const hasVersionMismatch = !!(parsed.daemon.daemonVersion &&
|
|
109
174
|
parsed.daemon.mcpVersion &&
|
|
110
|
-
parsed.daemon.daemonVersion !== parsed.daemon.mcpVersion)
|
|
175
|
+
parsed.daemon.daemonVersion !== parsed.daemon.mcpVersion);
|
|
176
|
+
if (hasVersionMismatch) {
|
|
111
177
|
failures.push(`version mismatch daemon=${parsed.daemon.daemonVersion} mcp=${parsed.daemon.mcpVersion}`);
|
|
112
178
|
}
|
|
113
179
|
for (const [sense, row] of Object.entries(parsed.senses)) {
|
|
@@ -133,15 +199,20 @@ function validateMcpStatus(parsed, requiredSenses, options = {}) {
|
|
|
133
199
|
const summary = failures.length === 0
|
|
134
200
|
? `mcp canary ok: daemon=${parsed.daemon.daemon} health=${parsed.daemon.health}${options.ignoreOverviewHealth ? " (overview ignored)" : ""} senses=${senseSummary}`
|
|
135
201
|
: `mcp canary failed: ${failures.join("; ")}`;
|
|
202
|
+
const repair = hasVersionMismatch && options.agent
|
|
203
|
+
? buildMcpBridgeRepairGuidance(options.agent)
|
|
204
|
+
: undefined;
|
|
205
|
+
const repairDetails = repair ? formatMcpBridgeRepairDetails(repair) : [];
|
|
136
206
|
return {
|
|
137
207
|
ok: failures.length === 0,
|
|
138
208
|
summary,
|
|
139
|
-
details: failures.length === 0 ? [parsed.raw] : [...failures, parsed.raw],
|
|
209
|
+
details: failures.length === 0 ? [parsed.raw] : [...failures, ...repairDetails, parsed.raw],
|
|
140
210
|
parsed,
|
|
211
|
+
...(repair ? { repair } : {}),
|
|
141
212
|
};
|
|
142
213
|
}
|
|
143
214
|
async function runMcpStatusCanary(options) {
|
|
144
|
-
const timeoutMs = options.timeoutMs ?? DEFAULT_CANARY_TIMEOUT_MS;
|
|
215
|
+
const timeoutMs = options.timeoutMs ?? exports.DEFAULT_CANARY_TIMEOUT_MS;
|
|
145
216
|
/* v8 ignore next -- default spawn is exercised by live canaries, while unit tests inject a fake child @preserve */
|
|
146
217
|
const spawnImpl = options.spawnImpl ?? child_process_1.spawn;
|
|
147
218
|
const command = options.command ?? process.execPath;
|
|
@@ -252,6 +323,7 @@ async function runMcpStatusCanary(options) {
|
|
|
252
323
|
}
|
|
253
324
|
const parsed = parseMcpStatusText(responseText(statusResponse));
|
|
254
325
|
const canary = validateMcpStatus(parsed, requiredSenses, {
|
|
326
|
+
agent: options.agent,
|
|
255
327
|
ignoreOverviewHealth: options.ignoreOverviewHealth,
|
|
256
328
|
});
|
|
257
329
|
(0, runtime_1.emitNervesEvent)({
|
|
@@ -286,6 +358,26 @@ function formatMcpStatusCanaryResult(result) {
|
|
|
286
358
|
...result.details.map((line) => ` ${line}`),
|
|
287
359
|
].join("\n");
|
|
288
360
|
}
|
|
361
|
+
function formatMcpStatusDoctorResult(result, agent) {
|
|
362
|
+
const rawDetails = result.details.filter((line) => !line.startsWith("repair ")
|
|
363
|
+
&& !line.startsWith("reload required:")
|
|
364
|
+
&& !line.startsWith("verify command="));
|
|
365
|
+
const lines = [
|
|
366
|
+
result.ok ? "mcp doctor: ok" : "mcp doctor: failed",
|
|
367
|
+
result.summary,
|
|
368
|
+
...rawDetails.map((line) => ` ${line}`),
|
|
369
|
+
];
|
|
370
|
+
if (result.ok || result.repair) {
|
|
371
|
+
const repair = result.repair ?? buildMcpBridgeRepairGuidance(agent);
|
|
372
|
+
lines.push("bridge registration path:");
|
|
373
|
+
lines.push(...formatMcpBridgeRepairDetails(repair).map((line) => ` ${line}`));
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
lines.push("next checks:");
|
|
377
|
+
lines.push(...formatMcpDoctorNextStepDetails(buildMcpDoctorNextSteps(result, agent)).map((line) => ` ${line}`));
|
|
378
|
+
}
|
|
379
|
+
return lines.join("\n");
|
|
380
|
+
}
|
|
289
381
|
function createMcpStatusCanaryProbe(options) {
|
|
290
382
|
return {
|
|
291
383
|
name: `mcp-canary:${options.agent}`,
|