@nylorun/runtime 0.8.0-beta → 0.9.0-beta
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.md +17 -0
- package/dist/core/runtime.js +46 -17
- package/dist/core/store.d.ts +2 -1
- package/dist/core/store.js +9 -2
- package/dist/mcp/pool.d.ts +13 -1
- package/dist/mcp/pool.js +53 -19
- package/dist/mcp/snapshot.d.ts +4 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.0-beta
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 1cd7dc7: Add subagents: put an agent in another agent's `tools` (`Agent({ tools: [lookupOrder, researcher] })` or `.use({ tools: [researcher] })`) and the model can delegate to it. The tool is named after the agent's id, takes `{ task: string }`, and its description is the agent's `description`, which is now required for an agent used as a tool. The engine runs the child inside the parent's turn as a durable branch: fresh context, its own tools and hooks served by the root agent's executor, its own MCP servers, the session's sandbox, and only its final output (or `outputSchema` result) returned. Empty output, failures (with partial output marked as evidence), and requests for input or approval inside a child reach the parent as failed tool results. Parallel delegation calls run concurrently, completed child work is never re-run on replay, and cancelling the session cancels every child.
|
|
8
|
+
|
|
9
|
+
v1 is one level deep and non-interactive. Nested delegation, child tools that declare `approval`, and differing sandboxes across the tree fail the build with a named diagnostic. The manifest adds an optional `agent` body on a tool (schema version unchanged), actions and effects carry `agent: { id, path, delegationId }`, tool context gains `ctx.agent`, the durable host resolves a new `delegation` effect kind, and the Runtime emits `delegation.started` / `delegation.completed` events and filters history with `?agent=` (`session.history({ agent })`). Studio shows delegations, labels child actions with their agent, and filters events by agent.
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Pin core to the tested release.
|
|
14
|
+
- Pin harness to the tested release.
|
|
15
|
+
- Updated dependencies [1cd7dc7]
|
|
16
|
+
- Updated dependencies
|
|
17
|
+
- @nylorun/core@0.4.0-beta
|
|
18
|
+
- @nylorun/harness@0.18.0-beta
|
|
19
|
+
|
|
3
20
|
## 0.8.0-beta
|
|
4
21
|
|
|
5
22
|
### Minor Changes
|
package/dist/core/runtime.js
CHANGED
|
@@ -7,7 +7,7 @@ import { tmpdir } from "node:os";
|
|
|
7
7
|
import { AgentManifestSchema, CreateCredentialRequestSchema, CreateVaultRequestSchema, PutAgentRequestSchema, PutHostModelRequestSchema, SelectHostModelRequestSchema, PutSessionRequestSchema, RegisterExecutorsRequestSchema, RotateCredentialRequestSchema, SessionCommandSchema, ActionClaimRequestSchema, ActionHeartbeatRequestSchema, } from "@nylorun/core/contracts";
|
|
8
8
|
import { createDurableCheckpoint, runDurable, } from "@nylorun/harness/run";
|
|
9
9
|
import { hashManifest } from "@nylorun/core/compatibility";
|
|
10
|
-
import { schemaFromJSON, } from "@nylorun/core/define";
|
|
10
|
+
import { delegateManifest, schemaFromJSON, } from "@nylorun/core/define";
|
|
11
11
|
import { Store, canonical } from "./store.js";
|
|
12
12
|
import { ExecutorRegistry, assertExecutorCredential, hashToken, } from "./executors.js";
|
|
13
13
|
import { RUNTIME_VERSION } from "../version.js";
|
|
@@ -18,7 +18,7 @@ import { scrub } from "../redact.js";
|
|
|
18
18
|
import { createKekFile, defaultKekPath, readVaultKek } from "../vault/kek.js";
|
|
19
19
|
import { VaultError } from "../vault/error.js";
|
|
20
20
|
import { VaultService } from "../vault/service.js";
|
|
21
|
-
import { McpPool } from "../mcp/pool.js";
|
|
21
|
+
import { McpPool, serversOf } from "../mcp/pool.js";
|
|
22
22
|
import { SandboxManager, sandboxCapabilityOf } from "../sandbox/manager.js";
|
|
23
23
|
import { defaultSandboxBackends } from "../sandbox/select.js";
|
|
24
24
|
class HttpError extends Error {
|
|
@@ -35,6 +35,7 @@ function sessionToolsOf(snapshot) {
|
|
|
35
35
|
if (!snapshot?.mcpTools.length)
|
|
36
36
|
return undefined;
|
|
37
37
|
return snapshot.mcpTools.map((tool) => ({
|
|
38
|
+
...(tool.agentId === undefined ? {} : { agentId: tool.agentId }),
|
|
38
39
|
capabilityId: tool.capabilityId,
|
|
39
40
|
name: tool.name,
|
|
40
41
|
...(tool.description === undefined
|
|
@@ -46,8 +47,14 @@ function sessionToolsOf(snapshot) {
|
|
|
46
47
|
: { outputSchema: tool.outputSchema }),
|
|
47
48
|
}));
|
|
48
49
|
}
|
|
49
|
-
function mcpToolOf(session,
|
|
50
|
-
return session.mcpSnapshot?.mcpTools.find((tool) => tool.
|
|
50
|
+
function mcpToolOf(session, request) {
|
|
51
|
+
return session.mcpSnapshot?.mcpTools.find((tool) => tool.agentId === request.agent?.id &&
|
|
52
|
+
tool.capabilityId === request.capabilityId &&
|
|
53
|
+
tool.name === request.toolName);
|
|
54
|
+
}
|
|
55
|
+
/** The manifest of the agent an effect belongs to: the root, or an agent it uses as a tool. */
|
|
56
|
+
function manifestFor(manifest, agent) {
|
|
57
|
+
return agent ? delegateManifest(manifest, agent.id) : manifest;
|
|
51
58
|
}
|
|
52
59
|
function pinnedTool(manifest, capabilityId, toolName) {
|
|
53
60
|
const capability = manifest.capabilities.find((item) => item.id === capabilityId);
|
|
@@ -351,6 +358,7 @@ export class CoreRuntime {
|
|
|
351
358
|
this.store.put("sessions", s.id, s);
|
|
352
359
|
events.push(this.store.event(s.id, s.activeTurnId, "action.uncertain", {
|
|
353
360
|
actionId: action.actionId,
|
|
361
|
+
...(action.agent ? { agent: action.agent } : {}),
|
|
354
362
|
}));
|
|
355
363
|
}
|
|
356
364
|
}
|
|
@@ -509,11 +517,24 @@ export class CoreRuntime {
|
|
|
509
517
|
: "pending",
|
|
510
518
|
};
|
|
511
519
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
:
|
|
520
|
+
if (request.kind === "delegation") {
|
|
521
|
+
// Lifecycle points of an agent used as a tool: journaled once, so replays never re-emit.
|
|
522
|
+
const outcome = { value: null };
|
|
523
|
+
this.store.put("effects", request.effectId, {
|
|
524
|
+
request,
|
|
525
|
+
status: "completed",
|
|
526
|
+
outcome,
|
|
527
|
+
});
|
|
528
|
+
const settled = request.effectId.endsWith(":settled");
|
|
529
|
+
event = this.store.event(s.id, s.activeTurnId, settled ? "delegation.completed" : "delegation.started", { agent: request.agent, ...request.input });
|
|
530
|
+
return { status: "completed", outcome };
|
|
531
|
+
}
|
|
532
|
+
const agentManifest = manifestFor(s.manifest, request.agent);
|
|
533
|
+
if (!agentManifest)
|
|
534
|
+
throw new Error(`Agent '${request.agent?.id ?? ""}' is not used as a tool`);
|
|
535
|
+
const mcpTool = request.kind === "tool" ? mcpToolOf(s, request) : undefined;
|
|
515
536
|
const sandboxTool = request.kind === "tool" && !mcpTool
|
|
516
|
-
? sandboxCapabilityOf(
|
|
537
|
+
? sandboxCapabilityOf(agentManifest, request.capabilityId, request.toolName)
|
|
517
538
|
: undefined;
|
|
518
539
|
this.store.put("effects", request.effectId, {
|
|
519
540
|
request,
|
|
@@ -526,7 +547,7 @@ export class CoreRuntime {
|
|
|
526
547
|
return undefined;
|
|
527
548
|
}
|
|
528
549
|
const tool = request.kind === "tool"
|
|
529
|
-
? pinnedTool(
|
|
550
|
+
? pinnedTool(agentManifest, request.capabilityId, request.toolName)
|
|
530
551
|
: undefined;
|
|
531
552
|
const base = {
|
|
532
553
|
actionId: request.effectId,
|
|
@@ -541,6 +562,7 @@ export class CoreRuntime {
|
|
|
541
562
|
generation: 0,
|
|
542
563
|
claimId: null,
|
|
543
564
|
leaseExpiresAt: null,
|
|
565
|
+
...(request.agent ? { agent: request.agent } : {}),
|
|
544
566
|
};
|
|
545
567
|
const action = request.kind === "hook"
|
|
546
568
|
? {
|
|
@@ -907,7 +929,11 @@ export class CoreRuntime {
|
|
|
907
929
|
action.claimId = randomUUID();
|
|
908
930
|
action.leaseExpiresAt = new Date(Date.now() + (this.options.leaseMs ?? 30000)).toISOString();
|
|
909
931
|
this.store.put("actions", actionId, action);
|
|
910
|
-
event = this.store.event(action.sessionId, action.turnId, "action.claimed", {
|
|
932
|
+
event = this.store.event(action.sessionId, action.turnId, "action.claimed", {
|
|
933
|
+
actionId,
|
|
934
|
+
generation: action.generation,
|
|
935
|
+
...(action.agent ? { agent: action.agent } : {}),
|
|
936
|
+
});
|
|
911
937
|
return {
|
|
912
938
|
action,
|
|
913
939
|
claimId: action.claimId,
|
|
@@ -1127,7 +1153,7 @@ export class CoreRuntime {
|
|
|
1127
1153
|
? request.headers["last-event-id"]
|
|
1128
1154
|
: undefined);
|
|
1129
1155
|
if (method === "GET" && path[3] === "items")
|
|
1130
|
-
return json(this.store.history(id, cursor));
|
|
1156
|
+
return json(this.store.history(id, cursor, url.searchParams.get("agent") ?? undefined));
|
|
1131
1157
|
if (method === "GET" && path[3] === "events") {
|
|
1132
1158
|
const history = this.store.history(id, cursor);
|
|
1133
1159
|
const set = this.observers.get(id) ?? new Set();
|
|
@@ -1199,9 +1225,7 @@ export class CoreRuntime {
|
|
|
1199
1225
|
}
|
|
1200
1226
|
async prepareMcp(id, signal) {
|
|
1201
1227
|
const s = this.session(id);
|
|
1202
|
-
|
|
1203
|
-
Object.keys(capability.mcpServers).length > 0);
|
|
1204
|
-
if (!declared)
|
|
1228
|
+
if (serversOf(s.manifest).length === 0)
|
|
1205
1229
|
return;
|
|
1206
1230
|
if (!s.mcpSnapshot) {
|
|
1207
1231
|
const found = await this.mcp.discover({
|
|
@@ -1247,11 +1271,12 @@ export class CoreRuntime {
|
|
|
1247
1271
|
}
|
|
1248
1272
|
async callMcpTool(request) {
|
|
1249
1273
|
const s = this.session(request.sessionId);
|
|
1250
|
-
const tool = mcpToolOf(s, request
|
|
1274
|
+
const tool = mcpToolOf(s, request);
|
|
1251
1275
|
if (!tool)
|
|
1252
1276
|
throw new Error(`MCP tool '${request.toolName ?? ""}' is not in the session snapshot`);
|
|
1253
1277
|
return this.mcp.call({
|
|
1254
1278
|
sessionId: s.id,
|
|
1279
|
+
...(tool.agentId === undefined ? {} : { agentId: tool.agentId }),
|
|
1255
1280
|
capabilityId: tool.capabilityId,
|
|
1256
1281
|
serverName: tool.serverName,
|
|
1257
1282
|
serverToolName: tool.serverToolName,
|
|
@@ -1262,7 +1287,8 @@ export class CoreRuntime {
|
|
|
1262
1287
|
}
|
|
1263
1288
|
async callSandboxTool(request, signal) {
|
|
1264
1289
|
const s = this.session(request.sessionId);
|
|
1265
|
-
|
|
1290
|
+
// Agents used as tools share the session's sandbox; the tree declares one sandbox spec.
|
|
1291
|
+
const capability = sandboxCapabilityOf(manifestFor(s.manifest, request.agent), request.capabilityId, request.toolName);
|
|
1266
1292
|
if (!capability)
|
|
1267
1293
|
throw new Error(`'${request.toolName ?? ""}' is not a sandbox tool`);
|
|
1268
1294
|
return this.sandbox.run({ id: s.id, activeTurnId: s.activeTurnId, manifest: s.manifest }, capability, request.toolName, request.input, signal);
|
|
@@ -1414,5 +1440,8 @@ export async function startRuntime(options) {
|
|
|
1414
1440
|
}
|
|
1415
1441
|
/** What an action runs, for events: a tool name, or a hook point and its capabilities. */
|
|
1416
1442
|
function actionTarget(action) {
|
|
1417
|
-
return
|
|
1443
|
+
return {
|
|
1444
|
+
...(action.kind === "hook" ? { hook: action.hook } : { toolName: action.toolName }),
|
|
1445
|
+
...(action.agent ? { agent: action.agent } : {}),
|
|
1446
|
+
};
|
|
1418
1447
|
}
|
package/dist/core/store.d.ts
CHANGED
|
@@ -22,7 +22,8 @@ export declare class Store {
|
|
|
22
22
|
deleteExecutor(agentId: string): void;
|
|
23
23
|
event(sessionId: string, turnId: string | null, type: string, payload: unknown): LiveEvent;
|
|
24
24
|
credentialCount(): number;
|
|
25
|
-
|
|
25
|
+
/** `agent` keeps only events of one agent used as a tool, by delegationId or path. */
|
|
26
|
+
history(sessionId: string, cursor?: string, agent?: string): {
|
|
26
27
|
items: LiveEvent[];
|
|
27
28
|
cursor: string | null;
|
|
28
29
|
};
|
package/dist/core/store.js
CHANGED
|
@@ -164,7 +164,8 @@ export class Store {
|
|
|
164
164
|
.get();
|
|
165
165
|
return Number(row.n);
|
|
166
166
|
}
|
|
167
|
-
|
|
167
|
+
/** `agent` keeps only events of one agent used as a tool, by delegationId or path. */
|
|
168
|
+
history(sessionId, cursor, agent) {
|
|
168
169
|
let sequence = 0;
|
|
169
170
|
if (cursor) {
|
|
170
171
|
const decoded = Buffer.from(cursor, "base64url").toString();
|
|
@@ -177,7 +178,8 @@ export class Store {
|
|
|
177
178
|
const items = this.db
|
|
178
179
|
.prepare("SELECT body FROM events WHERE session_id=? AND sequence>? ORDER BY sequence")
|
|
179
180
|
.all(sessionId, sequence)
|
|
180
|
-
.map((r) => JSON.parse(String(r.body)))
|
|
181
|
+
.map((r) => JSON.parse(String(r.body)))
|
|
182
|
+
.filter((event) => agent === undefined || belongsTo(event, agent));
|
|
181
183
|
const last = this.db
|
|
182
184
|
.prepare("SELECT body FROM events WHERE session_id=? ORDER BY sequence DESC LIMIT 1")
|
|
183
185
|
.get(sessionId);
|
|
@@ -187,6 +189,11 @@ export class Store {
|
|
|
187
189
|
};
|
|
188
190
|
}
|
|
189
191
|
}
|
|
192
|
+
function belongsTo(event, agent) {
|
|
193
|
+
const ref = event.payload
|
|
194
|
+
?.agent;
|
|
195
|
+
return ref?.delegationId === agent || ref?.path === agent;
|
|
196
|
+
}
|
|
190
197
|
function migrateVaultScope(db) {
|
|
191
198
|
const columns = db.prepare("PRAGMA table_info(vaults)").all();
|
|
192
199
|
if (!columns.some((column) => column.name === "scope"))
|
package/dist/mcp/pool.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentManifest } from "@nylorun/core/define";
|
|
1
|
+
import type { AgentManifest, McpServerManifest } from "@nylorun/core/define";
|
|
2
2
|
import type { AuthorizeResult } from "../vault/service.js";
|
|
3
3
|
import { callMcpTool } from "./connect.js";
|
|
4
4
|
import { type McpDiagnostic, type McpSnapshot, type McpToolRecord } from "./snapshot.js";
|
|
@@ -31,6 +31,7 @@ export declare class McpPool {
|
|
|
31
31
|
}): Promise<McpDiagnostic[]>;
|
|
32
32
|
call(input: {
|
|
33
33
|
sessionId: string;
|
|
34
|
+
agentId?: string;
|
|
34
35
|
capabilityId: string;
|
|
35
36
|
serverName: string;
|
|
36
37
|
serverToolName: string;
|
|
@@ -43,3 +44,14 @@ export declare class McpPool {
|
|
|
43
44
|
private liveKey;
|
|
44
45
|
private connectDeclared;
|
|
45
46
|
}
|
|
47
|
+
interface DeclaredServer {
|
|
48
|
+
/** Absent for the session's root agent. */
|
|
49
|
+
readonly agentId?: string;
|
|
50
|
+
/** The manifest of the agent that declares the server. */
|
|
51
|
+
readonly manifest: AgentManifest;
|
|
52
|
+
readonly capabilityId: string;
|
|
53
|
+
readonly server: McpServerManifest;
|
|
54
|
+
}
|
|
55
|
+
/** Servers of the root agent, then of each agent it uses as a tool. */
|
|
56
|
+
export declare function serversOf(manifest: AgentManifest): DeclaredServer[];
|
|
57
|
+
export {};
|
package/dist/mcp/pool.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
|
+
import { delegatesOf } from "@nylorun/core/define";
|
|
2
3
|
import { callMcpTool, diagnosticFromError, listMcpTools, openMcpServer, } from "./connect.js";
|
|
3
4
|
import { declaredToolNames, } from "./snapshot.js";
|
|
4
5
|
export class McpPool {
|
|
@@ -8,7 +9,8 @@ export class McpPool {
|
|
|
8
9
|
this.options = options;
|
|
9
10
|
}
|
|
10
11
|
async discover(input) {
|
|
11
|
-
|
|
12
|
+
// Each agent names its own tools, so collisions are checked per agent.
|
|
13
|
+
const taken = new Map();
|
|
12
14
|
const tools = [];
|
|
13
15
|
const diagnostics = [];
|
|
14
16
|
for (const declared of serversOf(input.manifest)) {
|
|
@@ -19,13 +21,16 @@ export class McpPool {
|
|
|
19
21
|
continue;
|
|
20
22
|
}
|
|
21
23
|
try {
|
|
24
|
+
if (!taken.has(declared.agentId))
|
|
25
|
+
taken.set(declared.agentId, declaredToolNames(declared.manifest));
|
|
22
26
|
const listed = await listMcpTools(opened.connection.client, {
|
|
23
27
|
capabilityId: declared.capabilityId,
|
|
24
28
|
serverName: declared.server.name,
|
|
25
|
-
taken,
|
|
29
|
+
taken: taken.get(declared.agentId),
|
|
26
30
|
});
|
|
27
|
-
tools.push(...listed.tools);
|
|
31
|
+
tools.push(...listed.tools.map((tool) => owned(declared, tool)));
|
|
28
32
|
diagnostics.push({
|
|
33
|
+
...ownerOf(declared),
|
|
29
34
|
capabilityId: declared.capabilityId,
|
|
30
35
|
serverName: declared.server.name,
|
|
31
36
|
outcome: "connected",
|
|
@@ -37,7 +42,7 @@ export class McpPool {
|
|
|
37
42
|
}
|
|
38
43
|
catch (error) {
|
|
39
44
|
await opened.connection.close().catch(() => { });
|
|
40
|
-
diagnostics.push(diagnosticFromError(declared.capabilityId, declared.server.name, error));
|
|
45
|
+
diagnostics.push(owned(declared, diagnosticFromError(declared.capabilityId, declared.server.name, error)));
|
|
41
46
|
}
|
|
42
47
|
}
|
|
43
48
|
return {
|
|
@@ -53,16 +58,17 @@ export class McpPool {
|
|
|
53
58
|
const diagnostics = [];
|
|
54
59
|
const seen = new Set();
|
|
55
60
|
for (const tool of input.tools) {
|
|
56
|
-
const key =
|
|
61
|
+
const key = this.liveKey(input.sessionId, tool.agentId, tool.capabilityId, tool.serverName);
|
|
57
62
|
if (seen.has(key))
|
|
58
63
|
continue;
|
|
59
64
|
seen.add(key);
|
|
60
|
-
if (this.live.has(
|
|
65
|
+
if (this.live.has(key))
|
|
61
66
|
continue;
|
|
62
67
|
input.signal?.throwIfAborted();
|
|
63
|
-
const declared =
|
|
68
|
+
const declared = findServer(input.manifest, tool.agentId, tool.capabilityId, tool.serverName);
|
|
64
69
|
if (!declared) {
|
|
65
70
|
diagnostics.push({
|
|
71
|
+
...(tool.agentId === undefined ? {} : { agentId: tool.agentId }),
|
|
66
72
|
capabilityId: tool.capabilityId,
|
|
67
73
|
serverName: tool.serverName,
|
|
68
74
|
outcome: "failed",
|
|
@@ -80,10 +86,10 @@ export class McpPool {
|
|
|
80
86
|
return diagnostics;
|
|
81
87
|
}
|
|
82
88
|
async call(input) {
|
|
83
|
-
const key = this.liveKey(input.sessionId, input.capabilityId, input.serverName);
|
|
89
|
+
const key = this.liveKey(input.sessionId, input.agentId, input.capabilityId, input.serverName);
|
|
84
90
|
let live = this.live.get(key);
|
|
85
91
|
if (!live) {
|
|
86
|
-
const declared =
|
|
92
|
+
const declared = findServer(input.manifest, input.agentId, input.capabilityId, input.serverName);
|
|
87
93
|
if (!declared)
|
|
88
94
|
throw new Error(`MCP server '${input.serverName}' is not declared`);
|
|
89
95
|
const opened = await this.connectDeclared(input, declared);
|
|
@@ -102,21 +108,21 @@ export class McpPool {
|
|
|
102
108
|
await Promise.all(connections.map((item) => item.connection.close().catch(() => { })));
|
|
103
109
|
}
|
|
104
110
|
remember(sessionId, declared, connection) {
|
|
105
|
-
this.live.set(this.liveKey(sessionId, declared.capabilityId, declared.server.name), {
|
|
111
|
+
this.live.set(this.liveKey(sessionId, declared.agentId, declared.capabilityId, declared.server.name), {
|
|
106
112
|
capabilityId: declared.capabilityId,
|
|
107
113
|
serverName: declared.server.name,
|
|
108
114
|
connection,
|
|
109
115
|
});
|
|
110
116
|
}
|
|
111
|
-
liveKey(sessionId, capabilityId, serverName) {
|
|
112
|
-
return JSON.stringify([sessionId, capabilityId, serverName]);
|
|
117
|
+
liveKey(sessionId, agentId, capabilityId, serverName) {
|
|
118
|
+
return JSON.stringify([sessionId, agentId ?? null, capabilityId, serverName]);
|
|
113
119
|
}
|
|
114
120
|
async connectDeclared(input, declared) {
|
|
115
121
|
try {
|
|
116
122
|
const connection = await openMcpServer({
|
|
117
123
|
server: declared.server,
|
|
118
|
-
pluginRoot: input.pluginRoots[declared
|
|
119
|
-
pluginData: join(this.options.dataDir, "plugin-data", declared.
|
|
124
|
+
pluginRoot: input.pluginRoots[pluginKey(declared)],
|
|
125
|
+
pluginData: join(this.options.dataDir, "plugin-data", ...pluginKey(declared).split("/")),
|
|
120
126
|
authorize: declared.server.type === "stdio"
|
|
121
127
|
? undefined
|
|
122
128
|
: (url) => this.options.authorize(input.sessionId, {
|
|
@@ -129,15 +135,43 @@ export class McpPool {
|
|
|
129
135
|
catch (error) {
|
|
130
136
|
return {
|
|
131
137
|
ok: false,
|
|
132
|
-
diagnostic: diagnosticFromError(declared.capabilityId, declared.server.name, error),
|
|
138
|
+
diagnostic: owned(declared, diagnosticFromError(declared.capabilityId, declared.server.name, error)),
|
|
133
139
|
};
|
|
134
140
|
}
|
|
135
141
|
}
|
|
136
142
|
}
|
|
137
|
-
|
|
143
|
+
/** Servers of the root agent, then of each agent it uses as a tool. */
|
|
144
|
+
export function serversOf(manifest) {
|
|
138
145
|
const servers = [];
|
|
139
|
-
|
|
140
|
-
for (const
|
|
141
|
-
|
|
146
|
+
const add = (agent, agentId) => {
|
|
147
|
+
for (const capability of agent.capabilities)
|
|
148
|
+
for (const server of Object.values(capability.mcpServers ?? {}))
|
|
149
|
+
servers.push({
|
|
150
|
+
...(agentId === undefined ? {} : { agentId }),
|
|
151
|
+
manifest: agent,
|
|
152
|
+
capabilityId: capability.id,
|
|
153
|
+
server,
|
|
154
|
+
});
|
|
155
|
+
};
|
|
156
|
+
add(manifest);
|
|
157
|
+
for (const child of delegatesOf(manifest))
|
|
158
|
+
add(child.manifest, child.manifest.id);
|
|
142
159
|
return servers;
|
|
143
160
|
}
|
|
161
|
+
function findServer(manifest, agentId, capabilityId, serverName) {
|
|
162
|
+
return serversOf(manifest).find((item) => item.agentId === agentId &&
|
|
163
|
+
item.capabilityId === capabilityId &&
|
|
164
|
+
item.server.name === serverName);
|
|
165
|
+
}
|
|
166
|
+
/** Plugin roots of agents used as tools are registered as `<agent>/<capability>`. */
|
|
167
|
+
function pluginKey(declared) {
|
|
168
|
+
return declared.agentId === undefined
|
|
169
|
+
? declared.capabilityId
|
|
170
|
+
: `${declared.agentId}/${declared.capabilityId}`;
|
|
171
|
+
}
|
|
172
|
+
function ownerOf(declared) {
|
|
173
|
+
return declared.agentId === undefined ? {} : { agentId: declared.agentId };
|
|
174
|
+
}
|
|
175
|
+
function owned(declared, value) {
|
|
176
|
+
return { ...ownerOf(declared), ...value };
|
|
177
|
+
}
|
package/dist/mcp/snapshot.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { JsonObject } from "@nylorun/core/define";
|
|
2
2
|
import type { AgentManifest } from "@nylorun/core/define";
|
|
3
3
|
export interface McpToolRecord {
|
|
4
|
+
/** The agent used as a tool that declares the server; absent for the session's root agent. */
|
|
5
|
+
readonly agentId?: string;
|
|
4
6
|
readonly capabilityId: string;
|
|
5
7
|
readonly serverName: string;
|
|
6
8
|
readonly serverToolName: string;
|
|
@@ -15,6 +17,8 @@ export interface McpSnapshot {
|
|
|
15
17
|
readonly mcpTools: readonly McpToolRecord[];
|
|
16
18
|
}
|
|
17
19
|
export interface McpDiagnostic {
|
|
20
|
+
/** The agent used as a tool that declares the server; absent for the session's root agent. */
|
|
21
|
+
readonly agentId?: string;
|
|
18
22
|
readonly capabilityId: string;
|
|
19
23
|
readonly serverName: string;
|
|
20
24
|
readonly outcome: "connected" | "refused" | "failed";
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const RUNTIME_VERSION = "0.
|
|
1
|
+
export declare const RUNTIME_VERSION = "0.9.0-beta";
|
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nylorun/runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0-beta",
|
|
4
4
|
"description": "SQLite agent execution host and model providers.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -55,8 +55,8 @@
|
|
|
55
55
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
56
56
|
"@earendil-works/pi-ai": "0.85.1",
|
|
57
57
|
"@hono/node-server": "^2.1.1",
|
|
58
|
-
"@nylorun/harness": "0.
|
|
59
|
-
"@nylorun/core": "0.
|
|
58
|
+
"@nylorun/harness": "0.18.0-beta",
|
|
59
|
+
"@nylorun/core": "0.4.0-beta",
|
|
60
60
|
"just-bash": "3.4.2"
|
|
61
61
|
},
|
|
62
62
|
"optionalDependencies": {
|