@nylorun/agents 0.23.0-beta → 0.25.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.
@@ -6,21 +6,25 @@
6
6
  * work. The browser never sees the Tenant key, the Runtime's URL or the Tenant id.
7
7
  *
8
8
  * The same threads are reachable directly from a browser with a trusted issuer's token (through
9
- * the operator's proxy): each thread is one session per subject, agent and thread on both paths.
9
+ * the operator's proxy): each thread is one session per subject, Agent and thread on both paths.
10
+ * The path's agent is an Agent id (`client.agents`, protocol 16): the thread's session runs on
11
+ * that Agent, with its sandbox and its vault's credentials.
10
12
  */
11
13
  import { type SubjectScope } from "@nylorun/core/contracts";
12
- import type { BuiltWorkflow } from "@nylorun/core/define";
13
- import { type AgentSource, type AgentsClient } from "../client.js";
14
- /** Per-session parameters the host may add, such as the person's Environment. */
14
+ import { type AgentsClient } from "../client.js";
15
+ /** Per-session parameters the host may add. */
15
16
  export interface AgUiSessionOptions {
16
17
  /** Set when the thread's session is created, on its first run; later runs keep them. */
17
18
  info?: Record<string, unknown>;
18
- /** The Environment the thread's session runs in: its sandbox and its vault's credentials. */
19
- environment?: string;
20
19
  }
21
20
  export interface AgUiHandlerOptions {
22
- /** Agents this endpoint may run: built agents, workflows or their ids. Nothing else is reachable. */
23
- agents: readonly (AgentSource | BuiltWorkflow | string)[];
21
+ /**
22
+ * The Agents this endpoint may run, by id (`client.agents`), or as `client.agents.get` answers
23
+ * them. Nothing else is reachable.
24
+ */
25
+ agents: readonly (string | {
26
+ readonly id: string;
27
+ })[];
24
28
  /** The signed-in person the request is for. `undefined` answers 401; there is no anonymous mode. */
25
29
  subject(request: Request): string | undefined | Promise<string | undefined>;
26
30
  /** Where `fetch` is mounted, e.g. `/api/agui`. Default `/`. */
@@ -29,7 +33,7 @@ export interface AgUiHandlerOptions {
29
33
  client?: AgentsClient | Promise<AgentsClient>;
30
34
  /** What each subject may do through this endpoint. Default `["sessions:own"]`. */
31
35
  scopes?: readonly SubjectScope[];
32
- /** Optional per-session parameters, e.g. the person's Environment with their keys. */
36
+ /** Optional per-session parameters: the `info` of the thread's session. */
33
37
  session?(subject: string, agentId: string): AgUiSessionOptions | Promise<AgUiSessionOptions>;
34
38
  }
35
39
  export interface AgUiHandler {
@@ -6,10 +6,12 @@
6
6
  * work. The browser never sees the Tenant key, the Runtime's URL or the Tenant id.
7
7
  *
8
8
  * The same threads are reachable directly from a browser with a trusted issuer's token (through
9
- * the operator's proxy): each thread is one session per subject, agent and thread on both paths.
9
+ * the operator's proxy): each thread is one session per subject, Agent and thread on both paths.
10
+ * The path's agent is an Agent id (`client.agents`, protocol 16): the thread's session runs on
11
+ * that Agent, with its sandbox and its vault's credentials.
10
12
  */
11
13
  import { parseSubjectHeaders } from "@nylorun/core/contracts";
12
- import { createClient, } from "../client.js";
14
+ import { createClient } from "../client.js";
13
15
  import { IncompatibleRuntimeError } from "../http.js";
14
16
  /** The Host feature the handler forwards to. */
15
17
  const REQUIRED_FEATURES = ["ag-ui-endpoint"];
@@ -119,9 +121,10 @@ export function createAgUiHandler(options) {
119
121
  ? { ...body.forwardedProps }
120
122
  : {};
121
123
  delete forwarded.nylorun;
122
- const extra = (await options.session?.(subject, agentId)) ?? {};
123
- if (Object.keys(extra).length > 0)
124
- forwarded.nylorun = { session: extra };
124
+ // The session's Agent is the path's; only its `info` is the host's to add.
125
+ const { info } = (await options.session?.(subject, agentId)) ?? {};
126
+ if (info !== undefined)
127
+ forwarded.nylorun = { session: { info } };
125
128
  return await forward(request, subject, `/${segment(agentId)}`, {
126
129
  method: "POST",
127
130
  headers: { accept: "text/event-stream" },
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Agent manifests (protocol 16): `client.agentManifests`. An agent manifest (v8) is one
3
+ * named, versioned document with what runs inside the agent loop (`harness`: the harness's id,
4
+ * instructions, tools, skills, MCP servers, plugins, output schema, the harness's own `loop`
5
+ * settings, memory; or a flow, `kind: "workflow"`) and what the Runtime provides around it
6
+ * (`runtime`: kind, image, network, setup, files, limits, secret slots). The Runtime checks the
7
+ * two against each other when it is saved (`422 manifest_inconsistent`); an Agent
8
+ * (`client.agents`) is made from one version.
9
+ *
10
+ * `save` takes the document, what `composeManifest` read from a folder, or a built agent or flow
11
+ * with the runtime section to run it in: the SDK makes the harness section from the agent's
12
+ * manifest (`harnessSectionOf`), with its skills' `secrets`, `loop` and `memory`. It first
13
+ * uploads the files the manifest names by hash that the Runtime does not hold (skills, a flow's
14
+ * agents' skills, runtime files), and refuses before sending a tool that runs your code or an
15
+ * `approval`, as the Runtime runs agents from their manifests alone.
16
+ */
17
+ import { AGENT_MANIFEST_V8_SCHEMA_VERSION, type AgentManifestPlan, type AgentManifestV8, type AgentMemorySettings, type AgentManifestVersions, type AgentManifestView, type DeleteAgentManifestResponse, type RuntimeSection, type SaveAgentManifestResponse } from "@nylorun/core/contracts";
18
+ import { type AgentManifest, type BuiltAgent, type BuiltWorkflow, type JsonObject, type SkillFileSource, type WorkflowManifest } from "@nylorun/core/define";
19
+ import { type Transport } from "./http.js";
20
+ /** A built agent or flow, or anything with its id and manifest. */
21
+ export interface AgentSource {
22
+ readonly id: string;
23
+ readonly manifest: AgentManifest | WorkflowManifest;
24
+ build?(): BuiltAgent | BuiltWorkflow;
25
+ getBinding?: BuiltAgent["getBinding"] | BuiltWorkflow["getBinding"];
26
+ }
27
+ /** An agent manifest v8 as authored: `manifestSchemaVersion` may be left out, and `$schema` is dropped. */
28
+ export type AgentManifestDocument = Omit<AgentManifestV8, "manifestSchemaVersion"> & {
29
+ readonly manifestSchemaVersion?: typeof AGENT_MANIFEST_V8_SCHEMA_VERSION;
30
+ readonly $schema?: string;
31
+ };
32
+ /** An agent manifest made from a built agent or flow (as `saveAgent` took it before protocol 16). */
33
+ export interface AgentManifestFromAgent {
34
+ readonly agent: AgentSource | BuiltWorkflow;
35
+ /** The manifest's name. Default: the agent's id. */
36
+ readonly name?: string;
37
+ /** Default: the agent's description. */
38
+ readonly description?: string;
39
+ /** Default: the agent's metadata. */
40
+ readonly metadata?: JsonObject;
41
+ /** The harness that runs the loop (`harness.id`). Default ours, `nylorun`. */
42
+ readonly harness?: string;
43
+ /** What the Runtime provides around the loop. Without it, an Agent made from it is kind `none`. */
44
+ readonly runtime?: RuntimeSection;
45
+ /** The harness's own settings (`harness.loop`), passed to it in `turn.start`. */
46
+ readonly loop?: JsonObject;
47
+ /**
48
+ * Turns on the Agent's memory (`harness.memory`): `access` `read_write` (the default) or
49
+ * `read_only`, and `instructions` on what to remember. An agent's only, not a flow's.
50
+ */
51
+ readonly memory?: AgentMemorySettings;
52
+ }
53
+ /** What `composeManifest` read from a folder: the document, the bytes of the files it names, its gaps. */
54
+ export interface ComposedManifest {
55
+ readonly manifest: AgentManifestV8;
56
+ /** Each file the manifest names by hash (its skills', its runtime's), by `sha256:<hex>`. */
57
+ readonly files: ReadonlyMap<string, SkillFileSource>;
58
+ /**
59
+ * Why the Runtime would refuse it (`sectionGaps`), and where it misses what the harness file's
60
+ * `requires` asks of its runtime, which only the composer checks.
61
+ */
62
+ readonly gaps: readonly string[];
63
+ /** Skills and servers the folders hold that were skipped, and why. */
64
+ readonly warnings: readonly string[];
65
+ }
66
+ export type AgentManifestInput = AgentManifestDocument | AgentManifestFromAgent | ComposedManifest;
67
+ /** File bytes to upload before a save, by `sha256:<hex>` (`runtimeFiles(...).bytes`, or sources). */
68
+ export type DefinitionFiles = ReadonlyMap<string, SkillFileSource> | Readonly<Record<string, Uint8Array>>;
69
+ export interface AgentManifestSaveOptions {
70
+ /** The version you read (`0` for none yet): a save over a newer one is `412 manifest_changed`. */
71
+ ifMatch?: number;
72
+ requestId?: string;
73
+ /** More files to upload when the Runtime lacks them (a document's runtime files by hash). */
74
+ files?: DefinitionFiles;
75
+ signal?: AbortSignal;
76
+ }
77
+ /** A manifest the SDK refuses before sending: its sections do not fit, or it misses its `requires`. */
78
+ export declare class ManifestGapsError extends Error {
79
+ /** The manifest's name. */
80
+ readonly manifest: string;
81
+ readonly gaps: readonly string[];
82
+ constructor(
83
+ /** The manifest's name. */
84
+ manifest: string, gaps: readonly string[]);
85
+ }
86
+ /**
87
+ * Where a runtime section misses what a harness `requires` (agent manifest v7's, and
88
+ * `harness.manifest.json`'s): `sandbox: "any"` needs kind `virtual` or `pod`, `"pod"` needs a
89
+ * pod, and each of `secrets` must be a declared slot. The API never sees `requires`, so the SDK
90
+ * and the CLI check it while they compose.
91
+ */
92
+ export declare function requirementGaps(requires: AgentManifest["requires"] | undefined, runtime: RuntimeSection | undefined): string[];
93
+ /**
94
+ * The agent manifest v8 document `input` describes, checked as the Runtime checks its schema:
95
+ * a document with `manifestSchemaVersion` filled in and `$schema` dropped, or a built agent's
96
+ * or flow's harness section with the name, runtime section and loop settings given.
97
+ */
98
+ export declare function agentManifest(input: AgentManifestInput): AgentManifestV8;
99
+ export declare class AgentManifestsClient {
100
+ private readonly transport;
101
+ constructor(transport: Transport);
102
+ private path;
103
+ private send;
104
+ /**
105
+ * Saves the manifest's next version (`PUT /v1/agent-manifests/{name}`), or answers the latest
106
+ * unchanged; every Agent that tracks the manifest moves to it. Refused before anything is sent when it
107
+ * misses its `requires` or, composed, its sections do not fit (`ManifestGapsError`).
108
+ */
109
+ save(input: AgentManifestInput, options?: AgentManifestSaveOptions): Promise<SaveAgentManifestResponse>;
110
+ /**
111
+ * What saving the manifest would do (`?dryRun=true`): the version it would answer, each change
112
+ * by section and path, warnings, errors, and the Agents on older versions. The gaps the SDK
113
+ * finds itself are among `errors`. Its files are uploaded, since they are stored by content.
114
+ */
115
+ plan(input: AgentManifestInput, options?: Omit<AgentManifestSaveOptions, "requestId">): Promise<AgentManifestPlan>;
116
+ /** The latest version of manifest `name`, or version `version`. */
117
+ get(name: string, version?: number, options?: {
118
+ signal?: AbortSignal;
119
+ }): Promise<AgentManifestView>;
120
+ /** Each manifest's latest version. */
121
+ list(options?: {
122
+ signal?: AbortSignal;
123
+ }): Promise<AgentManifestView[]>;
124
+ /** Every version of manifest `name`, newest first, with how many Agents were made from each. */
125
+ versions(name: string, options?: {
126
+ signal?: AbortSignal;
127
+ }): Promise<AgentManifestVersions>;
128
+ /** Deletes every version; refused while an Agent was made from one (`409 manifest_in_use`). */
129
+ delete(name: string, options?: {
130
+ signal?: AbortSignal;
131
+ }): Promise<DeleteAgentManifestResponse>;
132
+ }
@@ -0,0 +1,229 @@
1
+ /**
2
+ * Agent manifests (protocol 16): `client.agentManifests`. An agent manifest (v8) is one
3
+ * named, versioned document with what runs inside the agent loop (`harness`: the harness's id,
4
+ * instructions, tools, skills, MCP servers, plugins, output schema, the harness's own `loop`
5
+ * settings, memory; or a flow, `kind: "workflow"`) and what the Runtime provides around it
6
+ * (`runtime`: kind, image, network, setup, files, limits, secret slots). The Runtime checks the
7
+ * two against each other when it is saved (`422 manifest_inconsistent`); an Agent
8
+ * (`client.agents`) is made from one version.
9
+ *
10
+ * `save` takes the document, what `composeManifest` read from a folder, or a built agent or flow
11
+ * with the runtime section to run it in: the SDK makes the harness section from the agent's
12
+ * manifest (`harnessSectionOf`), with its skills' `secrets`, `loop` and `memory`. It first
13
+ * uploads the files the manifest names by hash that the Runtime does not hold (skills, a flow's
14
+ * agents' skills, runtime files), and refuses before sending a tool that runs your code or an
15
+ * `approval`, as the Runtime runs agents from their manifests alone.
16
+ */
17
+ import { AGENT_MANIFEST_V8_SCHEMA_VERSION, AgentManifestV8Schema, isAgentManifestName, } from "@nylorun/core/contracts";
18
+ import { approvalRefusal, codeToolRefusal, delegateOf, harnessSectionOf, isBuiltWorkflow, loopManifestOf, } from "@nylorun/core/define";
19
+ import { FilesClient } from "./files.js";
20
+ import { id, segment } from "./http.js";
21
+ /** A manifest the SDK refuses before sending: its sections do not fit, or it misses its `requires`. */
22
+ export class ManifestGapsError extends Error {
23
+ manifest;
24
+ gaps;
25
+ constructor(
26
+ /** The manifest's name. */
27
+ manifest, gaps) {
28
+ super(`Agent manifest ${manifest} does not fit its runtime:\n${gaps.map((gap) => ` - ${gap}`).join("\n")}`);
29
+ this.manifest = manifest;
30
+ this.gaps = gaps;
31
+ this.name = "ManifestGapsError";
32
+ }
33
+ }
34
+ /** An agent's binding, with its parts; none for a flow. */
35
+ function agentBindingOf(agent) {
36
+ if (isBuiltWorkflow(agent) || agent.manifest.kind === "workflow")
37
+ return undefined;
38
+ const built = agent.build?.() ?? agent;
39
+ if (isBuiltWorkflow(built))
40
+ return undefined;
41
+ const binding = built.getBinding?.();
42
+ return binding && "parts" in binding ? binding : undefined;
43
+ }
44
+ /** The agent's declarations, then those of each agent it uses as a tool. */
45
+ function declarationsOf(agent) {
46
+ const binding = agentBindingOf(agent);
47
+ if (!binding)
48
+ return [];
49
+ const found = [...(binding.parts ?? [])];
50
+ for (const tool of binding.tools ?? []) {
51
+ const delegate = delegateOf(tool);
52
+ found.push(...(delegate?.agent?.getBinding().parts ?? []));
53
+ // A flow agent used as a tool: its agents' declarations.
54
+ for (const leaf of Object.values(delegate?.workflow?.getBinding().agents ?? {}))
55
+ found.push(...(leaf.parts ?? []));
56
+ }
57
+ return found;
58
+ }
59
+ /** The bytes of every skill file the agent's definition names, by `sha256:<hex>`. */
60
+ function skillFilesOf(agent, into = new Map()) {
61
+ if (isBuiltWorkflow(agent)) {
62
+ // A flow agent embeds its agents: one document, carrying their files.
63
+ for (const binding of Object.values(agent.getBinding().agents))
64
+ skillFilesOf({ id: binding.manifest.id, manifest: binding.manifest, getBinding: () => binding }, into);
65
+ return into;
66
+ }
67
+ for (const item of declarationsOf(agent))
68
+ for (const [sha256, source] of Object.entries(item.skillFiles ?? {}))
69
+ into.set(sha256, source);
70
+ return into;
71
+ }
72
+ /**
73
+ * The harness section's skills with the secret slots each reads (a SKILL.md's `secrets`, which
74
+ * `.skills()` and `.plugin()` keep on the agent's parts, outside its v7 manifest).
75
+ */
76
+ function withSkillSecrets(agent, skills) {
77
+ const secrets = Object.assign({}, ...(agentBindingOf(agent)?.parts ?? []).map((part) => part.skillSecrets ?? {}));
78
+ if (skills === undefined || Object.keys(secrets).length === 0)
79
+ return skills;
80
+ return Object.fromEntries(Object.entries(skills).map(([key, skill]) => [key, secrets[key] ? { ...skill, secrets: [...secrets[key]] } : skill]));
81
+ }
82
+ const quoted = (value) => JSON.stringify(value);
83
+ /**
84
+ * Where a runtime section misses what a harness `requires` (agent manifest v7's, and
85
+ * `harness.manifest.json`'s): `sandbox: "any"` needs kind `virtual` or `pod`, `"pod"` needs a
86
+ * pod, and each of `secrets` must be a declared slot. The API never sees `requires`, so the SDK
87
+ * and the CLI check it while they compose.
88
+ */
89
+ export function requirementGaps(requires, runtime) {
90
+ const gaps = [];
91
+ if (!requires)
92
+ return gaps;
93
+ const kind = runtime?.kind ?? "none";
94
+ const kindText = runtime === undefined ? "the manifest has no runtime section" : `the runtime is ${quoted(kind)}`;
95
+ if (requires.sandbox === "any" && kind !== "virtual" && kind !== "pod")
96
+ gaps.push(`requires.sandbox "any" needs runtime.kind "virtual" or "pod"; ${kindText}`);
97
+ if (requires.sandbox === "pod" && kind !== "pod")
98
+ gaps.push(`requires.sandbox "pod" needs runtime.kind "pod"; ${kindText}`);
99
+ for (const name of requires.secrets ?? [])
100
+ if (runtime?.secrets?.[name] === undefined)
101
+ gaps.push(`requires.secrets ${quoted(name)} is not a secret slot in runtime.secrets`);
102
+ return gaps;
103
+ }
104
+ /** `value` as agent manifest v8, or a `TypeError` naming every issue. */
105
+ function parseManifest(value) {
106
+ const parsed = AgentManifestV8Schema.safeParse(value);
107
+ if (parsed.success)
108
+ return parsed.data;
109
+ const name = value?.name;
110
+ const issues = parsed.error.issues.map((issue) => ` ${issue.path.length ? `${issue.path.map(String).join(".")}: ` : ""}${issue.message}`);
111
+ throw new TypeError(`Agent manifest${typeof name === "string" ? ` ${name}` : ""} is not valid:\n${issues.join("\n")}`);
112
+ }
113
+ function refuse(loop) {
114
+ const refusal = codeToolRefusal(loop) ?? approvalRefusal(loop);
115
+ if (refusal)
116
+ throw new Error(refusal);
117
+ }
118
+ /**
119
+ * The agent manifest v8 document `input` describes, checked as the Runtime checks its schema:
120
+ * a document with `manifestSchemaVersion` filled in and `$schema` dropped, or a built agent's
121
+ * or flow's harness section with the name, runtime section and loop settings given.
122
+ */
123
+ export function agentManifest(input) {
124
+ return prepare(input).manifest;
125
+ }
126
+ function prepare(input) {
127
+ if ("agent" in input) {
128
+ const { agent } = input;
129
+ const loop = agent.manifest;
130
+ refuse(loop);
131
+ // `harnessSectionOf` leaves out the loop manifest's identity and its `requires`, checked here.
132
+ const section = harnessSectionOf(loop, input.harness === undefined ? {} : { harness: input.harness });
133
+ if (input.memory !== undefined && "kind" in section)
134
+ throw new TypeError(`memory turns on an agent's memory; ${agent.id} is a flow, whose harness section has none`);
135
+ const skills = "kind" in section ? undefined : withSkillSecrets(agent, section.skills);
136
+ const harness = {
137
+ ...section,
138
+ ...(skills === undefined ? {} : { skills }),
139
+ ...(input.loop === undefined ? {} : { loop: input.loop }),
140
+ ...(input.memory === undefined ? {} : { memory: input.memory }),
141
+ };
142
+ const description = input.description ?? loop.description;
143
+ const metadata = input.metadata ?? loop.metadata;
144
+ const manifest = parseManifest({
145
+ manifestSchemaVersion: AGENT_MANIFEST_V8_SCHEMA_VERSION,
146
+ name: input.name ?? agent.id,
147
+ ...(description === undefined ? {} : { description }),
148
+ ...(metadata === undefined ? {} : { metadata }),
149
+ harness,
150
+ ...(input.runtime === undefined ? {} : { runtime: input.runtime }),
151
+ });
152
+ const requires = "kind" in loop ? undefined : loop.requires;
153
+ return { manifest, files: skillFilesOf(agent), gaps: requirementGaps(requires, manifest.runtime) };
154
+ }
155
+ if ("files" in input && "gaps" in input) {
156
+ const manifest = parseManifest(input.manifest);
157
+ refuse(loopManifestOf(manifest));
158
+ return { manifest, files: new Map(input.files), gaps: [...input.gaps] };
159
+ }
160
+ const { $schema: _, ...document } = input;
161
+ const manifest = parseManifest({ manifestSchemaVersion: AGENT_MANIFEST_V8_SCHEMA_VERSION, ...document });
162
+ refuse(loopManifestOf(manifest));
163
+ return { manifest, files: new Map(), gaps: [] };
164
+ }
165
+ export class AgentManifestsClient {
166
+ transport;
167
+ constructor(transport) {
168
+ this.transport = transport;
169
+ }
170
+ path(name) {
171
+ if (!isAgentManifestName(name))
172
+ throw new TypeError(`${name} is not an agent manifest's name: 1-64 letters, digits, '.', '_' and '-'`);
173
+ return `/v1/agent-manifests/${segment(name)}`;
174
+ }
175
+ async send(prepared, query, options) {
176
+ const { ifMatch, signal } = options;
177
+ const path = this.path(prepared.manifest.name);
178
+ const files = new Map(prepared.files);
179
+ if (options.files instanceof Map)
180
+ for (const [sha256, source] of options.files)
181
+ files.set(sha256, source);
182
+ else
183
+ for (const [sha256, bytes] of Object.entries(options.files ?? {}))
184
+ files.set(sha256, { size: bytes.byteLength, read: async () => bytes });
185
+ // Its files by hash: uploaded first, each only when the Runtime does not hold it.
186
+ await new FilesClient(this.transport).ensure(files, signal ? { signal } : {});
187
+ const transport = ifMatch === undefined ? this.transport : this.transport.withHeaders({ "If-Match": String(ifMatch) });
188
+ return transport.json(`${path}${query}`, "PUT", { requestId: options.requestId ?? id(), ...prepared.manifest }, signal);
189
+ }
190
+ /**
191
+ * Saves the manifest's next version (`PUT /v1/agent-manifests/{name}`), or answers the latest
192
+ * unchanged; every Agent that tracks the manifest moves to it. Refused before anything is sent when it
193
+ * misses its `requires` or, composed, its sections do not fit (`ManifestGapsError`).
194
+ */
195
+ async save(input, options = {}) {
196
+ const prepared = prepare(input);
197
+ if (prepared.gaps.length > 0)
198
+ throw new ManifestGapsError(prepared.manifest.name, prepared.gaps);
199
+ return this.send(prepared, "", options);
200
+ }
201
+ /**
202
+ * What saving the manifest would do (`?dryRun=true`): the version it would answer, each change
203
+ * by section and path, warnings, errors, and the Agents on older versions. The gaps the SDK
204
+ * finds itself are among `errors`. Its files are uploaded, since they are stored by content.
205
+ */
206
+ async plan(input, options = {}) {
207
+ const prepared = prepare(input);
208
+ const plan = await this.send(prepared, "?dryRun=true", options);
209
+ const errors = [...plan.errors, ...prepared.gaps.filter((gap) => !plan.errors.includes(gap))];
210
+ return { ...plan, errors };
211
+ }
212
+ /** The latest version of manifest `name`, or version `version`. */
213
+ async get(name, version, options = {}) {
214
+ return this.transport.json(`${this.path(name)}${version === undefined ? "" : `/versions/${version}`}`, "GET", undefined, options.signal);
215
+ }
216
+ /** Each manifest's latest version. */
217
+ async list(options = {}) {
218
+ const reply = await this.transport.json("/v1/agent-manifests", "GET", undefined, options.signal);
219
+ return reply.manifests;
220
+ }
221
+ /** Every version of manifest `name`, newest first, with how many Agents were made from each. */
222
+ async versions(name, options = {}) {
223
+ return this.transport.json(`${this.path(name)}/versions`, "GET", undefined, options.signal);
224
+ }
225
+ /** Deletes every version; refused while an Agent was made from one (`409 manifest_in_use`). */
226
+ async delete(name, options = {}) {
227
+ return this.transport.json(this.path(name), "DELETE", undefined, options.signal);
228
+ }
229
+ }