@nylorun/agents 0.20.0-beta → 0.21.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.
@@ -1,16 +1,20 @@
1
1
  /**
2
2
  * Environments (protocol 11, D55, D56): `client.environments`. An Environment is one sandbox
3
- * (kind `virtual`, `pod` or `none`) with a recipe that can change, and one vault of secrets for
4
- * its shell, MCP servers and HTTP tools. It has its own id and outlives the sessions that name
5
- * it; whether it serves one session, one person or a project is the app's choice, made here:
3
+ * (kind `virtual`, `pod` or `none`) made from an environment manifest's version or from inline
4
+ * fields, and one vault of secrets for its shell, MCP servers and HTTP tools. It never changes
5
+ * what it runs after it is made (protocol 14, D63): a new version reaches an agent through a new
6
+ * Environment. It has its own id and outlives the sessions that name it; whether it serves one
7
+ * session, one person or a project is the app's choice, made here:
6
8
  *
7
9
  * - `environments.forSession(...)`: one Environment for one session. It creates the
8
10
  * Environment, opens the session on it, and `release()` deletes the Environment (and its
9
11
  * vault) when the session is done.
10
- * - `environments.put(id, recipe)`: one Environment per person or per project, by a
11
- * deterministic id (`users/alice/web`); get-or-create, or change its recipe, in one call.
12
- * Write its secrets with `environments.secrets.put(id, name, secret)` and open sessions on it
13
- * with `createSession({ environment: id })`.
12
+ * - `environments.put(id, { manifest: "analyst-box" })` (or inline fields): one Environment per
13
+ * person or per project, by a deterministic id (`users/alice/web`); get-or-create in one call.
14
+ * Write its secrets with `environments.secrets.put(id, name, { value })` and open sessions on
15
+ * it with `createSession({ environment: id })`.
16
+ * - `environments.manifests.save(manifest)`: save an environment manifest's next version, like
17
+ * `saveAgent`; `plan(manifest)` answers what a save would do and saves nothing.
14
18
  *
15
19
  * Ids may hold `/`; they are sent percent-encoded as one path segment. Secret values are
16
20
  * write-only: no call reads one back.
@@ -18,9 +22,22 @@
18
22
  * Disk versions (Host feature `environment-disk-versions`): `snapshot(id)` keeps a version of
19
23
  * the Environment's disk between turns, `restore(id, version)` puts one back, and
20
24
  * `put(id, { from: { environment, version } })` creates a new Environment from another's version.
25
+ *
26
+ * `fork(id, { id: next })` is how an Environment gets a new manifest version (Q15): a new
27
+ * Environment made from it, with the source's disk and secrets; `planFork` answers what it would do.
21
28
  */
22
- import { EnvironmentPageSchema, isEnvironmentId, } from "@nylorun/core/contracts";
29
+ import { ENVIRONMENT_MANIFEST_SCHEMA_VERSION, EnvironmentManifestSchema, EnvironmentPageSchema, isEnvironmentId, isEnvironmentManifestName, } from "@nylorun/core/contracts";
30
+ import { definitionFileHash, FilesClient } from "./files.js";
23
31
  import { id, segment } from "./http.js";
32
+ /**
33
+ * An environment manifest (protocol 14, D63): checked now against the schema the Runtime
34
+ * enforces, with `manifestSchemaVersion` filled in and `$schema` dropped. Save it with
35
+ * `client.environments.manifests.save()`, or make an Environment from it inline.
36
+ */
37
+ export function environmentManifest(input) {
38
+ const { $schema: _, ...rest } = input;
39
+ return EnvironmentManifestSchema.parse({ manifestSchemaVersion: ENVIRONMENT_MANIFEST_SCHEMA_VERSION, ...rest });
40
+ }
24
41
  /** An Environment's secrets: `client.environments.secrets`. */
25
42
  export class EnvironmentSecretsClient {
26
43
  transport;
@@ -32,7 +49,9 @@ export class EnvironmentSecretsClient {
32
49
  /**
33
50
  * Creates or replaces secret `name` (`PUT /v1/environments/{id}/secrets/{name}`): for
34
51
  * `environment_secret` and `environment_variable` the name is the variable's. A second
35
- * credential for a host or a URL is a 409 `credential_conflict`.
52
+ * credential for a host or a URL is a 409 `credential_conflict`. When what the Environment
53
+ * runs declares the secret's slot (D64), send only `{ value }` (or `{ headers }`); a name it
54
+ * does not declare is `409 secret_not_in_manifest`.
36
55
  */
37
56
  async put(environmentId, name, secret, options = {}) {
38
57
  return this.transport.json(`${this.path(environmentId)}/secrets/${segment(name)}`, "PUT", { requestId: id(), ...secret }, options.signal);
@@ -49,6 +68,71 @@ export class EnvironmentSecretsClient {
49
68
  return this.transport.json(`${this.path(environmentId)}/secrets/${segment(name)}`, "DELETE", undefined, options.signal);
50
69
  }
51
70
  }
71
+ /**
72
+ * A manifest `files` entry by content hash (Q6): `contents` (each file's path under `path` and
73
+ * its bytes or text) hashed, as `entry` for the manifest and `bytes` for `manifests.save`'s
74
+ * `files`, which uploads them. Each file holds at most 10 MiB.
75
+ */
76
+ export async function environmentFiles(path, contents) {
77
+ const files = {};
78
+ const bytes = {};
79
+ for (const [name, content] of Object.entries(contents).sort(([a], [b]) => a.localeCompare(b))) {
80
+ const data = typeof content === "string" ? new TextEncoder().encode(content) : content;
81
+ const sha256 = await definitionFileHash(data);
82
+ files[name] = sha256;
83
+ bytes[sha256] = data;
84
+ }
85
+ return { entry: { path, files }, bytes };
86
+ }
87
+ export class EnvironmentManifestsClient {
88
+ transport;
89
+ constructor(transport) {
90
+ this.transport = transport;
91
+ }
92
+ path(name) {
93
+ if (!isEnvironmentManifestName(name))
94
+ throw new TypeError(`${name} is not an environment manifest's name: 1-64 letters, digits, '.', '_' and '-'`);
95
+ return `/v1/environment-manifests/${segment(name)}`;
96
+ }
97
+ async send(manifest, query, options) {
98
+ const { ifMatch, signal } = options;
99
+ const body = environmentManifest(manifest);
100
+ // Its files by hash (Q6): uploaded first, each only when the Runtime does not hold it.
101
+ if (options.files)
102
+ await new FilesClient(this.transport).ensure(new Map(Object.entries(options.files).map(([sha256, bytes]) => [sha256, { size: bytes.byteLength, read: async () => bytes }])), signal ? { signal } : {});
103
+ const transport = ifMatch === undefined ? this.transport : this.transport.withHeaders({ "If-Match": String(ifMatch) });
104
+ return transport.json(`${this.path(body.name)}${query}`, "PUT", { requestId: id(), ...body }, signal);
105
+ }
106
+ /**
107
+ * Saves `manifest` (`PUT /v1/environment-manifests/{name}`). `ifMatch`: the version you read
108
+ * (`0` for none yet); a save over a newer one is `412 manifest_changed`. `files`: the bytes of
109
+ * its files by hash (`environmentFiles(...).bytes`), uploaded first when the Runtime lacks them.
110
+ */
111
+ async save(manifest, options = {}) {
112
+ return this.send(manifest, "", options);
113
+ }
114
+ /** What saving `manifest` would do (`?dryRun=true`): changes, errors, warnings, and the Environments behind. */
115
+ async plan(manifest, options = {}) {
116
+ return this.send(manifest, "?dryRun=true", options);
117
+ }
118
+ /** The latest version of manifest `name`, or version `version`. */
119
+ async get(name, options = {}) {
120
+ return this.transport.json(`${this.path(name)}${options.version === undefined ? "" : `/versions/${options.version}`}`, "GET", undefined, options.signal);
121
+ }
122
+ /** Each manifest's latest version. */
123
+ async list(options = {}) {
124
+ const reply = await this.transport.json("/v1/environment-manifests", "GET", undefined, options.signal);
125
+ return reply.manifests;
126
+ }
127
+ /** Every version of manifest `name`, newest first. */
128
+ async versions(name, options = {}) {
129
+ return this.transport.json(`${this.path(name)}/versions`, "GET", undefined, options.signal);
130
+ }
131
+ /** Deletes every version; refused while an Environment was made from one (`409 manifest_in_use`). */
132
+ async delete(name, options = {}) {
133
+ return this.transport.json(this.path(name), "DELETE", undefined, options.signal);
134
+ }
135
+ }
52
136
  export class EnvironmentsClient {
53
137
  transport;
54
138
  createSession;
@@ -67,11 +151,17 @@ export class EnvironmentsClient {
67
151
  get secrets() {
68
152
  return new EnvironmentSecretsClient(this.transport, (environmentId) => this.path(environmentId));
69
153
  }
154
+ /** Environment manifests (protocol 14, D63): named, versioned definitions of a kind of Environment. */
155
+ get manifests() {
156
+ return new EnvironmentManifestsClient(this.transport);
157
+ }
70
158
  /**
71
159
  * Creates the Environment and its vault, or finds the one with this id
72
- * (`PUT /v1/environments/{id}`). On an existing one, `kind` and `storage` are fixed (a
73
- * different one is a 409), labels replace, and any other change writes the recipe and bumps
74
- * its `version`: a pod's image and resources apply when it next starts from stopped.
160
+ * (`PUT /v1/environments/{id}`), from an environment manifest (`{ manifest: "name" }` takes
161
+ * the latest version, `"name@n"` version n) or from inline fields. It never changes what it
162
+ * runs after that (protocol 14): on an existing one, labels replace and `ownerUserId` sets or
163
+ * clears the owner guard, fields that match are accepted, and anything else is
164
+ * `409 environment_immutable`.
75
165
  */
76
166
  async put(environmentId, recipe = {}, options = {}) {
77
167
  return this.transport.json(this.path(environmentId), "PUT", { requestId: id(), ...recipe }, options.signal);
@@ -121,6 +211,26 @@ export class EnvironmentsClient {
121
211
  await this.transport.requireFeature("environment-disk-versions", options.signal);
122
212
  return this.transport.json(`${this.path(environmentId)}/restore`, "POST", { requestId: id(), version }, options.signal);
123
213
  }
214
+ /**
215
+ * Forks the Environment into a new one, `options.id` (Q15): how an Environment gets a new
216
+ * manifest version, since none ever changes what it runs. It is made from `options.manifest`
217
+ * (default the source's manifest at its latest version), its disk starts from `options.disk`
218
+ * (`latest`, the default, snapshots first; a version; or `empty`) and the secrets the new
219
+ * manifest declares are copied (`options.secrets: "none"` copies none). The source keeps what
220
+ * it runs, its disk and its sessions. Answers the new Environment and the plan it ran.
221
+ */
222
+ async fork(environmentId, options) {
223
+ const { signal, ...body } = options;
224
+ await this.transport.requireFeature("environment-manifests", signal);
225
+ return this.transport.json(`${this.path(environmentId)}/fork`, "POST", { requestId: id(), ...body }, signal);
226
+ }
227
+ /** What `fork` would do (the disk, the secrets copied and left behind, the changes), making nothing. */
228
+ async planFork(environmentId, options) {
229
+ const { signal, ...body } = options;
230
+ await this.transport.requireFeature("environment-manifests", signal);
231
+ const answer = await this.transport.json(`${this.path(environmentId)}/fork`, "POST", { ...body, dryRun: true }, signal);
232
+ return answer.plan;
233
+ }
124
234
  /** The Environment's disk versions, newest first. */
125
235
  async versions(environmentId, options = {}) {
126
236
  await this.transport.requireFeature("environment-disk-versions", options.signal);
@@ -134,9 +244,24 @@ export class EnvironmentsClient {
134
244
  async delete(environmentId, options = {}) {
135
245
  return this.transport.json(this.path(environmentId), "DELETE", undefined, options.signal);
136
246
  }
137
- /** The Environment's lifecycle events, from `from` on. */
247
+ /**
248
+ * The Environment's events, from `from` on: its lifecycle, and `environment.egress` for every
249
+ * connection its sandbox opens (with the turns that were running). `types` keeps only those
250
+ * types; `since` and `until` keep events recorded in that window (ISO times or `Date`s).
251
+ */
138
252
  async events(environmentId, options = {}) {
139
- const reply = await this.transport.json(`${this.path(environmentId)}/events${options.from === undefined ? "" : `?from=${options.from}`}`, "GET", undefined, options.signal);
253
+ const query = new URLSearchParams();
254
+ if (options.from !== undefined)
255
+ query.set("from", String(options.from));
256
+ if (options.types !== undefined && options.types.length > 0)
257
+ query.set("types", options.types.join(","));
258
+ const iso = (at) => (at instanceof Date ? at.toISOString() : at);
259
+ if (options.since !== undefined)
260
+ query.set("since", iso(options.since));
261
+ if (options.until !== undefined)
262
+ query.set("until", iso(options.until));
263
+ const search = query.toString();
264
+ const reply = await this.transport.json(`${this.path(environmentId)}/events${search === "" ? "" : `?${search}`}`, "GET", undefined, options.signal);
140
265
  return reply.events;
141
266
  }
142
267
  /**
package/dist/http.js CHANGED
@@ -28,12 +28,12 @@ export class IncompatibleRuntimeError extends Error {
28
28
  }
29
29
  function remedyFor(compatibility) {
30
30
  if (compatibility.reason === "feature") {
31
- return "Upgrade the Runtime Host (nylorun runtime restart from a newer CLI) so it advertises the required features.";
31
+ return "Upgrade the Runtime so it advertises the required features (a local Tenant: npx nylorun@latest start).";
32
32
  }
33
33
  if (compatibility.client < compatibility.host.min) {
34
34
  return "Upgrade this client (npm i -D @nylorun/agents@latest) or use a CLI that matches the Host protocol.";
35
35
  }
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>).";
36
+ return "Upgrade the Runtime (a local Tenant: npx nylorun@latest start), or install a matching older client (npm i -D @nylorun/agents@<compatible>).";
37
37
  }
38
38
  export class Transport {
39
39
  url;
package/dist/index.d.ts CHANGED
@@ -3,28 +3,26 @@ export type { ProtocolFeature, ProtocolRange, Compatibility, ErrorCode, } from "
3
3
  export { checkHealth, describeIncompatibility, parseProtocolRange, } from "@nylorun/core/transport";
4
4
  export type { HealthCheck, Incompatibility } from "@nylorun/core/transport";
5
5
  export { Agent, AgentBuilder } from "./builder.js";
6
- export { AgentBuildError, AgentLifecycleError, VerdictSchema, isVerdict, WorkflowBuildError, isBuiltWorkflow, capability, CapabilityBuilder, flow, tool, http, defineSchema, ToolError, } from "@nylorun/core/define";
7
- export type { FlowAgentBuilder, Flow, Named, LoopOptions, StageOptions, CapabilityIdentity, HttpTarget, } from "@nylorun/core/define";
6
+ export { AgentBuildError, AgentLifecycleError, VerdictSchema, isVerdict, WorkflowBuildError, isBuiltWorkflow, flow, tool, http, defineSchema, ToolError, } from "@nylorun/core/define";
7
+ export type { FlowAgentBuilder, Flow, Named, LoopOptions, StageOptions, HttpTarget, } from "@nylorun/core/define";
8
8
  export type { AgentOptions, AgentManifest, BuiltAgent, BuiltWorkflow, Verdict, WorkflowBinding, WorkflowManifest, ToolDefinition, ToolExecutionContext, ToolOutcome, JsonValue, JsonObject, } from "@nylorun/core/define";
9
- export type { SessionPage, SessionListItem, SessionManifestView, HistoryPage, SessionUsageTotals, ModelCall, ModelCallsPage, EnvironmentPage, ToolCall, ToolCallsPage, LiveEvent, SessionEvent, SessionEventOf, EventType, SessionCommand, SubjectScope, EnvironmentView, EnvironmentEvent, EnvironmentKind, EnvironmentRecipe, EnvironmentSecretView, EnvironmentSnapshot, EnvironmentRestore, EnvironmentVersions, RestoreEnvironmentRequest, MessagePart, ArtifactView, ArtifactKind, ArtifactListItem, ArtifactPage, ArtifactVersionView, ArtifactLink, UploadArtifactResponse, DefinitionFileView, ArtifactTree, ArtifactDiff, FolderEntry, } from "@nylorun/core/contracts";
10
- export { SUBJECT_SCOPES } from "@nylorun/core/contracts";
9
+ export type { SessionPage, SessionListItem, SessionManifestView, HistoryPage, SessionUsageTotals, ModelCall, ModelCallsPage, EnvironmentPage, ToolCall, ToolCallsPage, LiveEvent, SessionEvent, SessionEventOf, EventType, SessionCommand, SubjectScope, EnvironmentView, EnvironmentEvent, EnvironmentEventType, EgressRunningTurn, EnvironmentKind, EnvironmentRecipe, EnvironmentSecretView, EnvironmentSnapshot, EnvironmentRestore, EnvironmentVersions, RestoreEnvironmentRequest, EnvironmentForkPlan, ForkEnvironmentRequest, ForkEnvironmentResponse, EnvironmentManifest, EnvironmentManifestPlan, EnvironmentManifestView, EnvironmentManifestVersions, SaveEnvironmentManifestResponse, MessagePart, ArtifactView, ArtifactKind, ArtifactListItem, ArtifactPage, ArtifactVersionView, ArtifactLink, UploadArtifactResponse, DefinitionFileView, ArtifactTree, ArtifactDiff, FolderEntry, } from "@nylorun/core/contracts";
10
+ export { ENVIRONMENT_EVENT_TYPES, SUBJECT_SCOPES } from "@nylorun/core/contracts";
11
11
  export { AgentsClient, SessionClient, createClient } from "./client.js";
12
12
  export { ArtifactsClient } from "./artifacts.js";
13
13
  export { FilesClient, definitionFileHash } from "./files.js";
14
14
  export type { ArtifactBody, ArtifactPageOptions, ArtifactLinkWithUrl, UploadArtifactOptions, } from "./artifacts.js";
15
- export type { ActAsOptions, AgentSource, SessionView, CommandOptions, CreateSessionOptions, SessionSandbox, ForSessionOptions, EnvironmentRecipeInput, EnvironmentSecretInput, SessionEnvironmentHandle, } from "./client.js";
16
- export { EnvironmentsClient, EnvironmentSecretsClient } from "./client.js";
15
+ export type { ActAsOptions, AgentSource, SessionView, CommandOptions, CreateSessionOptions, SessionSandbox, ForSessionOptions, EnvironmentFileBytes, EnvironmentInput, EnvironmentManifestInput, EnvironmentSecretInput, SessionEnvironmentHandle, } from "./client.js";
16
+ export { EnvironmentManifestsClient, EnvironmentsClient, EnvironmentSecretsClient, environmentFiles, environmentManifest, } from "./client.js";
17
17
  export { AccessClient } from "./access.js";
18
18
  export { resolveConnection, ConnectionError } from "./connection.js";
19
19
  export type { ResolvedConnection } from "./connection.js";
20
20
  export { RuntimeError, IncompatibleRuntimeError } from "./http.js";
21
21
  export type { Destination, IncompatibleReason, TokenSource } from "./http.js";
22
- export { plugin } from "./plugins/plugin.js";
23
- export type { PluginCapability } from "./plugins/plugin.js";
24
22
  export { loadPlugin, PluginError } from "./plugins/load.js";
25
23
  export type { LoadedPlugin, PluginDiagnostic } from "./plugins/load.js";
26
- export { skills, loadSkillsFromDirectory, resolveSkillsRoot, parseSkill, SkillsError, formatSkillCatalog, SKILLS_USAGE, } from "./skills/index.js";
27
- export type { SkillsCapability, SkillsOptions, SkillDiagnostic, } from "./skills/index.js";
28
- export { mcp, McpError } from "./mcp/index.js";
29
- export type { McpCapability, McpOptions, McpServerSpec } from "./mcp/index.js";
24
+ export { loadSkillsFromDirectory, resolveSkillsRoot, parseSkill, SkillsError, formatSkillCatalog, SKILLS_USAGE, } from "./skills/index.js";
25
+ export type { SkillDiagnostic } from "./skills/index.js";
26
+ export { McpError } from "./mcp/index.js";
27
+ export type { McpServerSpec } from "./mcp/index.js";
30
28
  export type { SessionPageOptions } from "./reads.js";
package/dist/index.js CHANGED
@@ -3,16 +3,15 @@ export { hashManifest, PROTOCOL_VERSION, PROTOCOL_FEATURES, HOST_PROTOCOL, DEFIN
3
3
  // The `/health` compatibility probe every Runtime client shares (Studio's server uses it).
4
4
  export { checkHealth, describeIncompatibility, parseProtocolRange, } from "@nylorun/core/transport";
5
5
  export { Agent, AgentBuilder } from "./builder.js";
6
- export { AgentBuildError, AgentLifecycleError, VerdictSchema, isVerdict, WorkflowBuildError, isBuiltWorkflow, capability, CapabilityBuilder, flow, tool, http, defineSchema, ToolError, } from "@nylorun/core/define";
7
- export { SUBJECT_SCOPES } from "@nylorun/core/contracts";
6
+ export { AgentBuildError, AgentLifecycleError, VerdictSchema, isVerdict, WorkflowBuildError, isBuiltWorkflow, flow, tool, http, defineSchema, ToolError, } from "@nylorun/core/define";
7
+ export { ENVIRONMENT_EVENT_TYPES, SUBJECT_SCOPES } from "@nylorun/core/contracts";
8
8
  export { AgentsClient, SessionClient, createClient } from "./client.js";
9
9
  export { ArtifactsClient } from "./artifacts.js";
10
10
  export { FilesClient, definitionFileHash } from "./files.js";
11
- export { EnvironmentsClient, EnvironmentSecretsClient } from "./client.js";
11
+ export { EnvironmentManifestsClient, EnvironmentsClient, EnvironmentSecretsClient, environmentFiles, environmentManifest, } from "./client.js";
12
12
  export { AccessClient } from "./access.js";
13
13
  export { resolveConnection, ConnectionError } from "./connection.js";
14
14
  export { RuntimeError, IncompatibleRuntimeError } from "./http.js";
15
- export { plugin } from "./plugins/plugin.js";
16
15
  export { loadPlugin, PluginError } from "./plugins/load.js";
17
- export { skills, loadSkillsFromDirectory, resolveSkillsRoot, parseSkill, SkillsError, formatSkillCatalog, SKILLS_USAGE, } from "./skills/index.js";
18
- export { mcp, McpError } from "./mcp/index.js";
16
+ export { loadSkillsFromDirectory, resolveSkillsRoot, parseSkill, SkillsError, formatSkillCatalog, SKILLS_USAGE, } from "./skills/index.js";
17
+ export { McpError } from "./mcp/index.js";
@@ -1,2 +1,2 @@
1
- export { mcp, McpError, normalizeMcpServers } from "@nylorun/core/define";
2
- export type { McpCapability, McpOptions, McpServerSpec } from "@nylorun/core/define";
1
+ export { McpError, normalizeMcpServers } from "@nylorun/core/define";
2
+ export type { McpServerSpec } from "@nylorun/core/define";
package/dist/mcp/index.js CHANGED
@@ -1 +1 @@
1
- export { mcp, McpError, normalizeMcpServers } from "@nylorun/core/define";
1
+ export { McpError, normalizeMcpServers } from "@nylorun/core/define";
@@ -16,6 +16,7 @@ export declare class PluginError extends Error {
16
16
  export interface LoadedPlugin {
17
17
  readonly root: string;
18
18
  readonly name: string;
19
+ readonly version?: string;
19
20
  readonly description?: string;
20
21
  readonly skills: Readonly<Record<string, LoadedSkill>>;
21
22
  readonly mcpServers: Readonly<Record<string, McpServerManifest>>;
@@ -80,6 +80,7 @@ export function loadPlugin(directory) {
80
80
  return {
81
81
  root,
82
82
  name: parsed.name,
83
+ ...(typeof parsed.version === "string" ? { version: parsed.version } : {}),
83
84
  ...(typeof parsed.description === "string" ? { description: parsed.description } : {}),
84
85
  skills,
85
86
  mcpServers,
@@ -1,13 +1,17 @@
1
- import type { CapabilityDeclaration } from "@nylorun/core/define";
1
+ import type { PartDeclaration } from "@nylorun/core/define";
2
2
  import { type LoadedPlugin, type PluginDiagnostic } from "./load.js";
3
- export interface PluginCapability extends CapabilityDeclaration {
3
+ export interface PluginPart extends PartDeclaration {
4
4
  readonly diagnostics: readonly PluginDiagnostic[];
5
5
  }
6
6
  /**
7
- * Read an Agent Plugin package and return one capability for `Agent.use`. Each part it skips
7
+ * Read an Agent Plugin package as one part of an agent, for `.plugin()`. Each part it skips
8
8
  * (an invalid MCP server or skill) is a process warning, so building or registering the agent
9
9
  * says why the agent has less than the package declares. A stdio MCP server throws: Nylorun
10
10
  * accepts remote servers only.
11
11
  */
12
- export declare function plugin(directory: string): PluginCapability;
13
- export declare function capabilityFromPlugin(loaded: LoadedPlugin): PluginCapability;
12
+ export declare function plugin(directory: string): PluginPart;
13
+ /**
14
+ * The part a plugin adds: its skills and remote MCP servers, each labelled with the plugin's name
15
+ * and version. The plugin itself is not in the manifest; the labels say where each part came from.
16
+ */
17
+ export declare function partFromPlugin(loaded: LoadedPlugin): PluginPart;
@@ -1,8 +1,7 @@
1
- import { formatSkillCatalog, SKILLS_USAGE } from "../skills/catalog.js";
2
1
  import { skillsDeclaration } from "../skills/skills.js";
3
2
  import { loadPlugin, } from "./load.js";
4
3
  /**
5
- * Read an Agent Plugin package and return one capability for `Agent.use`. Each part it skips
4
+ * Read an Agent Plugin package as one part of an agent, for `.plugin()`. Each part it skips
6
5
  * (an invalid MCP server or skill) is a process warning, so building or registering the agent
7
6
  * says why the agent has less than the package declares. A stdio MCP server throws: Nylorun
8
7
  * accepts remote servers only.
@@ -10,7 +9,7 @@ import { loadPlugin, } from "./load.js";
10
9
  export function plugin(directory) {
11
10
  const loaded = loadPlugin(directory);
12
11
  warn(loaded);
13
- return capabilityFromPlugin(loaded);
12
+ return partFromPlugin(loaded);
14
13
  }
15
14
  function warn(loaded) {
16
15
  for (const diagnostic of loaded.diagnostics) {
@@ -23,26 +22,25 @@ function warn(loaded) {
23
22
  });
24
23
  }
25
24
  }
26
- export function capabilityFromPlugin(loaded) {
27
- const names = Object.values(loaded.skills);
28
- const instructions = names.length === 0
29
- ? undefined
30
- : [
31
- SKILLS_USAGE,
32
- formatSkillCatalog(names.map((skill) => ({
33
- name: skill.name,
34
- description: skill.description,
35
- }))),
36
- ];
25
+ /**
26
+ * The part a plugin adds: its skills and remote MCP servers, each labelled with the plugin's name
27
+ * and version. The plugin itself is not in the manifest; the labels say where each part came from.
28
+ */
29
+ export function partFromPlugin(loaded) {
30
+ const label = {
31
+ name: loaded.name,
32
+ ...(loaded.version === undefined ? {} : { version: loaded.version }),
33
+ };
34
+ const skills = Object.values(loaded.skills);
35
+ const servers = Object.entries(loaded.mcpServers);
37
36
  return {
38
- id: loaded.name,
39
- type: "agent-plugin",
40
- ...(loaded.description === undefined ? {} : { description: loaded.description }),
41
- ...(instructions === undefined ? {} : { instructions }),
42
- ...(names.length === 0 ? {} : skillsDeclaration(names)),
43
- ...(Object.keys(loaded.mcpServers).length === 0
37
+ id: `plugin:${loaded.name}`,
38
+ ...(skills.length === 0 ? {} : skillsDeclaration(skills, label)),
39
+ ...(servers.length === 0
44
40
  ? {}
45
- : { mcpServers: loaded.mcpServers }),
41
+ : {
42
+ mcpServers: Object.fromEntries(servers.map(([name, server]) => [name, { ...server, plugin: label }])),
43
+ }),
46
44
  diagnostics: loaded.diagnostics,
47
45
  };
48
46
  }
@@ -20,8 +20,6 @@ export interface AgentSource {
20
20
  build?(): BuiltAgent | BuiltWorkflow;
21
21
  getBinding?: BuiltAgent["getBinding"] | BuiltWorkflow["getBinding"];
22
22
  }
23
- /** Closures stay on the live binding. The published manifest cannot name them. */
24
- export declare function assertNoMiddlewareClosures(agent: AgentSource): void;
25
23
  export interface SessionView {
26
24
  id: string;
27
25
  agentId: string;
@@ -96,11 +94,13 @@ export declare class AgentsClient {
96
94
  * it names that the Runtime does not hold. A definition with a tool that runs your code
97
95
  * (`tool({ run })`, a flow's tool stage) is refused before anything is sent: the Runtime runs
98
96
  * agents from their manifests alone. `implementationVersion` defaults to
99
- * `NYLORUN_IMPLEMENTATION_VERSION`, else `dev`.
97
+ * `NYLORUN_IMPLEMENTATION_VERSION`, else `dev`. `dryRun` (protocol 14) runs every check and
98
+ * saves nothing; the skill files are still uploaded, since they are stored by content.
100
99
  */
101
100
  saveAgent(agent: AgentSource | BuiltWorkflow, options?: {
102
101
  implementationVersion?: string;
103
102
  requestId?: string;
103
+ dryRun?: boolean;
104
104
  }): Promise<unknown>;
105
105
  createSession(options: CreateSessionOptions): Promise<SessionClient>;
106
106
  session(sessionId: string): SessionClient;
@@ -241,10 +241,14 @@ export declare class SessionClient {
241
241
  effectId: string;
242
242
  turnId: string;
243
243
  callId: string | null;
244
- capabilityId: string | null;
245
244
  toolName: string | null;
246
- kind: "http" | "mcp" | "platform" | "sandbox" | null;
245
+ kind: "harness" | "http" | "mcp" | "platform" | "sandbox" | null;
247
246
  server: string | null;
247
+ skill: string | null;
248
+ plugin: {
249
+ name: string;
250
+ version?: string | undefined;
251
+ } | null;
248
252
  status: "cancelled" | "completed" | "failed" | "held" | "invoking" | "uncertain";
249
253
  attempts: number;
250
254
  input: unknown;
@@ -369,5 +373,5 @@ export { RuntimeError, IncompatibleRuntimeError } from "./http.js";
369
373
  export type { Destination, IncompatibleReason } from "./http.js";
370
374
  export type { LiveEvent } from "@nylorun/core/contracts";
371
375
  export { AccessClient } from "./access.js";
372
- export { EnvironmentSecretsClient, EnvironmentsClient } from "./environments.js";
373
- export type { EnvironmentRecipeInput, EnvironmentSecretInput, ForSessionOptions, SessionEnvironmentHandle, } from "./environments.js";
376
+ export { EnvironmentManifestsClient, EnvironmentSecretsClient, EnvironmentsClient, environmentFiles, environmentManifest, } from "./environments.js";
377
+ export type { EnvironmentFileBytes, EnvironmentInput, EnvironmentManifestInput, EnvironmentSecretInput, ForSessionOptions, SessionEnvironmentHandle, } from "./environments.js";
@@ -30,7 +30,6 @@ function linkedSessionId(event) {
30
30
  ? sessionId
31
31
  : undefined;
32
32
  }
33
- const middlewareClosure = "Hosted definitions support declarative capabilities, not middleware closures";
34
33
  /** The agent's declarations, then those of each agent it uses as a tool. */
35
34
  function declarationsOf(agent) {
36
35
  if (isBuiltWorkflow(agent) || agent.manifest.kind === "workflow")
@@ -39,15 +38,15 @@ function declarationsOf(agent) {
39
38
  if (isBuiltWorkflow(built))
40
39
  return [];
41
40
  const binding = built.getBinding?.();
42
- if (!binding || !("declarations" in binding))
41
+ if (!binding || !("parts" in binding))
43
42
  return [];
44
- const found = [...(binding.declarations ?? [])];
43
+ const found = [...(binding.parts ?? [])];
45
44
  for (const tool of binding.tools ?? []) {
46
45
  const delegate = delegateOf(tool);
47
- found.push(...(delegate?.agent?.getBinding().declarations ?? []));
46
+ found.push(...(delegate?.agent?.getBinding().parts ?? []));
48
47
  // A flow agent used as a tool: its agents' declarations.
49
48
  for (const leaf of Object.values(delegate?.workflow?.getBinding().agents ?? {}))
50
- found.push(...(leaf.declarations ?? []));
49
+ found.push(...(leaf.parts ?? []));
51
50
  }
52
51
  return found;
53
52
  }
@@ -58,11 +57,6 @@ function skillFilesOf(agent, into = new Map()) {
58
57
  into.set(sha256, source);
59
58
  return into;
60
59
  }
61
- /** Closures stay on the live binding. The published manifest cannot name them. */
62
- export function assertNoMiddlewareClosures(agent) {
63
- if (declarationsOf(agent).some((item) => item.hasMiddleware))
64
- throw new Error(middlewareClosure);
65
- }
66
60
  export class AgentsClient {
67
61
  transport;
68
62
  /** The subject this client acts for (`as`), or `undefined` for the principal itself. */
@@ -134,7 +128,8 @@ export class AgentsClient {
134
128
  * it names that the Runtime does not hold. A definition with a tool that runs your code
135
129
  * (`tool({ run })`, a flow's tool stage) is refused before anything is sent: the Runtime runs
136
130
  * agents from their manifests alone. `implementationVersion` defaults to
137
- * `NYLORUN_IMPLEMENTATION_VERSION`, else `dev`.
131
+ * `NYLORUN_IMPLEMENTATION_VERSION`, else `dev`. `dryRun` (protocol 14) runs every check and
132
+ * saves nothing; the skill files are still uploaded, since they are stored by content.
138
133
  */
139
134
  async saveAgent(agent, options = {}) {
140
135
  const files = new Map();
@@ -142,12 +137,10 @@ export class AgentsClient {
142
137
  // A flow agent embeds its agents: one document, carrying their files.
143
138
  for (const binding of Object.values(agent.getBinding().agents)) {
144
139
  const leaf = { id: binding.manifest.id, manifest: binding.manifest, getBinding: () => binding };
145
- assertNoMiddlewareClosures(leaf);
146
140
  skillFilesOf(leaf, files);
147
141
  }
148
142
  }
149
143
  else {
150
- assertNoMiddlewareClosures(agent);
151
144
  skillFilesOf(agent, files);
152
145
  }
153
146
  const refusal = codeToolRefusal(agent.manifest) ??
@@ -155,7 +148,7 @@ export class AgentsClient {
155
148
  if (refusal)
156
149
  throw new Error(refusal);
157
150
  await this.files.ensure(files);
158
- return this.transport.json(`/v1/agents/${segment(agent.id)}`, "PUT", {
151
+ return this.transport.json(`/v1/agents/${segment(agent.id)}${options.dryRun ? "?dryRun=true" : ""}`, "PUT", {
159
152
  requestId: options.requestId ?? id(),
160
153
  manifest: agent.manifest,
161
154
  implementationVersion: options.implementationVersion ?? env("NYLORUN_IMPLEMENTATION_VERSION") ?? "dev",
@@ -392,4 +385,4 @@ export class SessionClient {
392
385
  }
393
386
  export { RuntimeError, IncompatibleRuntimeError } from "./http.js";
394
387
  export { AccessClient } from "./access.js";
395
- export { EnvironmentSecretsClient, EnvironmentsClient } from "./environments.js";
388
+ export { EnvironmentManifestsClient, EnvironmentSecretsClient, EnvironmentsClient, environmentFiles, environmentManifest, } from "./environments.js";
@@ -1,5 +1,3 @@
1
- export { skills } from "./skills.js";
2
- export type { SkillsCapability, SkillsOptions } from "./skills.js";
3
1
  export { loadSkillsFromDirectory, resolveSkillsRoot, parseSkill, SkillsError, } from "./load.js";
4
2
  export type { LoadedSkill, SkillDiagnostic } from "./load.js";
5
- export { formatSkillCatalog, SKILLS_USAGE } from "./catalog.js";
3
+ export { formatSkillCatalog, SKILLS_USAGE } from "@nylorun/core/define";
@@ -1,3 +1,2 @@
1
- export { skills } from "./skills.js";
2
1
  export { loadSkillsFromDirectory, resolveSkillsRoot, parseSkill, SkillsError, } from "./load.js";
3
- export { formatSkillCatalog, SKILLS_USAGE } from "./catalog.js";
2
+ export { formatSkillCatalog, SKILLS_USAGE } from "@nylorun/core/define";
@@ -1,16 +1,12 @@
1
- import type { CapabilityDeclaration } from "@nylorun/core/define";
1
+ import type { PartDeclaration, PluginLabel } from "@nylorun/core/define";
2
2
  import { type LoadedSkill, type SkillDiagnostic } from "./load.js";
3
- export interface SkillsOptions {
4
- /** Capability id. Defaults to the catalog directory basename. */
5
- readonly id?: string;
6
- }
7
- export interface SkillsCapability extends CapabilityDeclaration {
3
+ export interface SkillsPart extends PartDeclaration {
8
4
  readonly diagnostics: readonly SkillDiagnostic[];
9
5
  /** Absolute catalog directory that was loaded. */
10
6
  readonly root: string;
11
7
  }
12
8
  /**
13
- * Load an agentskills.io catalog folder and return one capability for `Agent.use`.
9
+ * Load an agentskills.io catalog folder as one part of an agent, for `.skills()`.
14
10
  *
15
11
  * Expected layout:
16
12
  * ```
@@ -23,8 +19,12 @@ export interface SkillsCapability extends CapabilityDeclaration {
23
19
  *
24
20
  * Sets `skills`: each skill's name, description and files (every file of its folder, binary
25
21
  * included, by content hash). The client uploads the files the Runtime lacks when it registers
26
- * the agent, and the Runtime serves `load_skill` / `read_skill_resource` from them.
22
+ * the agent; the Runtime adds the skill catalog and serves `load_skill` / `read_skill_resource`
23
+ * from them.
24
+ */
25
+ export declare function skills(directory: string): SkillsPart;
26
+ /**
27
+ * The manifest's skills and the bytes behind their files. A plugin's skills carry its label, so
28
+ * a tool call can be traced to the plugin that brought the skill.
27
29
  */
28
- export declare function skills(directory: string, options?: SkillsOptions): SkillsCapability;
29
- /** The manifest's skills and the bytes behind their files, for a capability declaration. */
30
- export declare function skillsDeclaration(listed: readonly LoadedSkill[]): Required<Pick<CapabilityDeclaration, "skills" | "skillFiles">>;
30
+ export declare function skillsDeclaration(listed: readonly LoadedSkill[], plugin?: PluginLabel): Required<Pick<PartDeclaration, "skills" | "skillFiles">>;