@nylorun/agents 0.14.0-beta → 0.16.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/dist/files.js ADDED
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Definition files (track R2 M4): `client.files`. A definition names the files it needs by the
3
+ * SHA-256 of their bytes (a skill's folder, `SkillManifest.files`); the Runtime holds each one
4
+ * once, uploaded with `PUT /v1/files/sha256:<hex>`, and refuses a definition naming a file it
5
+ * does not hold (`400 definition_files_missing`). `saveAgent` uploads an agent's files itself.
6
+ */
7
+ import { isDefinitionFileHash, } from "@nylorun/core/contracts";
8
+ import { RuntimeError } from "./http.js";
9
+ /** `sha256:<hex>` of `bytes`. */
10
+ export async function definitionFileHash(bytes) {
11
+ const digest = new Uint8Array(await globalThis.crypto.subtle.digest("SHA-256", bytes));
12
+ return `sha256:${[...digest].map((byte) => byte.toString(16).padStart(2, "0")).join("")}`;
13
+ }
14
+ function pathOf(sha256) {
15
+ if (!isDefinitionFileHash(sha256))
16
+ throw new TypeError(`${sha256} is not a definition file's hash: sha256:<64 lowercase hex>`);
17
+ return `/v1/files/${sha256}`;
18
+ }
19
+ export class FilesClient {
20
+ transport;
21
+ constructor(transport) {
22
+ this.transport = transport;
23
+ }
24
+ /** Whether the Runtime holds the file `sha256` (`sha256:<hex>`) names. */
25
+ async has(sha256, options = {}) {
26
+ try {
27
+ await this.transport.request(pathOf(sha256), {
28
+ method: "HEAD",
29
+ ...(options.signal ? { signal: options.signal } : {}),
30
+ });
31
+ return true;
32
+ }
33
+ catch (error) {
34
+ if (error instanceof RuntimeError && error.status === 404)
35
+ return false;
36
+ throw error;
37
+ }
38
+ }
39
+ /**
40
+ * Uploads one file, at most 10 MiB. The Runtime checks its bytes hash to `sha256` (default:
41
+ * their hash) and keeps a file it already holds as it is.
42
+ */
43
+ async upload(bytes, options = {}) {
44
+ const response = await this.transport.request(pathOf(options.sha256 ?? (await definitionFileHash(bytes))), {
45
+ method: "PUT",
46
+ body: bytes,
47
+ headers: { "content-type": "application/octet-stream" },
48
+ ...(options.signal ? { signal: options.signal } : {}),
49
+ });
50
+ return (await response.json());
51
+ }
52
+ /** Uploads each of `files` the Runtime does not hold yet. */
53
+ async ensure(files, options = {}) {
54
+ for (const [sha256, source] of files) {
55
+ if (await this.has(sha256, options))
56
+ continue;
57
+ await this.upload(await source.read(), { sha256, ...options });
58
+ }
59
+ }
60
+ }
package/dist/http.d.ts CHANGED
@@ -51,11 +51,6 @@ export declare class Transport {
51
51
  * transport's compatibility check, so a copy made per request does not repeat `/health`.
52
52
  */
53
53
  withHeaders(headers: Readonly<Record<string, string>>): Transport;
54
- /**
55
- * The same destination with another bearer (a delivery token for an Action endpoint's
56
- * callbacks). Like `withHeaders`, the copy shares this transport's compatibility check.
57
- */
58
- withKey(key: string): Transport;
59
54
  /** Clears the cached Host compatibility result (used after a 426). */
60
55
  clearCompatibilityCache(): void;
61
56
  private ensureCompatible;
package/dist/http.js CHANGED
@@ -92,14 +92,6 @@ export class Transport {
92
92
  headers: Object.freeze({ ...this.headers, ...headers }),
93
93
  });
94
94
  }
95
- /**
96
- * The same destination with another bearer (a delivery token for an Action endpoint's
97
- * callbacks). Like `withHeaders`, the copy shares this transport's compatibility check.
98
- */
99
- withKey(key) {
100
- const copy = Object.create(Transport.prototype);
101
- return Object.assign(copy, this, { key });
102
- }
103
95
  /** Clears the cached Host compatibility result (used after a 426). */
104
96
  clearCompatibilityCache() {
105
97
  this.check.compatible = false;
package/dist/index.d.ts CHANGED
@@ -1,20 +1,18 @@
1
- export { hashManifest, PROTOCOL_VERSION, PROTOCOL_FEATURES, HOST_PROTOCOL, DEFINITION_SCHEMA_VERSION, PROTOCOL_HEADER, SUBJECT_HEADER, SCOPES_HEADER, TENANT_ID_PATTERN, isTenantId, newTenantId, newPrincipalId, checkCompatibility, compareVersions, ERROR_CODES, } from "@nylorun/core/compatibility";
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
3
  export { Agent, AgentBuilder } from "./builder.js";
4
- export { AgentBuildError, AgentLifecycleError, Loop, Chain, Switch, Parallel, Map, VerdictSchema, isVerdict, withInstructions, withoutTools, WorkflowBuildError, isBuiltWorkflow, capability, CapabilityBuilder, flow, tool, defineSchema, ToolError, } from "@nylorun/core/define";
5
- export type { FlowAgentBuilder, Flow, Named, StageArgs, NestedStageArgs, CapabilityIdentity, } from "@nylorun/core/define";
6
- export type { AgentOptions, AgentManifest, BuiltAgent, BuiltWorkflow, LoopOptions, LoopDecideArgs, LoopVerifyArgs, LoopDecision, ChainOptions, SwitchOptions, ParallelOptions, MapOptions, Verdict, WorkflowBinding, WorkflowManifest, ToolDefinition, ToolExecutionContext, ToolOutcome, Patch, Decision, TurnDecision, HookScope, BeforeHook, AfterHook, JsonValue, JsonObject, } from "@nylorun/core/define";
7
- export type { LiveEvent, SessionEvent, SessionEventOf, EventType, SessionCommand, Action, ActionOutcome, CredentialInfo, CredentialSelection, VaultInfo, SubjectScope, SandboxView, SandboxEvent, SandboxKind, MessagePart, ArtifactView, ArtifactVersionView, ArtifactLink, UploadArtifactResponse, ArtifactTree, ArtifactDiff, FolderEntry, } from "@nylorun/core/contracts";
4
+ export { AgentBuildError, AgentLifecycleError, VerdictSchema, isVerdict, WorkflowBuildError, isBuiltWorkflow, capability, CapabilityBuilder, flow, tool, http, defineSchema, ToolError, } from "@nylorun/core/define";
5
+ export type { FlowAgentBuilder, Flow, Named, LoopOptions, StageOptions, CapabilityIdentity, HttpTarget, } from "@nylorun/core/define";
6
+ export type { AgentOptions, AgentManifest, BuiltAgent, BuiltWorkflow, Verdict, WorkflowBinding, WorkflowManifest, ToolDefinition, ToolExecutionContext, ToolOutcome, JsonValue, JsonObject, } from "@nylorun/core/define";
7
+ export type { SessionPage, SessionListItem, SessionManifestView, HistoryPage, SessionUsageTotals, ModelCall, ModelCallsPage, SandboxPage, LiveEvent, SessionEvent, SessionEventOf, EventType, SessionCommand, CredentialInfo, CredentialSelection, VaultInfo, SubjectScope, SandboxView, SandboxEvent, SandboxKind, MessagePart, ArtifactView, ArtifactVersionView, ArtifactLink, UploadArtifactResponse, DefinitionFileView, ArtifactTree, ArtifactDiff, FolderEntry, } from "@nylorun/core/contracts";
8
8
  export { SUBJECT_SCOPES } from "@nylorun/core/contracts";
9
9
  export { AgentsClient, SessionClient, createClient } from "./client.js";
10
10
  export { ArtifactsClient } from "./artifacts.js";
11
+ export { FilesClient, definitionFileHash } from "./files.js";
11
12
  export type { ArtifactBody, ArtifactLinkWithUrl, UploadArtifactOptions, } from "./artifacts.js";
12
13
  export type { ActAsOptions, AgentSource, SessionView, CommandOptions, CreateSessionOptions, SessionSandbox, ForSessionOptions, SandboxSpec, SessionSandboxHandle, } from "./client.js";
13
14
  export { SandboxesClient } from "./client.js";
14
- export { AccessClient, SigningKeysClient } from "./access.js";
15
- export { createActionHandler } from "./action-handler.js";
16
- export type { ActionHandler, ActionHandlerOptions, RegisterOptions, } from "./action-handler.js";
17
- export type { ExecuteActionOptions, ExecutableDefinition, } from "./execute-action.js";
15
+ export { AccessClient } from "./access.js";
18
16
  export { resolveConnection, ConnectionError } from "./connection.js";
19
17
  export type { ResolvedConnection } from "./connection.js";
20
18
  export { RuntimeError, IncompatibleRuntimeError } from "./http.js";
@@ -23,11 +21,8 @@ export { plugin } from "./plugins/plugin.js";
23
21
  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 { prepareStdioLaunch, expandPluginPlaceholders } from "./plugins/launch.js";
27
- export type { StdioLaunch } from "./plugins/launch.js";
28
24
  export { skills, loadSkillsFromDirectory, resolveSkillsRoot, parseSkill, SkillsError, formatSkillCatalog, SKILLS_USAGE, } from "./skills/index.js";
29
25
  export type { SkillsCapability, SkillsOptions, SkillDiagnostic, } from "./skills/index.js";
30
26
  export { mcp, McpError } from "./mcp/index.js";
31
27
  export type { McpCapability, McpOptions, McpServerSpec } from "./mcp/index.js";
32
- export type { ActionSandbox, ActionSandboxToolResult, CreateActionSandboxOptions, } from "./sandbox/index.js";
33
- export { createActionSandbox, definitionDeclaresSandbox, isActionSandboxTool, } from "./sandbox/index.js";
28
+ export type { SessionPageOptions } from "./reads.js";
package/dist/index.js CHANGED
@@ -1,18 +1,16 @@
1
1
  // Definition authoring and wire contracts are the only harness runtime imports.
2
- export { hashManifest, PROTOCOL_VERSION, PROTOCOL_FEATURES, HOST_PROTOCOL, DEFINITION_SCHEMA_VERSION, PROTOCOL_HEADER, SUBJECT_HEADER, SCOPES_HEADER, TENANT_ID_PATTERN, isTenantId, newTenantId, newPrincipalId, checkCompatibility, compareVersions, ERROR_CODES, } from "@nylorun/core/compatibility";
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
3
  export { Agent, AgentBuilder } from "./builder.js";
4
- export { AgentBuildError, AgentLifecycleError, Loop, Chain, Switch, Parallel, Map, VerdictSchema, isVerdict, withInstructions, withoutTools, WorkflowBuildError, isBuiltWorkflow, capability, CapabilityBuilder, flow, tool, defineSchema, ToolError, } from "@nylorun/core/define";
4
+ export { AgentBuildError, AgentLifecycleError, VerdictSchema, isVerdict, WorkflowBuildError, isBuiltWorkflow, capability, CapabilityBuilder, flow, tool, http, defineSchema, ToolError, } from "@nylorun/core/define";
5
5
  export { SUBJECT_SCOPES } from "@nylorun/core/contracts";
6
6
  export { AgentsClient, SessionClient, createClient } from "./client.js";
7
7
  export { ArtifactsClient } from "./artifacts.js";
8
+ export { FilesClient, definitionFileHash } from "./files.js";
8
9
  export { SandboxesClient } from "./client.js";
9
- export { AccessClient, SigningKeysClient } from "./access.js";
10
- export { createActionHandler } from "./action-handler.js";
10
+ export { AccessClient } from "./access.js";
11
11
  export { resolveConnection, ConnectionError } from "./connection.js";
12
12
  export { RuntimeError, IncompatibleRuntimeError } from "./http.js";
13
13
  export { plugin } from "./plugins/plugin.js";
14
14
  export { loadPlugin, PluginError } from "./plugins/load.js";
15
- export { prepareStdioLaunch, expandPluginPlaceholders } from "./plugins/launch.js";
16
15
  export { skills, loadSkillsFromDirectory, resolveSkillsRoot, parseSkill, SkillsError, formatSkillCatalog, SKILLS_USAGE, } from "./skills/index.js";
17
16
  export { mcp, McpError } from "./mcp/index.js";
18
- export { createActionSandbox, definitionDeclaresSandbox, isActionSandboxTool, } from "./sandbox/index.js";
@@ -1,4 +1,5 @@
1
- import type { McpServerManifest, SkillRecord } from "@nylorun/core/define";
1
+ import { type McpServerManifest } from "@nylorun/core/define";
2
+ import { type LoadedSkill } from "../skills/load.js";
2
3
  export declare const PLUGIN_SCHEMA = "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json";
3
4
  export declare const MCP_SCHEMA = "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json";
4
5
  export interface PluginDiagnostic {
@@ -16,7 +17,7 @@ export interface LoadedPlugin {
16
17
  readonly root: string;
17
18
  readonly name: string;
18
19
  readonly description?: string;
19
- readonly skills: Readonly<Record<string, SkillRecord>>;
20
+ readonly skills: Readonly<Record<string, LoadedSkill>>;
20
21
  readonly mcpServers: Readonly<Record<string, McpServerManifest>>;
21
22
  readonly diagnostics: readonly PluginDiagnostic[];
22
23
  }
@@ -1,7 +1,7 @@
1
- import { existsSync, mkdirSync, readFileSync, realpathSync, statSync, } from "node:fs";
2
- import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
1
+ import { existsSync, readFileSync, realpathSync, statSync } from "node:fs";
2
+ import { isAbsolute, join, relative, resolve, sep } from "node:path";
3
+ import { stdioMcpRefusal } from "@nylorun/core/define";
3
4
  import { loadSkillsFromDirectory } from "../skills/load.js";
4
- import { expandPluginPlaceholders } from "./launch.js";
5
5
  export const PLUGIN_SCHEMA = "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json";
6
6
  export const MCP_SCHEMA = "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json";
7
7
  const PLUGIN_FIELDS = new Set([
@@ -17,7 +17,6 @@ const PLUGIN_FIELDS = new Set([
17
17
  "extensions",
18
18
  ]);
19
19
  const AUTHOR_FIELDS = new Set(["name", "email", "url"]);
20
- const RESERVED_ENV = new Set(["PLUGIN_ROOT", "PLUGIN_DATA"]);
21
20
  export class PluginError extends Error {
22
21
  code;
23
22
  diagnostics;
@@ -76,7 +75,7 @@ export function loadPlugin(directory) {
76
75
  });
77
76
  }
78
77
  const skills = loadSkills(root, diagnostics);
79
- const mcpServers = loadMcp(root, parsed.name, diagnostics);
78
+ const mcpServers = loadMcp(root, diagnostics);
80
79
  return {
81
80
  root,
82
81
  name: parsed.name,
@@ -132,7 +131,7 @@ function loadSkills(root, diagnostics) {
132
131
  codePrefix: "plugin",
133
132
  });
134
133
  }
135
- function loadMcp(root, pluginName, diagnostics) {
134
+ function loadMcp(root, diagnostics) {
136
135
  const location = join(root, "mcp.json");
137
136
  if (!existsSync(location))
138
137
  return {};
@@ -186,101 +185,54 @@ function loadMcp(root, pluginName, diagnostics) {
186
185
  });
187
186
  return {};
188
187
  }
189
- const pluginData = join(dirname(root), ".nylorun", "plugin-data", pluginName);
190
188
  const servers = {};
191
189
  for (const [name, value] of Object.entries(parsed.mcpServers)) {
192
- const server = validateServer(name, value, root, pluginData);
193
- if (!server) {
190
+ if (isRecord(value) && value.type === "stdio")
191
+ throw new PluginError("plugin.mcp-stdio", stdioMcpRefusal(name), diagnostics);
192
+ const server = validateServer(name, value);
193
+ if (typeof server === "string") {
194
194
  diagnostics.push({
195
195
  severity: "warning",
196
196
  code: "plugin.mcp-server-skipped",
197
- message: `Skipped invalid MCP server '${name}'`,
197
+ message: `Skipped invalid MCP server '${name}': ${server}`,
198
198
  path: location,
199
199
  });
200
200
  continue;
201
201
  }
202
- if (server.type === "stdio")
203
- mkdirSync(pluginData, { recursive: true });
204
202
  servers[name] = server;
205
203
  }
206
204
  return servers;
207
205
  }
208
- function validateServer(name, value, root, pluginData) {
206
+ /** The server, or why it is skipped. */
207
+ function validateServer(name, value) {
209
208
  if (!isRecord(value) || typeof value.type !== "string")
210
- return undefined;
211
- if (value.type === "stdio")
212
- return validateStdio(name, value, root, pluginData);
209
+ return "it has no type";
213
210
  if (value.type === "streamable-http" || value.type === "sse")
214
211
  return validateRemote(name, value);
215
- return undefined;
216
- }
217
- function validateStdio(name, value, root, pluginData) {
218
- const allowed = new Set(["type", "command", "args", "env", "cwd"]);
219
- if (Object.keys(value).some((key) => !allowed.has(key)))
220
- return undefined;
221
- if (typeof value.command !== "string" || !isCommandToken(value.command))
222
- return undefined;
223
- if (value.command.startsWith("./") && !commandStaysInside(root, value.command))
224
- return undefined;
225
- let args;
226
- if (value.args !== undefined) {
227
- if (!Array.isArray(value.args) || value.args.some((item) => typeof item !== "string"))
228
- return undefined;
229
- args = value.args;
230
- }
231
- let env;
232
- if (value.env !== undefined) {
233
- if (!isRecord(value.env))
234
- return undefined;
235
- env = {};
236
- for (const [key, item] of Object.entries(value.env)) {
237
- if (typeof item !== "string" || RESERVED_ENV.has(key))
238
- return undefined;
239
- env[key] = item;
240
- }
241
- }
242
- let cwd;
243
- if (value.cwd !== undefined) {
244
- if (typeof value.cwd !== "string" || !isCwdForm(value.cwd))
245
- return undefined;
246
- const expanded = expandPluginPlaceholders(value.cwd, root, pluginData);
247
- const absolute = expanded.startsWith("/") || /^[A-Za-z]:[\\/]/.test(expanded)
248
- ? expanded
249
- : resolve(root, expanded);
250
- const boundary = value.cwd === "${PLUGIN_DATA}" || value.cwd.startsWith("${PLUGIN_DATA}/")
251
- ? pluginData
252
- : root;
253
- if (!isInside(boundary, absolute))
254
- return undefined;
255
- cwd = value.cwd;
256
- }
257
- return {
258
- name,
259
- type: "stdio",
260
- command: value.command,
261
- ...(args === undefined ? {} : { args }),
262
- ...(env === undefined ? {} : { env }),
263
- ...(cwd === undefined ? {} : { cwd }),
264
- };
212
+ return `type '${value.type}' is not streamable-http or sse`;
265
213
  }
266
214
  function validateRemote(name, value) {
267
215
  const allowed = new Set(["type", "url", "headers"]);
268
- if (Object.keys(value).some((key) => !allowed.has(key)))
269
- return undefined;
270
- if (typeof value.url !== "string" || !isRemoteUrl(value.url))
271
- return undefined;
216
+ const unknown = Object.keys(value).filter((key) => !allowed.has(key));
217
+ if (unknown.length > 0)
218
+ return `${value.type} servers take only type, url and headers, not ${unknown.join(", ")}`;
219
+ if (typeof value.url !== "string")
220
+ return "url must be a string";
221
+ const problem = remoteUrlProblem(value.url);
222
+ if (problem)
223
+ return problem;
272
224
  let headers;
273
225
  if (value.headers !== undefined) {
274
226
  if (!isRecord(value.headers))
275
- return undefined;
227
+ return "headers must be an object";
276
228
  const seen = new Set();
277
229
  headers = {};
278
230
  for (const [key, item] of Object.entries(value.headers)) {
279
231
  if (typeof item !== "string" || !isHeaderName(key) || /[\r\n\0]/.test(item))
280
- return undefined;
232
+ return `header '${key}' is not a valid header`;
281
233
  const folded = key.toLowerCase();
282
234
  if (seen.has(folded))
283
- return undefined;
235
+ return `header '${key}' is repeated`;
284
236
  seen.add(folded);
285
237
  headers[key] = item;
286
238
  }
@@ -292,41 +244,29 @@ function validateRemote(name, value) {
292
244
  ...(headers === undefined ? {} : { headers }),
293
245
  };
294
246
  }
295
- function commandStaysInside(root, command) {
296
- const lexical = resolve(root, command);
297
- if (!isInside(root, lexical))
298
- return false;
299
- return !existsSync(lexical) || realInside(root, lexical) !== undefined;
300
- }
301
- function isCommandToken(command) {
302
- if (!command || /\s/.test(command))
303
- return false;
304
- if (command.startsWith("./"))
305
- return !command.split("/").includes("..");
306
- return !command.includes("/") && !command.includes("\\") && !command.includes("${");
307
- }
308
- function isCwdForm(cwd) {
309
- return (cwd.startsWith("./") ||
310
- cwd === "${PLUGIN_ROOT}" ||
311
- cwd.startsWith("${PLUGIN_ROOT}/") ||
312
- cwd === "${PLUGIN_DATA}" ||
313
- cwd.startsWith("${PLUGIN_DATA}/"));
314
- }
315
- function isRemoteUrl(value) {
247
+ /**
248
+ * Why `value` is not a remote server URL: https, or plain http to this machine's loopback (a
249
+ * local Tenant in Docker reaches it on the Docker host). No credentials or fragment.
250
+ */
251
+ function remoteUrlProblem(value) {
316
252
  let url;
317
253
  try {
318
254
  url = new URL(value);
319
255
  }
320
256
  catch {
321
- return false;
257
+ return `url '${value}' is not a URL`;
322
258
  }
323
- if (url.username || url.password || url.hash)
324
- return false;
259
+ if (url.username || url.password)
260
+ return "url must not carry credentials; use headers";
261
+ if (url.hash)
262
+ return "url must not have a fragment";
325
263
  if (url.protocol === "https:")
326
- return true;
264
+ return undefined;
327
265
  if (url.protocol !== "http:")
328
- return false;
329
- return isLoopback(url.hostname);
266
+ return `url must be https, not ${url.protocol.slice(0, -1)}`;
267
+ if (isLoopback(url.hostname))
268
+ return undefined;
269
+ return `plain http is allowed only for localhost, 127.0.0.1 or [::1]; use https for ${url.hostname}`;
330
270
  }
331
271
  function isLoopback(hostname) {
332
272
  const host = hostname.replace(/^\[|\]$/g, "");
@@ -3,6 +3,11 @@ import { type LoadedPlugin, type PluginDiagnostic } from "./load.js";
3
3
  export interface PluginCapability extends CapabilityDeclaration {
4
4
  readonly diagnostics: readonly PluginDiagnostic[];
5
5
  }
6
- /** Read an Agent Plugin package and return one capability for `Agent.use`. */
6
+ /**
7
+ * Read an Agent Plugin package and return one capability for `Agent.use`. Each part it skips
8
+ * (an invalid MCP server or skill) is a process warning, so building or registering the agent
9
+ * says why the agent has less than the package declares. A stdio MCP server throws: Nylorun
10
+ * accepts remote servers only.
11
+ */
7
12
  export declare function plugin(directory: string): PluginCapability;
8
13
  export declare function capabilityFromPlugin(loaded: LoadedPlugin): PluginCapability;
@@ -1,10 +1,28 @@
1
1
  import { formatSkillCatalog, SKILLS_USAGE } from "../skills/catalog.js";
2
+ import { skillsDeclaration } from "../skills/skills.js";
2
3
  import { loadPlugin, } from "./load.js";
3
- /** Read an Agent Plugin package and return one capability for `Agent.use`. */
4
+ /**
5
+ * Read an Agent Plugin package and return one capability for `Agent.use`. Each part it skips
6
+ * (an invalid MCP server or skill) is a process warning, so building or registering the agent
7
+ * says why the agent has less than the package declares. A stdio MCP server throws: Nylorun
8
+ * accepts remote servers only.
9
+ */
4
10
  export function plugin(directory) {
5
11
  const loaded = loadPlugin(directory);
12
+ warn(loaded);
6
13
  return capabilityFromPlugin(loaded);
7
14
  }
15
+ function warn(loaded) {
16
+ for (const diagnostic of loaded.diagnostics) {
17
+ if (diagnostic.severity !== "warning")
18
+ continue;
19
+ process.emitWarning(`Plugin '${loaded.name}': ${diagnostic.message}`, {
20
+ type: "NylorunPluginWarning",
21
+ code: diagnostic.code,
22
+ ...(diagnostic.path === undefined ? {} : { detail: diagnostic.path }),
23
+ });
24
+ }
25
+ }
8
26
  export function capabilityFromPlugin(loaded) {
9
27
  const names = Object.values(loaded.skills);
10
28
  const instructions = names.length === 0
@@ -16,17 +34,12 @@ export function capabilityFromPlugin(loaded) {
16
34
  description: skill.description,
17
35
  }))),
18
36
  ];
19
- const skills = Object.fromEntries(names.map((skill) => [
20
- skill.name,
21
- { name: skill.name, description: skill.description },
22
- ]));
23
37
  return {
24
38
  id: loaded.name,
25
39
  type: "agent-plugin",
26
- pluginRoot: loaded.root,
27
40
  ...(loaded.description === undefined ? {} : { description: loaded.description }),
28
41
  ...(instructions === undefined ? {} : { instructions }),
29
- ...(names.length === 0 ? {} : { skills, skillRecords: loaded.skills }),
42
+ ...(names.length === 0 ? {} : skillsDeclaration(names)),
30
43
  ...(Object.keys(loaded.mcpServers).length === 0
31
44
  ? {}
32
45
  : { mcpServers: loaded.mcpServers }),
@@ -0,0 +1,17 @@
1
+ /** Optional public reads shared by Studio, CLI and custom clients. */
2
+ import { type SessionListItem, type SessionPage } from "@nylorun/core/contracts";
3
+ import type { Transport } from "./http.js";
4
+ export type SessionPageOptions = {
5
+ agentId?: string;
6
+ status?: SessionListItem["status"];
7
+ sandboxId?: string;
8
+ ownerUserId?: string;
9
+ limit?: number;
10
+ cursor?: string;
11
+ signal?: AbortSignal;
12
+ };
13
+ export declare class SessionsReadClient {
14
+ private readonly transport;
15
+ constructor(transport: Transport);
16
+ page(options?: SessionPageOptions): Promise<SessionPage>;
17
+ }
package/dist/reads.js ADDED
@@ -0,0 +1,17 @@
1
+ /** Optional public reads shared by Studio, CLI and custom clients. */
2
+ import { SessionPageSchema, } from "@nylorun/core/contracts";
3
+ export class SessionsReadClient {
4
+ transport;
5
+ constructor(transport) {
6
+ this.transport = transport;
7
+ }
8
+ async page(options = {}) {
9
+ await this.transport.requireFeature("session-reads", options.signal);
10
+ const { signal, ...filters } = options;
11
+ const query = new URLSearchParams({ limit: String(options.limit ?? 50) });
12
+ for (const [key, value] of Object.entries(filters))
13
+ if (value !== undefined)
14
+ query.set(key, String(value));
15
+ return SessionPageSchema.parse(await this.transport.json(`/v1/sessions?${query}`, "GET", undefined, signal));
16
+ }
17
+ }
@@ -58,6 +58,35 @@ export declare class SandboxesClient {
58
58
  labels?: Readonly<Record<string, string>>;
59
59
  signal?: AbortSignal;
60
60
  }): Promise<SandboxView[]>;
61
+ page(options?: {
62
+ labels?: Readonly<Record<string, string>>;
63
+ limit?: number;
64
+ cursor?: string;
65
+ signal?: AbortSignal;
66
+ }): Promise<{
67
+ sandboxes: {
68
+ id: string;
69
+ kind: "pod" | "virtual";
70
+ labels: Record<string, string>;
71
+ spec: Record<string, unknown>;
72
+ state: "creating" | "ready" | "running" | "stopped";
73
+ pod?: {
74
+ desired: "deleted" | "running" | "suspended";
75
+ observed: "creating" | "deleting" | "expired" | "failed" | "lost" | "running" | "suspended";
76
+ volumeGeneration: number;
77
+ hostEpoch: number;
78
+ expiresAt?: string | undefined;
79
+ reason?: string | undefined;
80
+ } | undefined;
81
+ sessions: {
82
+ id: string;
83
+ activeTurnId: string | null;
84
+ }[];
85
+ createdAt: string;
86
+ updatedAt: string;
87
+ }[];
88
+ nextCursor: string | null;
89
+ }>;
61
90
  /** Deletes the sandbox and its files; `deleted` is false when there was none. */
62
91
  delete(sandboxId: string, options?: {
63
92
  signal?: AbortSignal;
package/dist/sandboxes.js CHANGED
@@ -12,7 +12,7 @@
12
12
  *
13
13
  * Ids may hold `/`; they are sent percent-encoded as one path segment.
14
14
  */
15
- import { isSandboxId, } from "@nylorun/core/contracts";
15
+ import { SandboxPageSchema, isSandboxId, } from "@nylorun/core/contracts";
16
16
  import { id, segment } from "./http.js";
17
17
  const FEATURE = "sandboxes";
18
18
  export class SandboxesClient {
@@ -53,6 +53,15 @@ export class SandboxesClient {
53
53
  const reply = await this.transport.json(`/v1/sandboxes${search ? `?${search}` : ""}`, "GET", undefined, options.signal);
54
54
  return reply.sandboxes;
55
55
  }
56
+ async page(options = {}) {
57
+ await this.transport.requireFeature("session-reads", options.signal);
58
+ const query = new URLSearchParams({ limit: String(options.limit ?? 50) });
59
+ if (options.cursor !== undefined)
60
+ query.set("cursor", options.cursor);
61
+ for (const [key, value] of Object.entries(options.labels ?? {}))
62
+ query.append("label", `${key}=${value}`);
63
+ return SandboxPageSchema.parse(await this.transport.json(`/v1/sandboxes?${query}`, "GET", undefined, options.signal));
64
+ }
56
65
  /** Deletes the sandbox and its files; `deleted` is false when there was none. */
57
66
  async delete(sandboxId, options = {}) {
58
67
  const path = this.path(sandboxId);