@nylorun/runtime 0.7.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 +50 -0
- package/README.md +7 -5
- package/dist/adapters/sandbox/microsandbox.d.ts +2 -0
- package/dist/adapters/sandbox/microsandbox.js +238 -0
- package/dist/adapters/sandbox/virtual.d.ts +4 -0
- package/dist/adapters/sandbox/virtual.js +125 -0
- package/dist/configuration.d.ts +1 -1
- package/dist/configuration.js +1 -1
- package/dist/core/executors.d.ts +32 -0
- package/dist/core/executors.js +60 -0
- package/dist/core/main.js +7 -12
- package/dist/core/runtime.d.ts +24 -1
- package/dist/core/runtime.js +364 -43
- package/dist/core/store.d.ts +14 -1
- package/dist/core/store.js +59 -3
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- 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/model/catalog.d.ts +12 -0
- package/dist/model/catalog.js +29 -0
- package/dist/model/models.d.ts +3 -1
- package/dist/model/models.js +4 -2
- package/dist/model/pi-model.d.ts +9 -3
- package/dist/model/pi-model.js +47 -11
- package/dist/model/settings.d.ts +0 -2
- package/dist/model/settings.js +0 -47
- package/dist/node/index.d.ts +1 -1
- package/dist/node/index.js +1 -1
- package/dist/sandbox/manager.d.ts +61 -0
- package/dist/sandbox/manager.js +239 -0
- package/dist/sandbox/policy.d.ts +13 -0
- package/dist/sandbox/policy.js +67 -0
- package/dist/sandbox/select.d.ts +35 -0
- package/dist/sandbox/select.js +76 -0
- package/dist/sandbox/tools.d.ts +25 -0
- package/dist/sandbox/tools.js +165 -0
- package/dist/sandbox/types.d.ts +66 -0
- package/dist/vault/service.d.ts +41 -1
- package/dist/vault/service.js +291 -4
- package/dist/version.d.ts +1 -0
- package/dist/version.js +3 -0
- package/package.json +9 -4
- package/dist/config.d.ts +0 -20
- package/dist/model/defaults.d.ts +0 -17
- package/dist/model/defaults.js +0 -21
- package/dist/model/http-model.d.ts +0 -12
- package/dist/model/http-model.js +0 -299
- package/dist/node/local-sessions.d.ts +0 -5
- package/dist/node/local-sessions.js +0 -174
- package/dist/server/ag-ui.d.ts +0 -8
- package/dist/server/ag-ui.js +0 -76
- package/dist/server/delivery.d.ts +0 -24
- package/dist/server/delivery.js +0 -107
- package/dist/server/digests.d.ts +0 -13
- package/dist/server/digests.js +0 -39
- package/dist/server/host.d.ts +0 -70
- package/dist/server/host.js +0 -647
- package/dist/session/api.d.ts +0 -10
- package/dist/session/api.js +0 -15
- package/dist/session/default.d.ts +0 -5
- package/dist/session/default.js +0 -30
- package/dist/session/handle.d.ts +0 -27
- package/dist/session/handle.js +0 -199
- package/dist/session/index.d.ts +0 -2
- package/dist/session/index.js +0 -2
- package/dist/sessions/host.d.ts +0 -39
- package/dist/sessions/host.js +0 -359
- package/dist/sessions/store.d.ts +0 -41
- package/dist/sessions/store.js +0 -33
- /package/dist/{config.js → sandbox/types.js} +0 -0
package/dist/core/store.d.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { DatabaseSync } from "node:sqlite";
|
|
2
2
|
import type { LiveEvent } from "@nylorun/core/contracts";
|
|
3
3
|
export declare function canonical(value: unknown): string;
|
|
4
|
+
export interface ExecutorRow {
|
|
5
|
+
agentId: string;
|
|
6
|
+
tokenHash: string;
|
|
7
|
+
implementationVersion: string;
|
|
8
|
+
manifestHash?: string;
|
|
9
|
+
createdAt: string;
|
|
10
|
+
updatedAt: string;
|
|
11
|
+
}
|
|
4
12
|
export declare class Store {
|
|
5
13
|
readonly db: DatabaseSync;
|
|
6
14
|
constructor(path: string);
|
|
@@ -8,9 +16,14 @@ export declare class Store {
|
|
|
8
16
|
get<T = any>(table: string, id: string): T | undefined;
|
|
9
17
|
all<T = any>(table: string): T[];
|
|
10
18
|
put(table: string, id: string, body: unknown): void;
|
|
19
|
+
delete(table: string, id: string): void;
|
|
20
|
+
allExecutors(): ExecutorRow[];
|
|
21
|
+
putExecutor(row: Omit<ExecutorRow, "createdAt">): void;
|
|
22
|
+
deleteExecutor(agentId: string): void;
|
|
11
23
|
event(sessionId: string, turnId: string | null, type: string, payload: unknown): LiveEvent;
|
|
12
24
|
credentialCount(): number;
|
|
13
|
-
|
|
25
|
+
/** `agent` keeps only events of one agent used as a tool, by delegationId or path. */
|
|
26
|
+
history(sessionId: string, cursor?: string, agent?: string): {
|
|
14
27
|
items: LiveEvent[];
|
|
15
28
|
cursor: string | null;
|
|
16
29
|
};
|
package/dist/core/store.js
CHANGED
|
@@ -25,6 +25,7 @@ export class Store {
|
|
|
25
25
|
CREATE TABLE IF NOT EXISTS checkpoints(id TEXT PRIMARY KEY, body TEXT NOT NULL);
|
|
26
26
|
CREATE TABLE IF NOT EXISTS effects(id TEXT PRIMARY KEY, body TEXT NOT NULL);
|
|
27
27
|
CREATE TABLE IF NOT EXISTS actions(id TEXT PRIMARY KEY, body TEXT NOT NULL);
|
|
28
|
+
CREATE TABLE IF NOT EXISTS sandboxes(id TEXT PRIMARY KEY, body TEXT NOT NULL);
|
|
28
29
|
CREATE TABLE IF NOT EXISTS events(sequence INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT NOT NULL, body TEXT NOT NULL);
|
|
29
30
|
CREATE INDEX IF NOT EXISTS events_session ON events(session_id, sequence);
|
|
30
31
|
CREATE TABLE IF NOT EXISTS vaults(
|
|
@@ -32,7 +33,8 @@ export class Store {
|
|
|
32
33
|
name TEXT NOT NULL,
|
|
33
34
|
owner_user_id TEXT NOT NULL,
|
|
34
35
|
metadata_json TEXT,
|
|
35
|
-
created_at TEXT NOT NULL
|
|
36
|
+
created_at TEXT NOT NULL,
|
|
37
|
+
scope TEXT NOT NULL DEFAULT 'user'
|
|
36
38
|
);
|
|
37
39
|
CREATE INDEX IF NOT EXISTS vaults_owner ON vaults(owner_user_id);
|
|
38
40
|
CREATE TABLE IF NOT EXISTS vault_credentials(
|
|
@@ -65,7 +67,16 @@ export class Store {
|
|
|
65
67
|
id TEXT PRIMARY KEY,
|
|
66
68
|
body_hash TEXT NOT NULL,
|
|
67
69
|
response TEXT NOT NULL
|
|
70
|
+
);
|
|
71
|
+
CREATE TABLE IF NOT EXISTS executors(
|
|
72
|
+
agent_id TEXT PRIMARY KEY,
|
|
73
|
+
token_hash TEXT NOT NULL UNIQUE,
|
|
74
|
+
implementation_version TEXT NOT NULL,
|
|
75
|
+
manifest_hash TEXT,
|
|
76
|
+
created_at TEXT NOT NULL,
|
|
77
|
+
updated_at TEXT NOT NULL
|
|
68
78
|
);`);
|
|
79
|
+
migrateVaultScope(this.db);
|
|
69
80
|
}
|
|
70
81
|
tx(fn) {
|
|
71
82
|
this.db.exec("BEGIN IMMEDIATE");
|
|
@@ -94,6 +105,38 @@ export class Store {
|
|
|
94
105
|
.prepare(`INSERT INTO ${table}(id,body) VALUES(?,?) ON CONFLICT(id) DO UPDATE SET body=excluded.body`)
|
|
95
106
|
.run(id, JSON.stringify(body));
|
|
96
107
|
}
|
|
108
|
+
delete(table, id) {
|
|
109
|
+
this.db.prepare(`DELETE FROM ${table} WHERE id=?`).run(id);
|
|
110
|
+
}
|
|
111
|
+
// Executors carry a bearer secret, so they use typed columns with a database-enforced unique
|
|
112
|
+
// token hash instead of flowing through the generic JSON document helpers above.
|
|
113
|
+
allExecutors() {
|
|
114
|
+
return this.db
|
|
115
|
+
.prepare("SELECT agent_id,token_hash,implementation_version,manifest_hash,created_at,updated_at FROM executors")
|
|
116
|
+
.all()
|
|
117
|
+
.map((r) => ({
|
|
118
|
+
agentId: String(r.agent_id),
|
|
119
|
+
tokenHash: String(r.token_hash),
|
|
120
|
+
implementationVersion: String(r.implementation_version),
|
|
121
|
+
manifestHash: r.manifest_hash == null ? undefined : String(r.manifest_hash),
|
|
122
|
+
createdAt: String(r.created_at),
|
|
123
|
+
updatedAt: String(r.updated_at),
|
|
124
|
+
}));
|
|
125
|
+
}
|
|
126
|
+
putExecutor(row) {
|
|
127
|
+
this.db
|
|
128
|
+
.prepare(`INSERT INTO executors(agent_id,token_hash,implementation_version,manifest_hash,created_at,updated_at)
|
|
129
|
+
VALUES(?,?,?,?,?,?)
|
|
130
|
+
ON CONFLICT(agent_id) DO UPDATE SET
|
|
131
|
+
token_hash=excluded.token_hash,
|
|
132
|
+
implementation_version=excluded.implementation_version,
|
|
133
|
+
manifest_hash=excluded.manifest_hash,
|
|
134
|
+
updated_at=excluded.updated_at`)
|
|
135
|
+
.run(row.agentId, row.tokenHash, row.implementationVersion, row.manifestHash ?? null, row.updatedAt, row.updatedAt);
|
|
136
|
+
}
|
|
137
|
+
deleteExecutor(agentId) {
|
|
138
|
+
this.db.prepare("DELETE FROM executors WHERE agent_id=?").run(agentId);
|
|
139
|
+
}
|
|
97
140
|
event(sessionId, turnId, type, payload) {
|
|
98
141
|
const base = {
|
|
99
142
|
eventId: randomUUID(),
|
|
@@ -121,7 +164,8 @@ export class Store {
|
|
|
121
164
|
.get();
|
|
122
165
|
return Number(row.n);
|
|
123
166
|
}
|
|
124
|
-
|
|
167
|
+
/** `agent` keeps only events of one agent used as a tool, by delegationId or path. */
|
|
168
|
+
history(sessionId, cursor, agent) {
|
|
125
169
|
let sequence = 0;
|
|
126
170
|
if (cursor) {
|
|
127
171
|
const decoded = Buffer.from(cursor, "base64url").toString();
|
|
@@ -134,7 +178,8 @@ export class Store {
|
|
|
134
178
|
const items = this.db
|
|
135
179
|
.prepare("SELECT body FROM events WHERE session_id=? AND sequence>? ORDER BY sequence")
|
|
136
180
|
.all(sessionId, sequence)
|
|
137
|
-
.map((r) => JSON.parse(String(r.body)))
|
|
181
|
+
.map((r) => JSON.parse(String(r.body)))
|
|
182
|
+
.filter((event) => agent === undefined || belongsTo(event, agent));
|
|
138
183
|
const last = this.db
|
|
139
184
|
.prepare("SELECT body FROM events WHERE session_id=? ORDER BY sequence DESC LIMIT 1")
|
|
140
185
|
.get(sessionId);
|
|
@@ -144,3 +189,14 @@ export class Store {
|
|
|
144
189
|
};
|
|
145
190
|
}
|
|
146
191
|
}
|
|
192
|
+
function belongsTo(event, agent) {
|
|
193
|
+
const ref = event.payload
|
|
194
|
+
?.agent;
|
|
195
|
+
return ref?.delegationId === agent || ref?.path === agent;
|
|
196
|
+
}
|
|
197
|
+
function migrateVaultScope(db) {
|
|
198
|
+
const columns = db.prepare("PRAGMA table_info(vaults)").all();
|
|
199
|
+
if (!columns.some((column) => column.name === "scope"))
|
|
200
|
+
db.exec("ALTER TABLE vaults ADD COLUMN scope TEXT NOT NULL DEFAULT 'user'");
|
|
201
|
+
db.exec("CREATE UNIQUE INDEX IF NOT EXISTS vaults_one_host ON vaults(scope) WHERE scope = 'host'");
|
|
202
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export { CoreRuntime, createRuntime, startRuntime, type RuntimeOptions } from "./core/runtime.js";
|
|
2
2
|
export { scriptedModel, gatewayModel, type ModelProvider } from "./core/provider.js";
|
|
3
|
-
export { httpModel, type HttpModelOptions, type ModelEnvironment } from "./model/http-model.js";
|
|
4
3
|
export type { RuntimeAgent } from "./contracts.js";
|
|
5
4
|
export { IMAGE_MEDIA_TYPES, MAX_IMAGE_BYTES, decodeImageBase64, validateImageBytes } from "./media.js";
|
|
6
5
|
export type { RuntimeModelAdapter } from "./contracts.js";
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
export { CoreRuntime, createRuntime, startRuntime } from "./core/runtime.js";
|
|
2
2
|
export { scriptedModel, gatewayModel } from "./core/provider.js";
|
|
3
|
-
export { httpModel } from "./model/http-model.js";
|
|
4
3
|
export { IMAGE_MEDIA_TYPES, MAX_IMAGE_BYTES, decodeImageBase64, validateImageBytes } from "./media.js";
|
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";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type HostModelCatalog = {
|
|
2
|
+
providers: {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
models: {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
}[];
|
|
9
|
+
}[];
|
|
10
|
+
};
|
|
11
|
+
/** Public provider and model names. No credentials. */
|
|
12
|
+
export declare function hostModelCatalog(): HostModelCatalog;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { modelsFor } from "./models.js";
|
|
2
|
+
const emptyStore = {
|
|
3
|
+
async read() {
|
|
4
|
+
return undefined;
|
|
5
|
+
},
|
|
6
|
+
async list() {
|
|
7
|
+
return [];
|
|
8
|
+
},
|
|
9
|
+
async modify(_providerId, fn) {
|
|
10
|
+
return fn(undefined);
|
|
11
|
+
},
|
|
12
|
+
async delete() { },
|
|
13
|
+
};
|
|
14
|
+
/** Public provider and model names. No credentials. */
|
|
15
|
+
export function hostModelCatalog() {
|
|
16
|
+
const models = modelsFor({ provider: "", model: "" }, emptyStore, {
|
|
17
|
+
environment: false,
|
|
18
|
+
});
|
|
19
|
+
return {
|
|
20
|
+
providers: models.getProviders().map((provider) => ({
|
|
21
|
+
id: provider.id,
|
|
22
|
+
name: provider.name,
|
|
23
|
+
models: models.getModels(provider.id).map((model) => ({
|
|
24
|
+
id: model.id,
|
|
25
|
+
name: model.name,
|
|
26
|
+
})),
|
|
27
|
+
})),
|
|
28
|
+
};
|
|
29
|
+
}
|
package/dist/model/models.d.ts
CHANGED
|
@@ -6,4 +6,6 @@ export type Selection = Readonly<{
|
|
|
6
6
|
baseUrl: string;
|
|
7
7
|
}>;
|
|
8
8
|
}>;
|
|
9
|
-
export declare function modelsFor(selection: Selection, credentials: CredentialStore
|
|
9
|
+
export declare function modelsFor(selection: Selection, credentials: CredentialStore, options?: {
|
|
10
|
+
environment?: boolean;
|
|
11
|
+
}): import("@earendil-works/pi-ai").MutableModels;
|
package/dist/model/models.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createProvider, defaultProviderAuthContext, envApiKeyAuth, } from "@earendil-works/pi-ai";
|
|
2
2
|
import { stream, streamSimple, } from "@earendil-works/pi-ai/api/openai-completions";
|
|
3
3
|
import { builtinModels } from "@earendil-works/pi-ai/providers/all";
|
|
4
|
-
export function modelsFor(selection, credentials) {
|
|
4
|
+
export function modelsFor(selection, credentials, options = {}) {
|
|
5
5
|
const environmentFirst = {
|
|
6
6
|
async read(providerId, options) {
|
|
7
7
|
const explicit = process.env.MODEL_PROVIDER_API_KEY;
|
|
@@ -23,7 +23,9 @@ export function modelsFor(selection, credentials) {
|
|
|
23
23
|
modify: (id, fn, options) => credentials.modify(id, fn, options),
|
|
24
24
|
delete: (id, options) => credentials.delete(id, options),
|
|
25
25
|
};
|
|
26
|
-
const models = builtinModels({
|
|
26
|
+
const models = builtinModels({
|
|
27
|
+
credentials: options.environment === false ? credentials : environmentFirst,
|
|
28
|
+
});
|
|
27
29
|
if (!selection.custom)
|
|
28
30
|
return models;
|
|
29
31
|
const model = {
|
package/dist/model/pi-model.d.ts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
|
+
import type { Credential } from "@earendil-works/pi-ai";
|
|
1
2
|
import type { RuntimeModelAdapter } from "../contracts.js";
|
|
2
3
|
import type { RuntimeMedia } from "../adapters/media.js";
|
|
3
|
-
import {
|
|
4
|
+
import type { HostModelSecret } from "../vault/service.js";
|
|
5
|
+
export interface ModelPreview {
|
|
6
|
+
readonly invocationId: string;
|
|
7
|
+
readonly text: string;
|
|
8
|
+
}
|
|
4
9
|
export interface PiModelOptions {
|
|
5
10
|
readonly root?: string;
|
|
6
|
-
readonly onPreview?: (preview:
|
|
7
|
-
readonly selection?: Selection;
|
|
11
|
+
readonly onPreview?: (preview: ModelPreview) => void;
|
|
8
12
|
readonly media?: Pick<RuntimeMedia, "dataUrl">;
|
|
13
|
+
readonly readHostModel?: () => HostModelSecret | undefined;
|
|
14
|
+
readonly writeHostCredential?: (credential: Credential) => void;
|
|
9
15
|
}
|
|
10
16
|
/** Node model adapter. Local provider configuration is read only when invoked. */
|
|
11
17
|
export declare function piModel(options?: PiModelOptions): RuntimeModelAdapter;
|
package/dist/model/pi-model.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import { join } from "node:path";
|
|
2
1
|
import { scrub } from "../redact.js";
|
|
3
|
-
import { ProjectCredentialStore } from "./auth-store.js";
|
|
4
2
|
import { modelsFor } from "./models.js";
|
|
5
|
-
import {
|
|
3
|
+
import { projectSecrets } from "./settings.js";
|
|
6
4
|
const emptyUsage = () => ({
|
|
7
5
|
input: 0,
|
|
8
6
|
output: 0,
|
|
@@ -16,15 +14,18 @@ export function piModel(options = {}) {
|
|
|
16
14
|
return async (call, context) => {
|
|
17
15
|
context.signal.throwIfAborted();
|
|
18
16
|
const root = options.root ?? process.cwd();
|
|
19
|
-
const
|
|
17
|
+
const stored = options.readHostModel?.();
|
|
18
|
+
if (!stored)
|
|
19
|
+
throw new Error("Model provider is not configured. Start nylorun dev in a terminal, or set it in Studio.");
|
|
20
20
|
const requested = call.model?.id;
|
|
21
21
|
const selection = {
|
|
22
|
-
|
|
23
|
-
model: requested?.startsWith(`${
|
|
24
|
-
? requested.slice(
|
|
25
|
-
: (requested ??
|
|
22
|
+
provider: stored.provider,
|
|
23
|
+
model: requested?.startsWith(`${stored.provider}/`)
|
|
24
|
+
? requested.slice(stored.provider.length + 1)
|
|
25
|
+
: (requested ?? stored.model),
|
|
26
|
+
...(stored.baseUrl ? { custom: { baseUrl: stored.baseUrl } } : {}),
|
|
26
27
|
};
|
|
27
|
-
const registry = modelsFor(selection,
|
|
28
|
+
const registry = modelsFor(selection, hostCredentialStore(stored, options.writeHostCredential), { environment: false });
|
|
28
29
|
const selected = registry.getModel(selection.provider, selection.model);
|
|
29
30
|
if (!selected)
|
|
30
31
|
throw new Error("Unknown model. Run nylorun configure.");
|
|
@@ -176,7 +177,10 @@ export function piModel(options = {}) {
|
|
|
176
177
|
messages,
|
|
177
178
|
tools,
|
|
178
179
|
};
|
|
179
|
-
const secrets =
|
|
180
|
+
const secrets = [
|
|
181
|
+
...projectSecrets(root),
|
|
182
|
+
...credentialSecrets(stored.credential),
|
|
183
|
+
];
|
|
180
184
|
// Publish only portable references, never the materialized provider image bytes.
|
|
181
185
|
context.reportPreparedCall?.({
|
|
182
186
|
adapter: "runtime.pi-ai",
|
|
@@ -265,7 +269,39 @@ export function piModel(options = {}) {
|
|
|
265
269
|
};
|
|
266
270
|
}
|
|
267
271
|
catch (error) {
|
|
268
|
-
throw new Error(String(scrub(error instanceof Error ? error.message : String(error),
|
|
272
|
+
throw new Error(String(scrub(error instanceof Error ? error.message : String(error), secrets)));
|
|
269
273
|
}
|
|
270
274
|
};
|
|
271
275
|
}
|
|
276
|
+
function hostCredentialStore(stored, write) {
|
|
277
|
+
let current = stored.credential;
|
|
278
|
+
return {
|
|
279
|
+
async read(providerId) {
|
|
280
|
+
if (providerId !== stored.provider)
|
|
281
|
+
return undefined;
|
|
282
|
+
return current;
|
|
283
|
+
},
|
|
284
|
+
async list() {
|
|
285
|
+
return [{ providerId: stored.provider, type: current.type }];
|
|
286
|
+
},
|
|
287
|
+
async modify(providerId, fn) {
|
|
288
|
+
if (providerId !== stored.provider)
|
|
289
|
+
return fn(undefined);
|
|
290
|
+
const next = await fn(current);
|
|
291
|
+
if (next === undefined)
|
|
292
|
+
return current;
|
|
293
|
+
write?.(next);
|
|
294
|
+
current = next;
|
|
295
|
+
return next;
|
|
296
|
+
},
|
|
297
|
+
async delete() {
|
|
298
|
+
throw new Error("Replace the model provider through nylorun dev or Studio.");
|
|
299
|
+
},
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
function credentialSecrets(credential) {
|
|
303
|
+
const values = [credential.key, credential.access, credential.refresh];
|
|
304
|
+
if (credential.env)
|
|
305
|
+
values.push(...Object.values(credential.env));
|
|
306
|
+
return values.filter((value) => Boolean(value));
|
|
307
|
+
}
|
package/dist/model/settings.d.ts
CHANGED
package/dist/model/settings.js
CHANGED
|
@@ -1,52 +1,5 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
export function modelSelection(root = process.cwd()) {
|
|
4
|
-
const { MODEL_PROVIDER: provider, MODEL: model, MODEL_PROVIDER_BASE_URL: baseUrl, } = process.env;
|
|
5
|
-
if (provider !== undefined || model !== undefined || baseUrl !== undefined) {
|
|
6
|
-
if (!provider?.trim() || !model?.trim())
|
|
7
|
-
throw new Error("Set both MODEL_PROVIDER and MODEL, or run nylorun configure.");
|
|
8
|
-
if (provider === "custom" && !baseUrl?.trim())
|
|
9
|
-
throw new Error("Set MODEL_PROVIDER_BASE_URL for MODEL_PROVIDER=custom.");
|
|
10
|
-
if (baseUrl && provider !== "custom")
|
|
11
|
-
throw new Error("MODEL_PROVIDER_BASE_URL requires MODEL_PROVIDER=custom.");
|
|
12
|
-
if (baseUrl) {
|
|
13
|
-
let url;
|
|
14
|
-
try {
|
|
15
|
-
url = new URL(baseUrl);
|
|
16
|
-
}
|
|
17
|
-
catch {
|
|
18
|
-
throw new Error("MODEL_PROVIDER_BASE_URL must be an HTTP(S) URL.");
|
|
19
|
-
}
|
|
20
|
-
if (!["http:", "https:"].includes(url.protocol))
|
|
21
|
-
throw new Error("MODEL_PROVIDER_BASE_URL must be an HTTP(S) URL.");
|
|
22
|
-
}
|
|
23
|
-
return { provider, model, ...(baseUrl ? { custom: { baseUrl } } : {}) };
|
|
24
|
-
}
|
|
25
|
-
try {
|
|
26
|
-
let contents;
|
|
27
|
-
try {
|
|
28
|
-
contents = readFileSync(join(root, ".env", "model.json"), "utf8");
|
|
29
|
-
}
|
|
30
|
-
catch (error) {
|
|
31
|
-
if (!(error instanceof Error) ||
|
|
32
|
-
!("code" in error) ||
|
|
33
|
-
!["ENOENT", "ENOTDIR"].includes(String(error.code)))
|
|
34
|
-
throw error;
|
|
35
|
-
contents = readFileSync(join(root, "config", "model.json"), "utf8");
|
|
36
|
-
}
|
|
37
|
-
const value = JSON.parse(contents);
|
|
38
|
-
if (typeof value.provider === "string" &&
|
|
39
|
-
value.provider &&
|
|
40
|
-
typeof value.model === "string" &&
|
|
41
|
-
value.model &&
|
|
42
|
-
(value.custom === undefined || typeof value.custom.baseUrl === "string"))
|
|
43
|
-
return value;
|
|
44
|
-
}
|
|
45
|
-
catch {
|
|
46
|
-
/* Report one actionable setup error without file or credential contents. */
|
|
47
|
-
}
|
|
48
|
-
throw new Error("Run nylorun configure to connect a model provider.");
|
|
49
|
-
}
|
|
50
3
|
export function projectSecrets(root = process.cwd()) {
|
|
51
4
|
const values = Object.entries(process.env)
|
|
52
5
|
.filter(([key]) => /key|token|secret|password|credential/i.test(key))
|
package/dist/node/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { localSessions } from "./local-sessions.js";
|
|
2
1
|
export { localMedia, MediaStore } from "../adapters/media.js";
|
|
3
2
|
export { jsonlObserver } from "../adapters/observe.js";
|
|
4
3
|
export { piModel, type PiModelOptions } from "../model/pi-model.js";
|
|
5
4
|
export { projectAsset } from "../assets.js";
|
|
5
|
+
export { probeSandboxBackends, type SandboxSelectionReport, } from "../sandbox/select.js";
|
package/dist/node/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { localSessions } from "./local-sessions.js";
|
|
2
1
|
export { localMedia, MediaStore } from "../adapters/media.js";
|
|
3
2
|
export { jsonlObserver } from "../adapters/observe.js";
|
|
4
3
|
export { piModel } from "../model/pi-model.js";
|
|
5
4
|
export { projectAsset } from "../assets.js";
|
|
5
|
+
export { probeSandboxBackends, } from "../sandbox/select.js";
|