@nylorun/agents 0.18.0-beta → 0.19.1-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 +35 -0
- package/README.md +7 -3
- package/dist/client.d.ts +2 -1
- package/dist/client.js +8 -6
- package/dist/connection.js +26 -34
- package/dist/http.d.ts +3 -4
- package/dist/http.js +20 -68
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/plugins/load.js +22 -37
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# @nylorun/agents
|
|
2
2
|
|
|
3
|
+
## 0.19.1-beta
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Pin core to the tested release.
|
|
8
|
+
- Updated dependencies [212364b]
|
|
9
|
+
- @nylorun/core@0.19.0-beta
|
|
10
|
+
|
|
11
|
+
## 0.19.0-beta
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- 7780b2f: **One client and connection layer.** The Project link and credentials are read one way, and every Runtime client shares one `/health` probe.
|
|
16
|
+
|
|
17
|
+
- `@nylorun/core`: `@nylorun/core/project` (Node only, the one Core subpath that imports Node modules) reads the Nylorun home, a local Tenant's Host root (`tenantHostRoot`), the Project root (`findProjectRoot`) and the Project link and credentials (`findLinkedProject`, `readProjectLink`, `readCredentialsFile`), validated with `ProjectLinkFileSchema` and `ProjectCredentialsFileSchema`; a broken or newer file throws `ProjectFileError`. `@nylorun/core/transport` (browser-safe) holds the `/health` compatibility probe (`checkHealth`), `parseProtocolRange` (on `ProtocolRangeSchema`, ignoring fields a newer Host adds), `describeIncompatibility`, `requestHeaders` and `readBody`.
|
|
18
|
+
- `@nylorun/agents`: `resolveConnection` and `createClient()` find the Project as every reader does: the nearest directory with `.nylorun/` from `cwd` upwards, never the home directory or above it (they used to walk to the filesystem root, and past a `.nylorun/` without a link). A link without `credentials.json`, or a link or credentials file that does not validate, is now `connection_missing` naming the file and `npx nylorun start`, instead of a raw file-system or parse error. `@nylorun/agents/client` loads the link reader only when `createClient()` resolves a connection, so it imports no Node module. The root entry re-exports `checkHealth`, `describeIncompatibility` and `parseProtocolRange`.
|
|
19
|
+
- `@nylorun/admin`: `createAdmin` reads the Project link with the same reader. A link that does not validate is now refused like one from an older nylorun (when nothing else names the Host root), and a linked Project's `credentials.json` that does not validate is `connection_missing` instead of being skipped. `@nylorun/admin/project` re-exports `@nylorun/core/project` for tools that depend on this package. The `/health` check and request headers come from `@nylorun/core/transport`.
|
|
20
|
+
- `nylorun`: reads the Project link and credentials with `@nylorun/core/project`; a credentials file whose keys are not 64 hex characters is replaced by `start`, as an unreadable one was. No command changes.
|
|
21
|
+
- `@nylorun/cli`: `nylo` reads the Project root, link and credentials through `@nylorun/admin/project` instead of its own copies, with the same messages. No command changes.
|
|
22
|
+
- `@nylorun/studio`: the server's Runtime compatibility probe is `checkHealth` from `@nylorun/agents`.
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- 070674d: **One source of truth for the manifest and Harness API schemas.**
|
|
27
|
+
|
|
28
|
+
- `@nylorun/core`: `Agent.from` (`agentFrom`) checks a manifest with `AgentManifestSchema`, the Runtime's own check, instead of a validator of its own, and refuses with `agent.build-failed` and the schema's messages (each prefixed with its path, such as `capabilities.0.tools.0.name: …`). It now refuses what the Runtime refuses at registration and it let through, such as unknown fields (it dropped them) or a tool or MCP server name that not every model provider accepts. The schema names a top-level `model` or `schemaVersion` and a capability's `model` as before, and the skill whose `files` are wrong (`Skill 'triage' files must include SKILL.md`). The rebuilt manifest is a frozen copy; the object passed in is no longer frozen. `AgentManifestSchema` and `WorkflowManifestSchema` are no longer force-cast to their types: the build checks that each accepts exactly `AgentManifest` and `WorkflowManifest`. `@nylorun/core/contracts` exports `McpServerManifestSchema`, `HEADER_NAME_PATTERN` and `LOOPBACK_HOSTS`. The Harness API's types (`@nylorun/core/harness-api`) are inferred from the schemas that validate its frames, so they match them: `TurnStart.manifest`, `TurnStart.checkpoint` and `RunRouting.rootManifest` are `object`, `WorkspaceCall.tool` is a sandbox tool name, `ABORT_REASONS` is a tuple, and properties are no longer `readonly` (arrays that were stay so).
|
|
29
|
+
- `@nylorun/harness`: `runDurable` checks the manifest once, when it rebuilds the agent, instead of twice. The unpublished, stale `schemas/manifest.schema.json` (manifest `schemaVersion` 2) is gone; `z.toJSONSchema(AgentManifestSchema)` is the manifest's JSON Schema.
|
|
30
|
+
- `@nylorun/agents`: a plugin's MCP servers are checked with core's `McpServerManifestSchema`, header-name pattern and loopback hosts. A server whose key is not a name every model provider accepts is skipped with a `plugin.mcp-server-skipped` warning, since the Runtime would refuse the manifest declaring it.
|
|
31
|
+
- `@nylorun/runtime`: the stack configuration reads its four listeners (`NYLORUN_LISTEN_*`, `NYLORUN_GATES_LISTEN_*`, `NYLORUN_HARNESS_LISTEN_*`, `NYLORUN_EGRESS_LISTEN_*`) with one parser; the variables and their errors are unchanged.
|
|
32
|
+
|
|
33
|
+
- Pin core to the tested release.
|
|
34
|
+
- Updated dependencies [7780b2f]
|
|
35
|
+
- Updated dependencies [070674d]
|
|
36
|
+
- @nylorun/core@0.18.0-beta
|
|
37
|
+
|
|
3
38
|
## 0.18.0-beta
|
|
4
39
|
|
|
5
40
|
### Major Changes
|
package/README.md
CHANGED
|
@@ -86,9 +86,13 @@ only on the Runtime (a local `run()` reports `http.runtime-only`).
|
|
|
86
86
|
2. Environment — if `NYLORUN_RUNTIME_URL` or `NYLORUN_SERVER_KEY` is set, both
|
|
87
87
|
must be present
|
|
88
88
|
3. Project link — `.nylorun/link.json` + `credentials.json` (the application
|
|
89
|
-
key), written by `npx nylorun start
|
|
90
|
-
|
|
91
|
-
the
|
|
89
|
+
key), written by `npx nylorun start`, in the nearest directory with
|
|
90
|
+
`.nylorun/` from `cwd` upwards (never the home directory or above it, whose
|
|
91
|
+
`.nylorun/` is the Nylorun home). A link from an older nylorun
|
|
92
|
+
(format 0 to 2), a link without credentials, or a file that does not
|
|
93
|
+
validate fails with `connection_missing`: run `npx nylorun start` in the
|
|
94
|
+
project again. The Project files are read by `@nylorun/core/project`, as
|
|
95
|
+
`@nylorun/admin`, `nylorun` and `nylo` read them.
|
|
92
96
|
|
|
93
97
|
Sources never mix. Partial environment fails with `connection_missing`. A
|
|
94
98
|
Runtime serves one Tenant, so nothing names it: the `tenant` option is gone
|
package/dist/client.d.ts
CHANGED
|
@@ -5,7 +5,8 @@ import type { Destination } from "./http.js";
|
|
|
5
5
|
* Build a Tenant API client.
|
|
6
6
|
* Explicit connection fields keep today's sync Transport resolution (including
|
|
7
7
|
* environment fill-ins). With no connection fields, resolves via
|
|
8
|
-
* `resolveConnection` (environment → project link)
|
|
8
|
+
* `resolveConnection` (environment → project link), which reads the Project's files: it is
|
|
9
|
+
* loaded only then, so this entry point imports no Node module (Studio's web bundle uses it).
|
|
9
10
|
*/
|
|
10
11
|
export declare function createClient(destination: Destination): AgentsClient;
|
|
11
12
|
export declare function createClient(destination?: undefined): Promise<AgentsClient>;
|
package/dist/client.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
export * from "./session-client.js";
|
|
2
2
|
import { AgentsClient } from "./session-client.js";
|
|
3
|
-
import { resolveConnection } from "./connection.js";
|
|
4
3
|
export function createClient(destination = {}) {
|
|
5
4
|
if (destination.url !== undefined || destination.key !== undefined) {
|
|
6
5
|
return new AgentsClient(destination);
|
|
7
6
|
}
|
|
8
|
-
return
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
return import("./connection.js").then(async ({ resolveConnection }) => {
|
|
8
|
+
const connection = await resolveConnection();
|
|
9
|
+
return new AgentsClient({
|
|
10
|
+
url: connection.url,
|
|
11
|
+
key: connection.key,
|
|
12
|
+
...(destination.fetch ? { fetch: destination.fetch } : {}),
|
|
13
|
+
});
|
|
14
|
+
});
|
|
13
15
|
}
|
package/dist/connection.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { dirname, join, resolve } from "node:path";
|
|
3
|
-
import { ProjectCredentialsFileSchema, ProjectLinkFileSchema, } from "@nylorun/core/contracts";
|
|
1
|
+
import { ProjectFileError, findLinkedProject } from "@nylorun/core/project";
|
|
4
2
|
import { env } from "./http.js";
|
|
5
3
|
export class ConnectionError extends Error {
|
|
6
4
|
code = "connection_missing";
|
|
@@ -17,37 +15,31 @@ function missing(tried) {
|
|
|
17
15
|
function stripTrailingSlash(url) {
|
|
18
16
|
return url.replace(/\/$/, "");
|
|
19
17
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if (error.code === "ENOENT") {
|
|
31
|
-
const parent = dirname(dir);
|
|
32
|
-
if (parent === dir)
|
|
33
|
-
return undefined;
|
|
34
|
-
dir = parent;
|
|
35
|
-
continue;
|
|
36
|
-
}
|
|
37
|
-
throw error;
|
|
38
|
-
}
|
|
39
|
-
const link = ProjectLinkFileSchema.parse(JSON.parse(linkRaw));
|
|
40
|
-
// Formats 0 to 2 are from older releases.
|
|
41
|
-
if (link.format < 3)
|
|
42
|
-
throw new ConnectionError(`connection_missing: the Project link at ${linkPath} is from an older nylorun. ` +
|
|
43
|
-
`Run "npx nylorun start" in this project to link it again.`);
|
|
44
|
-
const credentials = ProjectCredentialsFileSchema.parse(JSON.parse(await readFile(join(nylorun, "credentials.json"), "utf8")));
|
|
45
|
-
return {
|
|
46
|
-
url: stripTrailingSlash(link.hostUrl),
|
|
47
|
-
key: credentials.applicationKey,
|
|
48
|
-
source: "project-link",
|
|
49
|
-
};
|
|
18
|
+
/** The application key of the linked Project from `cwd` upwards (`@nylorun/core/project`). */
|
|
19
|
+
function readProjectLink(cwd) {
|
|
20
|
+
let project;
|
|
21
|
+
try {
|
|
22
|
+
project = findLinkedProject(cwd);
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
if (error instanceof ProjectFileError)
|
|
26
|
+
throw new ConnectionError(`connection_missing: ${error.message}`);
|
|
27
|
+
throw error;
|
|
50
28
|
}
|
|
29
|
+
if (!project)
|
|
30
|
+
return undefined;
|
|
31
|
+
// Formats 0 to 2 are from older releases.
|
|
32
|
+
if (project.link.format < 3)
|
|
33
|
+
throw new ConnectionError(`connection_missing: the Project link at ${project.linkPath} is from an older nylorun. ` +
|
|
34
|
+
`Run "npx nylorun start" in this project to link it again.`);
|
|
35
|
+
if (!project.credentials)
|
|
36
|
+
throw new ConnectionError(`connection_missing: the Project at ${project.root} has a link but no credentials ` +
|
|
37
|
+
`(.nylorun/credentials.json). Run "npx nylorun start" in this project to link it again.`);
|
|
38
|
+
return {
|
|
39
|
+
url: project.link.hostUrl,
|
|
40
|
+
key: project.credentials.applicationKey,
|
|
41
|
+
source: "project-link",
|
|
42
|
+
};
|
|
51
43
|
}
|
|
52
44
|
/**
|
|
53
45
|
* Resolve Tenant API connection: options → environment → project link (D§7.1). A connection is a
|
|
@@ -83,7 +75,7 @@ export async function resolveConnection(options) {
|
|
|
83
75
|
}
|
|
84
76
|
tried.push("project-link");
|
|
85
77
|
const cwd = options?.cwd ?? (typeof process !== "undefined" ? process.cwd() : ".");
|
|
86
|
-
const fromLink =
|
|
78
|
+
const fromLink = readProjectLink(cwd);
|
|
87
79
|
if (fromLink)
|
|
88
80
|
return fromLink;
|
|
89
81
|
missing(tried);
|
package/dist/http.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type ProtocolRange } from "@nylorun/core/compatibility";
|
|
2
|
+
import { type Incompatibility } from "@nylorun/core/transport";
|
|
2
3
|
/**
|
|
3
4
|
* Bearer tokens for a client that holds no Tenant key: a trusted issuer's tokens (Host feature
|
|
4
5
|
* `trusted-issuers`), from the operator's identity provider. `get` returns a current token and
|
|
@@ -26,9 +27,7 @@ export declare class RuntimeError extends Error {
|
|
|
26
27
|
readonly body: unknown;
|
|
27
28
|
constructor(status: number, body: unknown);
|
|
28
29
|
}
|
|
29
|
-
export type IncompatibleReason =
|
|
30
|
-
ok: false;
|
|
31
|
-
}>;
|
|
30
|
+
export type IncompatibleReason = Incompatibility;
|
|
32
31
|
/** Host protocol is outside the client's supported range or missing required features. */
|
|
33
32
|
export declare class IncompatibleRuntimeError extends Error {
|
|
34
33
|
readonly compatibility: IncompatibleReason;
|
package/dist/http.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { PROTOCOL_FEATURES,
|
|
1
|
+
import { PROTOCOL_FEATURES, } from "@nylorun/core/compatibility";
|
|
2
|
+
import { advertisedProtocol, checkHealth, clientCompatibility, describeIncompatibility, readBody, requestHeaders, } from "@nylorun/core/transport";
|
|
2
3
|
export function env(name) {
|
|
3
4
|
return typeof process !== "undefined" ? process.env[name] : undefined;
|
|
4
5
|
}
|
|
@@ -18,10 +19,7 @@ export class IncompatibleRuntimeError extends Error {
|
|
|
18
19
|
host;
|
|
19
20
|
constructor(compatibility) {
|
|
20
21
|
const remedy = remedyFor(compatibility);
|
|
21
|
-
|
|
22
|
-
? `client protocol ${compatibility.client} is outside Host range ${compatibility.host.min}–${compatibility.host.max}`
|
|
23
|
-
: `Host is missing required features: ${compatibility.missing.join(", ")}`;
|
|
24
|
-
super(`Incompatible Runtime: ${detail}. ${remedy}`);
|
|
22
|
+
super(`Incompatible Runtime: ${describeIncompatibility(compatibility)}. ${remedy}`);
|
|
25
23
|
this.name = "IncompatibleRuntimeError";
|
|
26
24
|
this.compatibility = compatibility;
|
|
27
25
|
this.remedy = remedy;
|
|
@@ -37,21 +35,6 @@ function remedyFor(compatibility) {
|
|
|
37
35
|
}
|
|
38
36
|
return "Upgrade the Runtime Host with nylorun runtime restart from a newer CLI, or install a matching older client (npm i -D @nylorun/cli@<compatible>).";
|
|
39
37
|
}
|
|
40
|
-
function parseProtocolRange(value) {
|
|
41
|
-
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
42
|
-
return undefined;
|
|
43
|
-
const record = value;
|
|
44
|
-
if (typeof record.min !== "number" ||
|
|
45
|
-
typeof record.max !== "number" ||
|
|
46
|
-
!Array.isArray(record.features) ||
|
|
47
|
-
!record.features.every((f) => typeof f === "string"))
|
|
48
|
-
return undefined;
|
|
49
|
-
return {
|
|
50
|
-
min: record.min,
|
|
51
|
-
max: record.max,
|
|
52
|
-
features: record.features,
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
38
|
export class Transport {
|
|
56
39
|
url;
|
|
57
40
|
/** The Tenant key; empty for a client that uses issuer tokens. */
|
|
@@ -99,36 +82,23 @@ export class Transport {
|
|
|
99
82
|
async ensureCompatible(signal) {
|
|
100
83
|
if (this.check.compatible)
|
|
101
84
|
return;
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
signal,
|
|
85
|
+
const health = await checkHealth(this.url, {
|
|
86
|
+
fetch: this.fetcher,
|
|
87
|
+
...(signal ? { signal } : {}),
|
|
106
88
|
});
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
body = JSON.parse(text);
|
|
111
|
-
}
|
|
112
|
-
catch {
|
|
113
|
-
/* keep text */
|
|
114
|
-
}
|
|
115
|
-
if (!response.ok)
|
|
116
|
-
throw new RuntimeError(response.status, body);
|
|
117
|
-
const protocol = parseProtocolRange(body && typeof body === "object" && !Array.isArray(body)
|
|
118
|
-
? body.protocol
|
|
119
|
-
: undefined);
|
|
120
|
-
if (!protocol)
|
|
89
|
+
if (health.result === "failed")
|
|
90
|
+
throw new RuntimeError(health.status, health.body);
|
|
91
|
+
if (health.result === "unadvertised")
|
|
121
92
|
throw new IncompatibleRuntimeError({
|
|
122
93
|
ok: false,
|
|
123
94
|
reason: "feature",
|
|
124
95
|
missing: [...PROTOCOL_FEATURES],
|
|
125
96
|
host: { min: 0, max: 0, features: [] },
|
|
126
97
|
});
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
this.check.
|
|
131
|
-
this.check.range = protocol;
|
|
98
|
+
if (health.result === "incompatible")
|
|
99
|
+
throw new IncompatibleRuntimeError(health.compatibility);
|
|
100
|
+
this.check.features = [...health.protocol.features];
|
|
101
|
+
this.check.range = health.protocol;
|
|
132
102
|
this.check.compatible = true;
|
|
133
103
|
}
|
|
134
104
|
/** Throws `IncompatibleRuntimeError` before anything is sent when the Host lacks `feature`. */
|
|
@@ -154,8 +124,8 @@ export class Transport {
|
|
|
154
124
|
const bearer = this.token
|
|
155
125
|
? await this.token.get(init.signal ?? undefined)
|
|
156
126
|
: this.key;
|
|
157
|
-
|
|
158
|
-
|
|
127
|
+
for (const [name, value] of Object.entries(requestHeaders(bearer)))
|
|
128
|
+
headers.set(name, value);
|
|
159
129
|
// JSON unless the caller says otherwise (an artifact upload sends the file's type).
|
|
160
130
|
if (init.body && !headers.has("content-type"))
|
|
161
131
|
headers.set("Content-Type", "application/json");
|
|
@@ -183,35 +153,17 @@ export class Transport {
|
|
|
183
153
|
await this.ensureCompatible(init.signal === null ? undefined : init.signal);
|
|
184
154
|
return this.request(path, init, { retried426: true });
|
|
185
155
|
}
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
try {
|
|
189
|
-
body = JSON.parse(text);
|
|
190
|
-
}
|
|
191
|
-
catch {
|
|
192
|
-
/* keep text */
|
|
193
|
-
}
|
|
194
|
-
const protocol = parseProtocolRange(body && typeof body === "object" && !Array.isArray(body)
|
|
195
|
-
? body.protocol
|
|
196
|
-
: undefined);
|
|
156
|
+
const body = await readBody(response);
|
|
157
|
+
const protocol = advertisedProtocol(body);
|
|
197
158
|
if (protocol) {
|
|
198
|
-
const result =
|
|
159
|
+
const result = clientCompatibility(protocol);
|
|
199
160
|
if (!result.ok)
|
|
200
161
|
throw new IncompatibleRuntimeError(result);
|
|
201
162
|
}
|
|
202
163
|
throw new RuntimeError(426, body);
|
|
203
164
|
}
|
|
204
|
-
if (!response.ok)
|
|
205
|
-
|
|
206
|
-
let body = text;
|
|
207
|
-
try {
|
|
208
|
-
body = JSON.parse(text);
|
|
209
|
-
}
|
|
210
|
-
catch {
|
|
211
|
-
/* keep text */
|
|
212
|
-
}
|
|
213
|
-
throw new RuntimeError(response.status, body);
|
|
214
|
-
}
|
|
165
|
+
if (!response.ok)
|
|
166
|
+
throw new RuntimeError(response.status, await readBody(response));
|
|
215
167
|
return response;
|
|
216
168
|
}
|
|
217
169
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { hashManifest, PROTOCOL_VERSION, PROTOCOL_FEATURES, HOST_PROTOCOL, DEFINITION_SCHEMA_VERSION, PROTOCOL_HEADER, SUBJECT_HEADER, SCOPES_HEADER, SESSION_ID_HEADER, TURN_ID_HEADER, AGENT_ID_HEADER, TENANT_ID_PATTERN, isTenantId, newTenantId, newPrincipalId, checkCompatibility, compareVersions, ERROR_CODES, } from "@nylorun/core/compatibility";
|
|
2
2
|
export type { ProtocolFeature, ProtocolRange, Compatibility, ErrorCode, } from "@nylorun/core/compatibility";
|
|
3
|
+
export { checkHealth, describeIncompatibility, parseProtocolRange, } from "@nylorun/core/transport";
|
|
4
|
+
export type { HealthCheck, Incompatibility } from "@nylorun/core/transport";
|
|
3
5
|
export { Agent, AgentBuilder } from "./builder.js";
|
|
4
6
|
export { AgentBuildError, AgentLifecycleError, VerdictSchema, isVerdict, WorkflowBuildError, isBuiltWorkflow, capability, CapabilityBuilder, flow, tool, http, defineSchema, ToolError, } from "@nylorun/core/define";
|
|
5
7
|
export type { FlowAgentBuilder, Flow, Named, LoopOptions, StageOptions, CapabilityIdentity, HttpTarget, } from "@nylorun/core/define";
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// Definition authoring and wire contracts are the only harness runtime imports.
|
|
2
2
|
export { hashManifest, PROTOCOL_VERSION, PROTOCOL_FEATURES, HOST_PROTOCOL, DEFINITION_SCHEMA_VERSION, PROTOCOL_HEADER, SUBJECT_HEADER, SCOPES_HEADER, SESSION_ID_HEADER, TURN_ID_HEADER, AGENT_ID_HEADER, TENANT_ID_PATTERN, isTenantId, newTenantId, newPrincipalId, checkCompatibility, compareVersions, ERROR_CODES, } from "@nylorun/core/compatibility";
|
|
3
|
+
// The `/health` compatibility probe every Runtime client shares (Studio's server uses it).
|
|
4
|
+
export { checkHealth, describeIncompatibility, parseProtocolRange, } from "@nylorun/core/transport";
|
|
3
5
|
export { Agent, AgentBuilder } from "./builder.js";
|
|
4
6
|
export { AgentBuildError, AgentLifecycleError, VerdictSchema, isVerdict, WorkflowBuildError, isBuiltWorkflow, capability, CapabilityBuilder, flow, tool, http, defineSchema, ToolError, } from "@nylorun/core/define";
|
|
5
7
|
export { SUBJECT_SCOPES } from "@nylorun/core/contracts";
|
package/dist/plugins/load.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync, realpathSync, statSync } from "node:fs";
|
|
2
2
|
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
3
3
|
import { stdioMcpRefusal } from "@nylorun/core/define";
|
|
4
|
+
import { HEADER_NAME_PATTERN, LOOPBACK_HOSTS, McpServerManifestSchema, } from "@nylorun/core/contracts";
|
|
4
5
|
import { loadSkillsFromDirectory } from "../skills/load.js";
|
|
5
6
|
export const PLUGIN_SCHEMA = "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json";
|
|
6
7
|
export const MCP_SCHEMA = "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json";
|
|
@@ -211,38 +212,34 @@ function validateServer(name, value) {
|
|
|
211
212
|
return validateRemote(name, value);
|
|
212
213
|
return `type '${value.type}' is not streamable-http or sse`;
|
|
213
214
|
}
|
|
215
|
+
/**
|
|
216
|
+
* A plugin's server as a manifest declares it (`McpServerManifestSchema`), named by its key. A
|
|
217
|
+
* plugin may set only type, url and headers, and the URL and headers must be fit to send.
|
|
218
|
+
*/
|
|
214
219
|
function validateRemote(name, value) {
|
|
215
220
|
const allowed = new Set(["type", "url", "headers"]);
|
|
216
221
|
const unknown = Object.keys(value).filter((key) => !allowed.has(key));
|
|
217
222
|
if (unknown.length > 0)
|
|
218
223
|
return `${value.type} servers take only type, url and headers, not ${unknown.join(", ")}`;
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
224
|
+
const parsed = McpServerManifestSchema.safeParse({ ...value, name });
|
|
225
|
+
if (!parsed.success)
|
|
226
|
+
return parsed.error.issues
|
|
227
|
+
.map((issue) => (issue.path.length ? `${issue.path.join(".")}: ${issue.message}` : issue.message))
|
|
228
|
+
.join("; ");
|
|
229
|
+
const server = parsed.data;
|
|
230
|
+
const problem = remoteUrlProblem(server.url);
|
|
222
231
|
if (problem)
|
|
223
232
|
return problem;
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
if (!
|
|
227
|
-
return
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
return `header '${key}' is not a valid header`;
|
|
233
|
-
const folded = key.toLowerCase();
|
|
234
|
-
if (seen.has(folded))
|
|
235
|
-
return `header '${key}' is repeated`;
|
|
236
|
-
seen.add(folded);
|
|
237
|
-
headers[key] = item;
|
|
238
|
-
}
|
|
233
|
+
const seen = new Set();
|
|
234
|
+
for (const [key, item] of Object.entries(server.headers ?? {})) {
|
|
235
|
+
if (!HEADER_NAME_PATTERN.test(key) || /[\r\n\0]/.test(item))
|
|
236
|
+
return `header '${key}' is not a valid header`;
|
|
237
|
+
const folded = key.toLowerCase();
|
|
238
|
+
if (seen.has(folded))
|
|
239
|
+
return `header '${key}' is repeated`;
|
|
240
|
+
seen.add(folded);
|
|
239
241
|
}
|
|
240
|
-
return
|
|
241
|
-
name,
|
|
242
|
-
type: value.type,
|
|
243
|
-
url: value.url,
|
|
244
|
-
...(headers === undefined ? {} : { headers }),
|
|
245
|
-
};
|
|
242
|
+
return server;
|
|
246
243
|
}
|
|
247
244
|
/**
|
|
248
245
|
* Why `value` is not a remote server URL: https, or plain http to this machine's loopback (a
|
|
@@ -264,22 +261,10 @@ function remoteUrlProblem(value) {
|
|
|
264
261
|
return undefined;
|
|
265
262
|
if (url.protocol !== "http:")
|
|
266
263
|
return `url must be https, not ${url.protocol.slice(0, -1)}`;
|
|
267
|
-
if (
|
|
264
|
+
if (LOOPBACK_HOSTS.test(url.hostname))
|
|
268
265
|
return undefined;
|
|
269
266
|
return `plain http is allowed only for localhost, 127.0.0.1 or [::1]; use https for ${url.hostname}`;
|
|
270
267
|
}
|
|
271
|
-
function isLoopback(hostname) {
|
|
272
|
-
const host = hostname.replace(/^\[|\]$/g, "");
|
|
273
|
-
if (host === "localhost" || host === "::1")
|
|
274
|
-
return true;
|
|
275
|
-
const match = /^127\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(host);
|
|
276
|
-
if (!match)
|
|
277
|
-
return false;
|
|
278
|
-
return match.slice(1).every((part) => Number(part) <= 255);
|
|
279
|
-
}
|
|
280
|
-
function isHeaderName(name) {
|
|
281
|
-
return /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/.test(name);
|
|
282
|
-
}
|
|
283
268
|
function isPluginName(value) {
|
|
284
269
|
if (typeof value !== "string" || value.length < 1 || value.length > 64)
|
|
285
270
|
return false;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nylorun/agents",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.1-beta",
|
|
4
4
|
"description": "Nylorun agent definitions and session client",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"test": "vitest run --passWithNoTests"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@nylorun/core": "0.
|
|
46
|
+
"@nylorun/core": "0.19.0-beta",
|
|
47
47
|
"gray-matter": "^4.0.3"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|