@ouro.bot/cli 0.1.0-alpha.659 → 0.1.0-alpha.660
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 +7 -0
- package/dist/heart/daemon/cli-exec.js +41 -1
- package/dist/heart/daemon/cli-parse.js +22 -1
- package/dist/heart/daemon/daemon.js +4 -0
- package/dist/heart/mcp/mcp-server.js +4 -0
- package/dist/heart/turn-context.js +9 -1
- package/dist/mind/prompt.js +10 -2
- package/dist/repertoire/mcp-manager.js +42 -11
- package/dist/senses/shared-turn.js +3 -2
- package/package.json +1 -1
package/changelog.json
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
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.660",
|
|
6
|
+
"changes": [
|
|
7
|
+
"Add `ouro mcp-serve --workbench-mcp [<path>]` so Ouro Workbench injects its `ouro_workbench` MCP into the boss agent's turn at runtime — merged per-turn and per-agent in the daemon with no cross-agent leak — instead of writing a machine-specific entry into the git-synced agent bundle.",
|
|
8
|
+
"Drop the Workbench MCP bundle-write from the boss path (the explicit `ouro connect workbench` opt-in escape hatch is unchanged) and update the agent self-model copy so the boss understands it receives `ouro_workbench` via runtime injection rather than treating the missing bundle entry as a blocked or trust-level state."
|
|
9
|
+
]
|
|
10
|
+
},
|
|
4
11
|
{
|
|
5
12
|
"version": "0.1.0-alpha.659",
|
|
6
13
|
"changes": [
|
|
@@ -46,6 +46,7 @@ exports.mergeStartupStability = mergeStartupStability;
|
|
|
46
46
|
exports.ensureDaemonRunning = ensureDaemonRunning;
|
|
47
47
|
exports.listGithubCopilotModels = listGithubCopilotModels;
|
|
48
48
|
exports.checkManualCloneBundles = checkManualCloneBundles;
|
|
49
|
+
exports.resolveWorkbenchRuntimeMcp = resolveWorkbenchRuntimeMcp;
|
|
49
50
|
exports.resolveMailImportFilePath = resolveMailImportFilePath;
|
|
50
51
|
exports.runOuroCli = runOuroCli;
|
|
51
52
|
const child_process_1 = require("child_process");
|
|
@@ -2540,6 +2541,28 @@ function findInstalledWorkbenchMcp(deps, preferred) {
|
|
|
2540
2541
|
];
|
|
2541
2542
|
return candidates.find((candidate) => cliPathExists(deps, candidate)) ?? null;
|
|
2542
2543
|
}
|
|
2544
|
+
/**
|
|
2545
|
+
* Resolve the `--workbench-mcp [<path>]` flag into a per-turn runtime MCP
|
|
2546
|
+
* override for the `ouro mcp-serve` bridge, or null when the flag is absent /
|
|
2547
|
+
* unresolvable.
|
|
2548
|
+
*
|
|
2549
|
+
* - `undefined` (flag not passed) → null (no runtime injection).
|
|
2550
|
+
* - `true` (bare opt-in) → self-discover the installed OuroWorkbenchMCP.
|
|
2551
|
+
* - a string path → use it if it exists, otherwise fall back to discovery
|
|
2552
|
+
* (treating the string as the preferred candidate).
|
|
2553
|
+
*
|
|
2554
|
+
* Returns `{ ouro_workbench: { command, args: [] } }` so the daemon merges it
|
|
2555
|
+
* into the boss agent's toolset for the turn without writing to agent.json.
|
|
2556
|
+
*/
|
|
2557
|
+
function resolveWorkbenchRuntimeMcp(flag, deps) {
|
|
2558
|
+
if (flag === undefined)
|
|
2559
|
+
return null;
|
|
2560
|
+
const preferred = typeof flag === "string" ? flag : null;
|
|
2561
|
+
const command = findInstalledWorkbenchMcp(deps, preferred);
|
|
2562
|
+
if (!command)
|
|
2563
|
+
return null;
|
|
2564
|
+
return { ouro_workbench: { command, args: [] } };
|
|
2565
|
+
}
|
|
2543
2566
|
function writeWorkbenchMcpRegistration(agent, executablePath, deps) {
|
|
2544
2567
|
const { configPath } = (0, auth_flow_1.readAgentConfigForAgent)(agent, deps.bundlesRoot);
|
|
2545
2568
|
enableAgentSense(agent, "workbench", deps);
|
|
@@ -2758,6 +2781,14 @@ async function buildConnectMenu(agent, deps, onProgress) {
|
|
|
2758
2781
|
installedWorkbenchMcp
|
|
2759
2782
|
? `OuroWorkbenchMCP found: ${installedWorkbenchMcp}`
|
|
2760
2783
|
: "OuroWorkbenchMCP not found in ~/Applications or /Applications",
|
|
2784
|
+
// Runtime injection is the boss's normal path: the Workbench app launches
|
|
2785
|
+
// its boss with `ouro mcp-serve --workbench-mcp`, which injects the
|
|
2786
|
+
// ouro_workbench MCP per-turn without any agent.json entry. A blank/“not
|
|
2787
|
+
// registered” bundle state above is expected for a runtime-only boss; this
|
|
2788
|
+
// `connect workbench` command only writes a persistent entry as an opt-in.
|
|
2789
|
+
installedWorkbenchMcp
|
|
2790
|
+
? "boss runtime injection: when the Workbench app launches this agent it injects ouro_workbench per-turn; an agent.json entry is not required (this command writes one as an opt-in escape hatch)."
|
|
2791
|
+
: "boss runtime injection: install Ouro Workbench.app so the app can inject ouro_workbench per-turn when it launches this agent as boss (no agent.json entry required).",
|
|
2761
2792
|
];
|
|
2762
2793
|
const entries = [
|
|
2763
2794
|
{
|
|
@@ -6674,19 +6705,28 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
6674
6705
|
const { createMcpServer } = await Promise.resolve().then(() => __importStar(require("../mcp/mcp-server")));
|
|
6675
6706
|
const friendId = command.friendId ?? `local-${os.userInfo().username}`;
|
|
6676
6707
|
const mcpSocketPath = command.socketOverride ?? deps.socketPath;
|
|
6708
|
+
// Resolve the optional --workbench-mcp flag into a concrete runtime MCP
|
|
6709
|
+
// override. A string is taken as the explicit binary path (falling back to
|
|
6710
|
+
// discovery if it does not exist); a boolean opt-in (true) self-discovers
|
|
6711
|
+
// the installed OuroWorkbenchMCP. The resolved override is forwarded by the
|
|
6712
|
+
// bridge on every senseTurn and merged per-turn in the daemon — nothing is
|
|
6713
|
+
// written to agent.json.
|
|
6714
|
+
const workbenchMcpFlag = command.workbenchMcp;
|
|
6715
|
+
const runtimeMcp = resolveWorkbenchRuntimeMcp(workbenchMcpFlag, deps);
|
|
6677
6716
|
const server = createMcpServer({
|
|
6678
6717
|
agent: command.agent,
|
|
6679
6718
|
friendId,
|
|
6680
6719
|
socketPath: mcpSocketPath,
|
|
6681
6720
|
stdin: process.stdin,
|
|
6682
6721
|
stdout: process.stdout,
|
|
6722
|
+
...(runtimeMcp ? { runtimeMcp } : {}),
|
|
6683
6723
|
});
|
|
6684
6724
|
server.start();
|
|
6685
6725
|
(0, runtime_1.emitNervesEvent)({
|
|
6686
6726
|
component: "daemon",
|
|
6687
6727
|
event: "daemon.mcp_serve_started",
|
|
6688
6728
|
message: "MCP server started via CLI",
|
|
6689
|
-
meta: { agent: command.agent, friendId },
|
|
6729
|
+
meta: { agent: command.agent, friendId, workbenchRuntimeMcp: !!runtimeMcp },
|
|
6690
6730
|
});
|
|
6691
6731
|
// Keep process alive until stdin closes
|
|
6692
6732
|
await new Promise((resolve) => {
|
|
@@ -1293,6 +1293,11 @@ function parseMcpServeCommand(args) {
|
|
|
1293
1293
|
let agent;
|
|
1294
1294
|
let friendId;
|
|
1295
1295
|
let socketOverride;
|
|
1296
|
+
// Optional runtime Workbench MCP injection. The path is OPTIONAL: when a
|
|
1297
|
+
// concrete path follows the flag it is carried verbatim; when the flag stands
|
|
1298
|
+
// alone (or is immediately followed by another `--flag`) it is a boolean
|
|
1299
|
+
// opt-in and the daemon self-discovers the installed OuroWorkbenchMCP binary.
|
|
1300
|
+
let workbenchMcp;
|
|
1296
1301
|
for (let i = 0; i < args.length; i++) {
|
|
1297
1302
|
if (args[i] === "--agent" && args[i + 1]) {
|
|
1298
1303
|
agent = args[++i];
|
|
@@ -1306,10 +1311,26 @@ function parseMcpServeCommand(args) {
|
|
|
1306
1311
|
socketOverride = args[++i];
|
|
1307
1312
|
continue;
|
|
1308
1313
|
}
|
|
1314
|
+
if (args[i] === "--workbench-mcp") {
|
|
1315
|
+
const next = args[i + 1];
|
|
1316
|
+
if (next && !next.startsWith("--")) {
|
|
1317
|
+
workbenchMcp = args[++i];
|
|
1318
|
+
}
|
|
1319
|
+
else {
|
|
1320
|
+
workbenchMcp = true;
|
|
1321
|
+
}
|
|
1322
|
+
continue;
|
|
1323
|
+
}
|
|
1309
1324
|
}
|
|
1310
1325
|
if (!agent)
|
|
1311
1326
|
throw new Error("mcp-serve requires --agent <name>");
|
|
1312
|
-
return {
|
|
1327
|
+
return {
|
|
1328
|
+
kind: "mcp-serve",
|
|
1329
|
+
agent,
|
|
1330
|
+
...(friendId ? { friendId } : {}),
|
|
1331
|
+
...(socketOverride ? { socketOverride } : {}),
|
|
1332
|
+
...(workbenchMcp !== undefined ? { workbenchMcp } : {}),
|
|
1333
|
+
};
|
|
1313
1334
|
}
|
|
1314
1335
|
function parseSetupCommand(args) {
|
|
1315
1336
|
let tool;
|
|
@@ -472,6 +472,10 @@ async function handleAgentSenseTurn(command, runtime) {
|
|
|
472
472
|
friendId: command.friendId,
|
|
473
473
|
userMessage: command.message,
|
|
474
474
|
...(runtime?.socketPath ? { toolContext: { daemonSocketPath: runtime.socketPath } } : {}),
|
|
475
|
+
// Per-turn, per-agent runtime MCP injection (e.g. Workbench's ouro_workbench).
|
|
476
|
+
// Scoped to THIS turn only — never stored as module state, so a concurrent
|
|
477
|
+
// turn for a different agent cannot inherit these servers.
|
|
478
|
+
...(command.runtimeMcp ? { runtimeMcpServers: command.runtimeMcp } : {}),
|
|
475
479
|
});
|
|
476
480
|
return {
|
|
477
481
|
ok: true,
|
|
@@ -165,6 +165,7 @@ function canonicalizeMcpFriendId(agent, rawFriendId) {
|
|
|
165
165
|
function createMcpServer(options) {
|
|
166
166
|
const { agent, socketPath, stdin, stdout } = options;
|
|
167
167
|
const rawFriendId = options.friendId;
|
|
168
|
+
const runtimeMcp = options.runtimeMcp;
|
|
168
169
|
const friendId = canonicalizeMcpFriendId(agent, rawFriendId);
|
|
169
170
|
let buffer = "";
|
|
170
171
|
let running = false;
|
|
@@ -374,6 +375,9 @@ function createMcpServer(options) {
|
|
|
374
375
|
channel: "mcp",
|
|
375
376
|
sessionKey: sessionId,
|
|
376
377
|
message,
|
|
378
|
+
// Forward the resolved runtime MCP override (if any) so the daemon merges
|
|
379
|
+
// it into this agent's toolset for the turn. Per-turn parameter data only.
|
|
380
|
+
...(runtimeMcp ? { runtimeMcp } : {}),
|
|
377
381
|
});
|
|
378
382
|
/* v8 ignore next -- branch: ?? fallback for empty daemon response @preserve */
|
|
379
383
|
const text = response.message ?? "(empty response)";
|
|
@@ -213,7 +213,15 @@ function readSenseStatusLines() {
|
|
|
213
213
|
},
|
|
214
214
|
{
|
|
215
215
|
label: "Workbench",
|
|
216
|
-
|
|
216
|
+
// Workbench can be injected at runtime (the Workbench app spawns
|
|
217
|
+
// `ouro mcp-serve --workbench-mcp` for its boss) with NO agent.json entry.
|
|
218
|
+
// A disabled/needs_config row here does NOT mean the tools are absent —
|
|
219
|
+
// the authoritative signal is whether `workbench_*` tools are in the
|
|
220
|
+
// toolset this turn. The annotation prevents the agent from misreading the
|
|
221
|
+
// row as "blocked".
|
|
222
|
+
status: !senses.workbench.enabled
|
|
223
|
+
? "disabled (runtime-injected when launched by Workbench app)"
|
|
224
|
+
: configured.workbench ? "ready" : "needs_config",
|
|
217
225
|
},
|
|
218
226
|
];
|
|
219
227
|
return rows.map((row) => `- ${row.label}: ${row.status}`);
|
package/dist/mind/prompt.js
CHANGED
|
@@ -492,7 +492,15 @@ function localSenseStatusLines() {
|
|
|
492
492
|
},
|
|
493
493
|
{
|
|
494
494
|
label: "Workbench",
|
|
495
|
-
|
|
495
|
+
// Workbench can be injected at runtime (the Workbench app spawns
|
|
496
|
+
// `ouro mcp-serve --workbench-mcp` for its boss) with NO agent.json entry.
|
|
497
|
+
// A disabled/needs_config row here does NOT mean the tools are absent —
|
|
498
|
+
// the authoritative signal is whether `workbench_*` tools are in the
|
|
499
|
+
// toolset this turn. The annotation prevents the agent from misreading the
|
|
500
|
+
// row as "blocked".
|
|
501
|
+
status: !senses.workbench.enabled
|
|
502
|
+
? "disabled (runtime-injected when launched by Workbench app)"
|
|
503
|
+
: configured.workbench ? "ready" : "needs_config",
|
|
496
504
|
},
|
|
497
505
|
];
|
|
498
506
|
return rows.map((row) => `- ${row.label}: ${row.status}`);
|
|
@@ -512,7 +520,7 @@ function senseRuntimeGuidance(channel, preReadStatusLines) {
|
|
|
512
520
|
lines.push("teams setup truth: run `ouro connect teams --agent <agent>` from the connect bay; it stores Teams runtime/config fields and enables `senses.teams.enabled`.");
|
|
513
521
|
lines.push("bluebubbles setup truth: run `ouro connect bluebubbles --agent <agent>` from the connect bay; it stores this machine's BlueBubbles URL/password/listener config in the agent vault machine runtime item.");
|
|
514
522
|
lines.push("a2a setup truth: run `ouro connect a2a --agent <agent>` to enable the A2A sense, `ouro a2a card --agent <agent> --base-url <public-url>` to publish an agent card, and `ouro a2a onboard --agent <agent> --card-url <peer-card-url>` to add a peer as an agent friend. A2A uses the existing friend trust model, not a separate trust registry.");
|
|
515
|
-
lines.push("workbench setup truth: Ouro Workbench is the local machine sense for terminal/TUI agents.
|
|
523
|
+
lines.push("workbench setup truth: Ouro Workbench is the local machine sense for terminal/TUI agents. When the Workbench app launches me as its boss it injects the `ouro_workbench` MCP into my turn at runtime (it spawns `ouro mcp-serve --agent <me> --workbench-mcp`), so I receive the tools for the served session without any `agent.json` `mcpServers.ouro_workbench` entry — nothing is written to the bundle, so this stays path-free and cross-machine clean. The authoritative signal that Workbench is active is simply that the `workbench_*` tools are present in my toolset this turn; the sense table or `agent.json` may still read as disabled/needs_config because no bundle entry exists, and that is expected under runtime injection — I do NOT treat that as blocked or as a trust-level problem. I observe and queue auditable Workbench actions through `workbench_status`, `workbench_sense`, `workbench_transcript_tail`, `workbench_search_transcripts`, `workbench_recovery_drill`, and `workbench_request_action`; raw provider secrets remain in the agent vault, and Apple notarization is unrelated to local use. The explicit `ouro connect workbench --agent <me>` command still writes a persistent bundle entry as an opt-in escape hatch, but the boss does not need it.");
|
|
516
524
|
lines.push("mail setup AX: if a human asks me to set up email, I do not hand them a terminal checklist. I guide the flow end-to-end: name the current phase, run agent-runnable commands myself with shell/tools when available, ask the human only for human-required facts or browser actions, wait for their reply, verify the result, then continue.");
|
|
517
525
|
lines.push("mail setup hard rule: never tell the human to run `ouro account ensure`, `ouro connect mail`, `ouro mail import-mbox`, `ouro status`, or `ouro doctor` for setup. Say what I am about to run, run it myself, and report the result. If my current surface cannot run shell/tools, I ask for a tool-capable Ouro setup session or companion to continue; I do not offload CLI operation to the human.");
|
|
518
526
|
lines.push("mail setup truth: Agent Mail uses Mailroom, not HEY OAuth/IMAP. For the full work substrate account, the agent-runnable command is `ouro account ensure --agent <agent> --owner-email <email> --source hey`; use `ouro connect mail --agent <agent> --owner-email <email> --source hey` for mail-only repair/provisioning, or `--no-delegated-source` for native-only mail. The detailed runbook is `docs/agent-mail-setup.md`.");
|
|
@@ -21,8 +21,17 @@ const RESTART_DELAY_MS = 1000;
|
|
|
21
21
|
* (re-read on each turn). Both code paths MUST use the same merge logic — if
|
|
22
22
|
* reconcile reads only builtin, plugin servers get classified as "removed"
|
|
23
23
|
* on the second turn and torn down. See alpha.635 fix.
|
|
24
|
+
*
|
|
25
|
+
* `runtimeServers` are per-turn, per-agent overrides (e.g. Workbench's
|
|
26
|
+
* `ouro_workbench`) supplied as PARAMETER data for the current turn — never read
|
|
27
|
+
* from module state. They merge with the HIGHEST precedence (after builtin), so
|
|
28
|
+
* a stale `agent.json` entry loses to the live runtime path. Because they are a
|
|
29
|
+
* parameter, a turn that omits them produces a merged set WITHOUT them, and
|
|
30
|
+
* `reconcile()` then tears the runtime server down — this is the no-leak
|
|
31
|
+
* invariant that keeps the runtime MCP from bleeding into a different agent's
|
|
32
|
+
* concurrent turn on the shared daemon.
|
|
24
33
|
*/
|
|
25
|
-
function buildMergedServerConfig() {
|
|
34
|
+
function buildMergedServerConfig(runtimeServers) {
|
|
26
35
|
const config = (0, identity_1.loadAgentConfig)();
|
|
27
36
|
const builtinServers = config.mcpServers ?? {};
|
|
28
37
|
const pluginServers = (0, plugin_mcp_1.listPluginMcpServers)();
|
|
@@ -37,6 +46,15 @@ function buildMergedServerConfig() {
|
|
|
37
46
|
for (const [name, cfg] of Object.entries(builtinServers)) {
|
|
38
47
|
mergedServers[name] = cfg;
|
|
39
48
|
}
|
|
49
|
+
// Runtime overrides win over both plugin and builtin (highest precedence).
|
|
50
|
+
// They are NOT recorded in pluginOrigins, so they surface as builtin-style
|
|
51
|
+
// (un-namespaced) tools — matching how an agent.json mcpServers entry would.
|
|
52
|
+
if (runtimeServers) {
|
|
53
|
+
for (const [name, cfg] of Object.entries(runtimeServers)) {
|
|
54
|
+
mergedServers[name] = cfg;
|
|
55
|
+
delete pluginOrigins[name];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
40
58
|
return { mergedServers, pluginOrigins };
|
|
41
59
|
}
|
|
42
60
|
class McpManager {
|
|
@@ -119,14 +137,19 @@ class McpManager {
|
|
|
119
137
|
}
|
|
120
138
|
return results;
|
|
121
139
|
}
|
|
122
|
-
/* v8 ignore start — reconcile: dynamic MCP server management, tested via integration @preserve */
|
|
123
140
|
/** Re-read agent config AND enabled-plugin .mcp.json files, then connect new
|
|
124
141
|
* servers / disconnect removed ones. Must include plugin-declared servers
|
|
125
142
|
* in the desired set — otherwise plugin servers (e.g. mcp__desk__*) are
|
|
126
|
-
* treated as "removed" on every call and get torn down between turns.
|
|
127
|
-
|
|
143
|
+
* treated as "removed" on every call and get torn down between turns.
|
|
144
|
+
*
|
|
145
|
+
* `runtimeServers` are the current turn's per-agent overrides. They MUST be
|
|
146
|
+
* passed on every reconcile for the agent that owns them, otherwise the
|
|
147
|
+
* runtime server (e.g. ouro_workbench) is classified as "removed" and torn
|
|
148
|
+
* down — which is exactly the desired no-leak behavior for a turn that omits
|
|
149
|
+
* them. */
|
|
150
|
+
async reconcile(runtimeServers) {
|
|
128
151
|
try {
|
|
129
|
-
const { mergedServers, pluginOrigins } = buildMergedServerConfig();
|
|
152
|
+
const { mergedServers, pluginOrigins } = buildMergedServerConfig(runtimeServers);
|
|
130
153
|
const currentNames = new Set(this.servers.keys());
|
|
131
154
|
const desiredNames = new Set(Object.keys(mergedServers));
|
|
132
155
|
// Connect new servers
|
|
@@ -151,6 +174,7 @@ class McpManager {
|
|
|
151
174
|
meta: { server: name },
|
|
152
175
|
});
|
|
153
176
|
const entry = this.servers.get(name);
|
|
177
|
+
/* v8 ignore next -- defensive: name comes from this.servers.keys() this same tick, so entry is always present; the guard only protects against an awaited connectServer crash-handler racing a delete @preserve */
|
|
154
178
|
if (entry)
|
|
155
179
|
entry.client.shutdown();
|
|
156
180
|
this.servers.delete(name);
|
|
@@ -167,7 +191,6 @@ class McpManager {
|
|
|
167
191
|
});
|
|
168
192
|
}
|
|
169
193
|
}
|
|
170
|
-
/* v8 ignore stop */
|
|
171
194
|
shutdown() {
|
|
172
195
|
this.shuttingDown = true;
|
|
173
196
|
// `_end` (not `_stop`) to pair with `mcp.manager_start` under the
|
|
@@ -346,22 +369,30 @@ let _sharedManagerPromise = null;
|
|
|
346
369
|
* Get or create a shared McpManager instance from the agent's config.
|
|
347
370
|
* Returns null if no mcpServers are configured.
|
|
348
371
|
* Safe to call from multiple senses — will only create one instance.
|
|
372
|
+
*
|
|
373
|
+
* `options.runtimeServers` are the current turn's per-agent MCP overrides (e.g.
|
|
374
|
+
* Workbench's `ouro_workbench`). They are PARAMETER data for this call only —
|
|
375
|
+
* passed into the merge for both the initial `start()` and every subsequent
|
|
376
|
+
* `reconcile()`, and never persisted as module state. A call that omits them
|
|
377
|
+
* reconciles to a set WITHOUT them, tearing any prior runtime server down — the
|
|
378
|
+
* no-leak invariant for the shared multi-agent daemon.
|
|
349
379
|
*/
|
|
350
|
-
async function getSharedMcpManager() {
|
|
380
|
+
async function getSharedMcpManager(options) {
|
|
381
|
+
const runtimeServers = options?.runtimeServers;
|
|
351
382
|
// If manager exists, reconcile to pick up config changes (new/removed servers)
|
|
352
|
-
|
|
383
|
+
// AND this turn's runtime overrides. Passing runtimeServers per-call is what
|
|
384
|
+
// scopes the runtime MCP to the active agent's turn.
|
|
353
385
|
if (_sharedManager) {
|
|
354
|
-
await _sharedManager.reconcile();
|
|
386
|
+
await _sharedManager.reconcile(runtimeServers);
|
|
355
387
|
return _sharedManager;
|
|
356
388
|
}
|
|
357
|
-
/* v8 ignore stop */
|
|
358
389
|
/* v8 ignore next -- race guard: deduplicates concurrent initialization calls @preserve */
|
|
359
390
|
if (_sharedManagerPromise)
|
|
360
391
|
return _sharedManagerPromise;
|
|
361
392
|
// Always re-check config — agent may have added servers since last call
|
|
362
393
|
_sharedManagerPromise = (async () => {
|
|
363
394
|
try {
|
|
364
|
-
const { mergedServers, pluginOrigins } = buildMergedServerConfig();
|
|
395
|
+
const { mergedServers, pluginOrigins } = buildMergedServerConfig(runtimeServers);
|
|
365
396
|
if (Object.keys(mergedServers).length === 0)
|
|
366
397
|
return null;
|
|
367
398
|
const manager = new McpManager();
|
|
@@ -230,8 +230,9 @@ async function runSenseTurn(options) {
|
|
|
230
230
|
resolverParams = { provider: "local", externalId: username, displayName: username, channel };
|
|
231
231
|
}
|
|
232
232
|
const resolver = new resolver_1.FriendResolver(friendStore, resolverParams);
|
|
233
|
-
// Initialize MCP manager so MCP tools appear as first-class tools in the agent's tool list
|
|
234
|
-
|
|
233
|
+
// Initialize MCP manager so MCP tools appear as first-class tools in the agent's tool list.
|
|
234
|
+
// Runtime MCP servers (e.g. Workbench's ouro_workbench) are passed per-turn for THIS agent only.
|
|
235
|
+
const mcpManager = await (0, mcp_manager_1.getSharedMcpManager)(options.runtimeMcpServers ? { runtimeServers: options.runtimeMcpServers } : undefined) ?? undefined;
|
|
235
236
|
// Session path and loading
|
|
236
237
|
const sessionDir = path.join(agentRoot, "state", "sessions", friendId, channel);
|
|
237
238
|
fs.mkdirSync(sessionDir, { recursive: true });
|