@nylorun/agents 0.20.0-beta → 0.22.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,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
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * The Tenant API client classes, with no Node-only imports. `client.ts` adds `createClient`,
2
+ * The Runtime API client classes, with no Node-only imports. `client.ts` adds `createClient`,
3
3
  * which resolves the connection from the environment or the Project link.
4
4
  */
5
5
  import type { AgentManifest, BuiltAgent, BuiltWorkflow, JsonValue, SandboxToolName, WorkflowManifest } from "@nylorun/core/define";
@@ -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">>;
@@ -1,8 +1,7 @@
1
1
  import { basename } from "node:path";
2
- import { formatSkillCatalog, SKILLS_USAGE } from "./catalog.js";
3
2
  import { loadSkillsFromDirectory, resolveSkillsRoot, } from "./load.js";
4
3
  /**
5
- * Load an agentskills.io catalog folder and return one capability for `Agent.use`.
4
+ * Load an agentskills.io catalog folder as one part of an agent, for `.skills()`.
6
5
  *
7
6
  * Expected layout:
8
7
  * ```
@@ -15,39 +14,34 @@ import { loadSkillsFromDirectory, resolveSkillsRoot, } from "./load.js";
15
14
  *
16
15
  * Sets `skills`: each skill's name, description and files (every file of its folder, binary
17
16
  * included, by content hash). The client uploads the files the Runtime lacks when it registers
18
- * the agent, and the Runtime serves `load_skill` / `read_skill_resource` from them.
17
+ * the agent; the Runtime adds the skill catalog and serves `load_skill` / `read_skill_resource`
18
+ * from them.
19
19
  */
20
- export function skills(directory, options = {}) {
20
+ export function skills(directory) {
21
21
  const root = resolveSkillsRoot(directory);
22
22
  const diagnostics = [];
23
23
  const loaded = loadSkillsFromDirectory(root, diagnostics);
24
24
  const listed = Object.values(loaded);
25
- const id = options.id ?? basename(root);
25
+ const id = `skills:${basename(root)}`;
26
26
  if (listed.length === 0) {
27
27
  return { id, root, diagnostics };
28
28
  }
29
- const { skills: skillManifest, skillFiles } = skillsDeclaration(listed);
30
- return {
31
- id,
32
- root,
33
- instructions: [
34
- SKILLS_USAGE,
35
- formatSkillCatalog(listed.map((skill) => ({
36
- name: skill.name,
37
- description: skill.description,
38
- }))),
39
- ],
40
- skills: skillManifest,
41
- skillFiles,
42
- diagnostics,
43
- };
29
+ return { id, root, ...skillsDeclaration(listed), diagnostics };
44
30
  }
45
- /** The manifest's skills and the bytes behind their files, for a capability declaration. */
46
- export function skillsDeclaration(listed) {
31
+ /**
32
+ * The manifest's skills and the bytes behind their files. A plugin's skills carry its label, so
33
+ * a tool call can be traced to the plugin that brought the skill.
34
+ */
35
+ export function skillsDeclaration(listed, plugin) {
47
36
  return {
48
37
  skills: Object.fromEntries(listed.map((skill) => [
49
38
  skill.name,
50
- { name: skill.name, description: skill.description, files: skill.files },
39
+ {
40
+ name: skill.name,
41
+ description: skill.description,
42
+ files: skill.files,
43
+ ...(plugin === undefined ? {} : { plugin }),
44
+ },
51
45
  ])),
52
46
  skillFiles: Object.assign({}, ...listed.map((skill) => skill.sources)),
53
47
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nylorun/agents",
3
- "version": "0.20.0-beta",
3
+ "version": "0.22.0-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.20.0-beta",
46
+ "@nylorun/core": "0.22.0-beta",
47
47
  "gray-matter": "^4.0.3"
48
48
  },
49
49
  "peerDependencies": {
@@ -1,5 +0,0 @@
1
- export declare const SKILLS_USAGE = "The following skills provide specialized instructions. When a task matches a skill description, call load_skill with that name before proceeding.";
2
- export declare function formatSkillCatalog(skills: readonly {
3
- name: string;
4
- description: string;
5
- }[]): string;
@@ -1,14 +0,0 @@
1
- export const SKILLS_USAGE = "The following skills provide specialized instructions. When a task matches a skill description, call load_skill with that name before proceeding.";
2
- export function formatSkillCatalog(skills) {
3
- const entries = skills
4
- .map((skill) => ` <skill>\n <name>${escapeXml(skill.name)}</name>\n <description>${escapeXml(skill.description)}</description>\n </skill>`)
5
- .join("\n");
6
- return `<available_skills>\n${entries}\n</available_skills>`;
7
- }
8
- function escapeXml(value) {
9
- return value
10
- .replaceAll("&", "&amp;")
11
- .replaceAll("<", "&lt;")
12
- .replaceAll(">", "&gt;")
13
- .replaceAll('"', "&quot;");
14
- }