@nylorun/agents 0.25.0-beta → 0.27.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.
- package/CHANGELOG.md +27 -0
- package/README.md +15 -2
- package/dist/agent-manifests.d.ts +24 -1
- package/dist/agent-manifests.js +54 -1
- package/dist/folder-check.d.ts +14 -0
- package/dist/folder-check.js +20 -0
- package/dist/folder-pack.d.ts +5 -0
- package/dist/folder-pack.js +54 -0
- package/dist/folder.d.ts +38 -0
- package/dist/folder.js +30 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/session-client.d.ts +2 -0
- package/dist/session-client.js +1 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# @nylorun/agents
|
|
2
2
|
|
|
3
|
+
## 0.27.0-beta
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- aaa079f: Agent folders in the SDK and the CLI (track T6). `@nylorun/agents` adds `packFolder(dir)`, which lists a folder's files with their sizes and hashes (skipping `.git` and `node_modules`, following no symbolic link), `checkFolder`, which compiles it offline with the Runtime's compiler, and `client.agentManifests.saveFolder` and `planFolder`, which upload the files the Runtime lacks and send the folder by hash; `source` and `sourceFile` read a version's folder back. `nylorun init <name>` writes a new folder's `AGENT.md`, `nylorun check [dir…]` prints every problem by file, line and column with no Runtime, and `nylorun apply` saves a folder with `AGENT.md` (`--dry-run` prints the compiled manifest). A folder that does not compile sends nothing.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Pin core to the tested release.
|
|
12
|
+
- Updated dependencies [9ba6634]
|
|
13
|
+
- Updated dependencies [aaa079f]
|
|
14
|
+
- @nylorun/core@0.27.0-beta
|
|
15
|
+
|
|
16
|
+
## 0.26.0-beta
|
|
17
|
+
|
|
18
|
+
### Minor Changes
|
|
19
|
+
|
|
20
|
+
- ab7c08a: A gateway's tool prefix (#494, Host feature `gateway-credentials`). A `bearer` or `headers` credential with `via`, written to an Agent's vault, may add `toolPrefix`: what the gateway at `via` puts before each tool name it lists, such as LiteLLM's `github-`. The Runtime lists the server's tools without it, so tool names (`github__create_issue`), a manifest's `tools` settings and tool policy rules name the server's own tools whether or not a gateway is in between, and calls each tool by the name the gateway listed. A listed tool that lacks the prefix keeps its name, and the server's `mcp.discovered` message names it. `toolPrefix` needs `via`, and secret views carry it. It is the installation's routing, never a manifest's: a manifest's secret slot cannot name it, and a slot's value write (`{ headers, toolPrefix }`) adds it. A fork keeps it while the new slot keeps `via`. No protocol change.
|
|
21
|
+
|
|
22
|
+
### Patch Changes
|
|
23
|
+
|
|
24
|
+
- Pin core to the tested release.
|
|
25
|
+
- Updated dependencies [fce0c9b]
|
|
26
|
+
- Updated dependencies [ab7c08a]
|
|
27
|
+
- Updated dependencies [4a563e7]
|
|
28
|
+
- @nylorun/core@0.26.0-beta
|
|
29
|
+
|
|
3
30
|
## 0.25.0-beta
|
|
4
31
|
|
|
5
32
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -272,7 +272,7 @@ Each key names a server; its `name` defaults to the key and, when given, must eq
|
|
|
272
272
|
|
|
273
273
|
Set a server's tools one by one with `tools`, keyed by the server's own tool names, `"*"` for the rest: `{ "*": { enabled: false }, search_issues: { enabled: true }, create_issue: { enabled: true } }` is an allowlist (whether a call waits for a person is the [tool policy](#tool-policy)). When an agent's MCP tools would fill more than a tenth of the model's context window, the Runtime defers them: the model finds them with `tool_search` and runs them with `tool_call`. `deferred: true` or `false`, on a tool or on the server, decides instead.
|
|
274
274
|
|
|
275
|
-
A manifest never carries a server's credential, and its `headers` hold no secret. A server may name the runtime section's secret slot it authenticates with (`credential`: a `bearer` or `headers` slot bound to its URL, checked when the manifest is saved), and you write the credential into each Agent's vault (a token or a header map, or a gateway's key with `via` and an identity header for servers that need each person's sign-in, with `client.agents.secrets.put`); the sessions on that Agent use it: [DEPLOYMENT.md](../../guides/DEPLOYMENT.md#mcp-servers-and-http-tools), which also shows `nylorun tenant mcp inspect` for listing a server's tools first. `admin.coverage({ agentId })` in `@nylorun/admin` says which credential each server and HTTP tool of an Agent's manifest version would get from its vault ([Checking an agent's credentials](../../guides/DEPLOYMENT.md#checking-an-agents-credentials)). The model knows each tool as `server__tool`, with characters outside `[A-Za-z0-9_-]` replaced by `_`; a failed call reaches it with a code (`credential_rejected` on a `401`, `mcp.unreachable`, …), and a result past 32 KiB, or an image, becomes an artifact of the session that it reads with `read_artifact`.
|
|
275
|
+
A manifest never carries a server's credential, and its `headers` hold no secret. A server may name the runtime section's secret slot it authenticates with (`credential`: a `bearer` or `headers` slot bound to its URL, checked when the manifest is saved), and you write the credential into each Agent's vault (a token or a header map, or a gateway's key with `via`, its tool prefix (`toolPrefix`) and an identity header for servers that need each person's sign-in, with `client.agents.secrets.put`); the sessions on that Agent use it: [DEPLOYMENT.md](../../guides/DEPLOYMENT.md#mcp-servers-and-http-tools), which also shows `nylorun tenant mcp inspect` for listing a server's tools first. `admin.coverage({ agentId })` in `@nylorun/admin` says which credential each server and HTTP tool of an Agent's manifest version would get from its vault ([Checking an agent's credentials](../../guides/DEPLOYMENT.md#checking-an-agents-credentials)). The model knows each tool as `server__tool`, with characters outside `[A-Za-z0-9_-]` replaced by `_`; a failed call reaches it with a code (`credential_rejected` on a `401`, `mcp.unreachable`, …), and a result past 32 KiB, or an image, becomes an artifact of the session that it reads with `read_artifact`.
|
|
276
276
|
|
|
277
277
|
## Agent manifests and Agents
|
|
278
278
|
|
|
@@ -303,6 +303,19 @@ const session = await client.createSession({ id: "alice-q3", agent: "users/alice
|
|
|
303
303
|
|
|
304
304
|
`agentManifests.save(input, { ifMatch, requestId })` and `plan(input, { ifMatch })` (a dry run that saves nothing) take a v8 document, what `composeManifest` read, or `{ agent, runtime?, harness?, loop?, memory?, name?, description?, metadata? }`: the SDK makes the harness section from the built agent or flow (`harnessSectionOf`, with its skills' `secrets`; `harness` is the harness's id; `memory`, `{ access?, instructions? }`, turns on an agent's memory, not a flow's) and names the manifest after the agent's id. First it uploads every file the manifest names by hash that the Runtime does not hold: the agent's skills, a flow's agents' skills, and the runtime's files (`runtimeFiles(path, contents)` hashes a `files` entry; pass its `bytes` as `files`). `If-Match` is the version you read (`0` for a new manifest). A save that changes nothing answers the latest version, `unchanged`. `get(name, version?)`, `list()`, `versions(name)` (with the Agents made from each) and `delete(name)` (refused while an Agent was made from it: `409 manifest_in_use`) read and remove them.
|
|
305
305
|
|
|
306
|
+
### Agent folders: `packFolder`, `checkFolder`, `saveFolder`
|
|
307
|
+
|
|
308
|
+
An agent can be a folder of Markdown and JSON (track T6): `AGENT.md` with `name`, `description` and the instructions, and beside it `mcp.json`, `runtime.json`, `skills/`, `tools/`, `triggers/` and `files/` as it needs them. `packFolder(dir)` (Node only) lists every file with its size and SHA-256, skipping `.git` and `node_modules`; `checkFolder(folder)` compiles it offline with the Runtime's compiler (`@nylorun/core/folder`) and returns `{ manifest?, diagnostics, compiler }`, each diagnostic with its file, line and column (`diagnosticLine` formats one). `agentManifests.saveFolder(folder, { ifMatch })` and `planFolder` compile it first, refuse one that does not compile (`FolderInvalidError`), upload the files the Runtime lacks and send the folder by hash; the Runtime keeps it as the version's source, which `source(name, version)` and `sourceFile(name, version, path)` read back.
|
|
309
|
+
|
|
310
|
+
```ts
|
|
311
|
+
import { createClient, packFolder } from "@nylorun/agents";
|
|
312
|
+
|
|
313
|
+
const client = await createClient();
|
|
314
|
+
const plan = await client.agentManifests.planFolder(await packFolder("agents/triage"));
|
|
315
|
+
console.log(plan.manifest, plan.diagnostics); // the compiled manifest, and e.g. the derived GITHUB_TOKEN slot
|
|
316
|
+
await client.agentManifests.saveFolder(await packFolder("agents/triage"), { ifMatch: plan.from ?? 0 });
|
|
317
|
+
```
|
|
318
|
+
|
|
306
319
|
### Three files: `composeManifest`
|
|
307
320
|
|
|
308
321
|
A manifest can also be authored as files, so a runtime is shared by many agents and skills stay folders. `composeManifest(dir)` (Node only) reads `<dir>/agent.manifest.json` and the files it names, each path relative to the file that names it, and returns `{ manifest, files, gaps, warnings }`; `agentManifests.save` takes it as it is and uploads `files`. `npx nylorun apply <dir>` does the same from the command line.
|
|
@@ -349,7 +362,7 @@ With a sandbox (kind `virtual` or `pod`), the model gets `bash`, `read`, `write`
|
|
|
349
362
|
|
|
350
363
|
An Agent runs one version of its manifest at a time and evolves in place, with its id, disk, vault and sessions: a `put` on an existing one may change its `labels` and `owner` guard and moves it to the version its `manifest` names (`"name"` tracks the latest, `"name@n"` pins), `plan(id, input)` answers what that move would do, and a save of a new version moves every Agent that tracks the manifest. A harness change applies at once (each session's next turn runs it); a pod's runtime change waits for its next start on the same volume (`targetVersion`, `waitingFor: "start"`; `restart(id)` applies it now), and a start that fails goes back to the version it ran (`failedVersion`; `restart(id, { retry: true })` tries again). A version that adds a required secret waits for its value (`waitingFor: "secrets"`). `list({ outdated: true })` lists the Agents still moving. Its manifest's name, kind, harness and storage are fixed (`409 agent_fixed_field`): `fork(id, { id: next, manifest? })` makes a new Agent (default the source's manifest, tracking), with the source's disk (`disk`: `latest`, a version, or `empty`) and the secrets the new manifest declares (`secrets: "none"` copies none), and `planFork` answers what a fork would do. The Runtime checks the runtime section against the Tenant's limits (`GET`/`PUT /v1/tenant/sandbox`: a network ceiling, a resource maximum, the idle timeout; `/v1/tenant/artifacts` and `/v1/tenant/skills` for its `limits`). `lifecycle.idle` stops a pod after that long without a turn (at most the Tenant's); `lifecycle.deleteAfter` deletes the Agent, its vault and its files once nothing has used it that long. Private networks, loopback, the host and cloud metadata endpoints are always blocked.
|
|
351
364
|
|
|
352
|
-
Secrets are write-only: `client.agents.secrets.put(id, name, secret)` creates or replaces one, and `list`, `get` and `delete` never return a value. When the manifest declares the secret's slot (`runtime.secrets`), send only `{ value }` (or `{ headers }`): the type and binding come from the slot, and a name it does not declare is `409 secret_not_in_manifest`. Typed, an `environment_secret` (`{ value, allowedHosts, inject? }`) shows its name to commands as the sentinel `nylorun_managed`, and egress-gate sets its header on HTTPS requests to `allowedHosts` (pods only); an `environment_variable` (`{ value }`) is a plain variable; `bearer` (`{ url, value, via?, identity? }`) and `headers` (`{ url, headers, via?, identity? }`) are credentials for the MCP servers and HTTP tools at `url`. A second secret for one host, or a second credential for one URL, is `409 credential_conflict` at the write ([DEPLOYMENT.md](../../guides/DEPLOYMENT.md#credentials)).
|
|
365
|
+
Secrets are write-only: `client.agents.secrets.put(id, name, secret)` creates or replaces one, and `list`, `get` and `delete` never return a value. When the manifest declares the secret's slot (`runtime.secrets`), send only `{ value }` (or `{ headers }`): the type and binding come from the slot, and a name it does not declare is `409 secret_not_in_manifest`. Typed, an `environment_secret` (`{ value, allowedHosts, inject? }`) shows its name to commands as the sentinel `nylorun_managed`, and egress-gate sets its header on HTTPS requests to `allowedHosts` (pods only); an `environment_variable` (`{ value }`) is a plain variable; `bearer` (`{ url, value, via?, toolPrefix?, identity? }`) and `headers` (`{ url, headers, via?, toolPrefix?, identity? }`) are credentials for the MCP servers and HTTP tools at `url`. A second secret for one host, or a second credential for one URL, is `409 credential_conflict` at the write ([DEPLOYMENT.md](../../guides/DEPLOYMENT.md#credentials)).
|
|
353
366
|
|
|
354
367
|
Sessions on one Agent share its `/workspace` and run their turns at the same time (one turn per session); which sessions work on one disk, and when, is your app's call. Stop, reset, snapshot and restore wait until no turn runs (`409 agent_busy`). Deleting a session only detaches it. With `owner` set, a session of another owner is `403 agent_owner_mismatch` (a caller acting for a person gets the `404` of a missing Agent), checked when it opens and at every turn start. An Agent's view says what it runs (`manifest`, `resolvedVersion`, `tracking`), its `state` (`working`, `sleeping`, `input_required`, `failing` or `paused`, `AGENT_STATES`) with the sessions behind it (`attention`), its compute (`compute.state`) and its slots. `client.agents.list({ labels, state, manifest })`, `page`, `get(id)`, `events(id, { types, since, until })` (its lifecycle, and `agent.egress` for every connection its sandbox opens, naming the turns that were running), `stop(id)`, `reset(id)` and `delete(id)` manage them, and `npx nylorun agents list | put | rm` does the same from the command line. Acting for a person (`app.as(...)`), changing an Agent or its secrets needs `agents:write`, and saving a manifest `agent-manifests:write`. A trusted issuer's token reaches only the Agents its issuer grants (the identity file's `agents` templates, exact ids or prefixes ending in `/*`), checked at every turn start. The Tenant holds at most `limits.environments` of them (`PUT /v1/tenant/sandbox`, default 100).
|
|
355
368
|
|
|
@@ -13,9 +13,15 @@
|
|
|
13
13
|
* uploads the files the manifest names by hash that the Runtime does not hold (skills, a flow's
|
|
14
14
|
* agents' skills, runtime files), and refuses before sending a tool that runs your code or an
|
|
15
15
|
* `approval`, as the Runtime runs agents from their manifests alone.
|
|
16
|
+
*
|
|
17
|
+
* `saveFolder` and `planFolder` take an agent folder (`packFolder`, track T6): they compile it
|
|
18
|
+
* first with the Runtime's compiler and refuse one that does not compile (`FolderInvalidError`),
|
|
19
|
+
* upload its files by hash, and send the folder, which the Runtime compiles again and keeps as the
|
|
20
|
+
* version's source.
|
|
16
21
|
*/
|
|
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";
|
|
22
|
+
import { AGENT_MANIFEST_V8_SCHEMA_VERSION, type AgentManifestPlan, type AgentManifestSource, type AgentManifestV8, type AgentMemorySettings, type AgentManifestVersions, type AgentManifestView, type DeleteAgentManifestResponse, type RuntimeSection, type SaveAgentManifestResponse } from "@nylorun/core/contracts";
|
|
18
23
|
import { type AgentManifest, type BuiltAgent, type BuiltWorkflow, type JsonObject, type SkillFileSource, type WorkflowManifest } from "@nylorun/core/define";
|
|
24
|
+
import { type PackedFolder } from "./folder.js";
|
|
19
25
|
import { type Transport } from "./http.js";
|
|
20
26
|
/** A built agent or flow, or anything with its id and manifest. */
|
|
21
27
|
export interface AgentSource {
|
|
@@ -113,6 +119,23 @@ export declare class AgentManifestsClient {
|
|
|
113
119
|
* finds itself are among `errors`. Its files are uploaded, since they are stored by content.
|
|
114
120
|
*/
|
|
115
121
|
plan(input: AgentManifestInput, options?: Omit<AgentManifestSaveOptions, "requestId">): Promise<AgentManifestPlan>;
|
|
122
|
+
/**
|
|
123
|
+
* Saves an agent folder (`packFolder`) as its manifest's next version, keeping the folder as the
|
|
124
|
+
* version's source; `unchanged` when its manifest is. The folder is compiled first, and one that
|
|
125
|
+
* does not compile is refused before anything is sent (`FolderInvalidError`).
|
|
126
|
+
*/
|
|
127
|
+
saveFolder(folder: PackedFolder, options?: Omit<AgentManifestSaveOptions, "files">): Promise<SaveAgentManifestResponse>;
|
|
128
|
+
/** What saving an agent folder would do (`?dryRun=true`), with the compiled `manifest` and its `diagnostics`. */
|
|
129
|
+
planFolder(folder: PackedFolder, options?: Omit<AgentManifestSaveOptions, "files" | "requestId">): Promise<AgentManifestPlan>;
|
|
130
|
+
private sendFolder;
|
|
131
|
+
/** The folder version `version` of manifest `name` was saved from; a version saved as JSON has none (`404`). */
|
|
132
|
+
source(name: string, version: number, options?: {
|
|
133
|
+
signal?: AbortSignal;
|
|
134
|
+
}): Promise<AgentManifestSource>;
|
|
135
|
+
/** The bytes of file `path` in the folder version `version` of manifest `name` was saved from. */
|
|
136
|
+
sourceFile(name: string, version: number, path: string, options?: {
|
|
137
|
+
signal?: AbortSignal;
|
|
138
|
+
}): Promise<Uint8Array>;
|
|
116
139
|
/** The latest version of manifest `name`, or version `version`. */
|
|
117
140
|
get(name: string, version?: number, options?: {
|
|
118
141
|
signal?: AbortSignal;
|
package/dist/agent-manifests.js
CHANGED
|
@@ -13,10 +13,16 @@
|
|
|
13
13
|
* uploads the files the manifest names by hash that the Runtime does not hold (skills, a flow's
|
|
14
14
|
* agents' skills, runtime files), and refuses before sending a tool that runs your code or an
|
|
15
15
|
* `approval`, as the Runtime runs agents from their manifests alone.
|
|
16
|
+
*
|
|
17
|
+
* `saveFolder` and `planFolder` take an agent folder (`packFolder`, track T6): they compile it
|
|
18
|
+
* first with the Runtime's compiler and refuse one that does not compile (`FolderInvalidError`),
|
|
19
|
+
* upload its files by hash, and send the folder, which the Runtime compiles again and keeps as the
|
|
20
|
+
* version's source.
|
|
16
21
|
*/
|
|
17
|
-
import { AGENT_MANIFEST_V8_SCHEMA_VERSION, AgentManifestV8Schema, isAgentManifestName, } from "@nylorun/core/contracts";
|
|
22
|
+
import { AGENT_FOLDER_MEDIA_TYPE, AGENT_MANIFEST_V8_SCHEMA_VERSION, AgentManifestV8Schema, isAgentManifestName, } from "@nylorun/core/contracts";
|
|
18
23
|
import { approvalRefusal, codeToolRefusal, delegateOf, harnessSectionOf, isBuiltWorkflow, loopManifestOf, } from "@nylorun/core/define";
|
|
19
24
|
import { FilesClient } from "./files.js";
|
|
25
|
+
import { FolderInvalidError } from "./folder.js";
|
|
20
26
|
import { id, segment } from "./http.js";
|
|
21
27
|
/** A manifest the SDK refuses before sending: its sections do not fit, or it misses its `requires`. */
|
|
22
28
|
export class ManifestGapsError extends Error {
|
|
@@ -209,6 +215,53 @@ export class AgentManifestsClient {
|
|
|
209
215
|
const errors = [...plan.errors, ...prepared.gaps.filter((gap) => !plan.errors.includes(gap))];
|
|
210
216
|
return { ...plan, errors };
|
|
211
217
|
}
|
|
218
|
+
/**
|
|
219
|
+
* Saves an agent folder (`packFolder`) as its manifest's next version, keeping the folder as the
|
|
220
|
+
* version's source; `unchanged` when its manifest is. The folder is compiled first, and one that
|
|
221
|
+
* does not compile is refused before anything is sent (`FolderInvalidError`).
|
|
222
|
+
*/
|
|
223
|
+
async saveFolder(folder, options = {}) {
|
|
224
|
+
return this.sendFolder(folder, "", options);
|
|
225
|
+
}
|
|
226
|
+
/** What saving an agent folder would do (`?dryRun=true`), with the compiled `manifest` and its `diagnostics`. */
|
|
227
|
+
async planFolder(folder, options = {}) {
|
|
228
|
+
return this.sendFolder(folder, "?dryRun=true", options);
|
|
229
|
+
}
|
|
230
|
+
async sendFolder(folder, query, options) {
|
|
231
|
+
const { ifMatch, signal } = options;
|
|
232
|
+
// Loaded only now: the compiler's YAML parser needs Node.
|
|
233
|
+
const { checkFolder } = await import("./folder-check.js");
|
|
234
|
+
const compiled = await checkFolder(folder);
|
|
235
|
+
if (!compiled.manifest)
|
|
236
|
+
throw new FolderInvalidError(folder.dir, compiled.diagnostics);
|
|
237
|
+
const path = this.path(compiled.manifest.name);
|
|
238
|
+
const files = new Map(folder.entries.map((entry) => [`sha256:${entry.sha256}`, folder.files.get(entry.path)]));
|
|
239
|
+
await new FilesClient(this.transport).ensure(files, signal ? { signal } : {});
|
|
240
|
+
const transport = ifMatch === undefined ? this.transport : this.transport.withHeaders({ "If-Match": String(ifMatch) });
|
|
241
|
+
const response = await transport.request(`${path}${query}`, {
|
|
242
|
+
method: "PUT",
|
|
243
|
+
headers: { "content-type": AGENT_FOLDER_MEDIA_TYPE },
|
|
244
|
+
body: JSON.stringify({
|
|
245
|
+
requestId: options.requestId ?? id(),
|
|
246
|
+
format: "nylorun.folder.v2",
|
|
247
|
+
entries: folder.entries.map(({ path, size, sha256 }) => ({ path, size, sha256 })),
|
|
248
|
+
}),
|
|
249
|
+
...(signal ? { signal } : {}),
|
|
250
|
+
});
|
|
251
|
+
return (await response.json());
|
|
252
|
+
}
|
|
253
|
+
/** The folder version `version` of manifest `name` was saved from; a version saved as JSON has none (`404`). */
|
|
254
|
+
async source(name, version, options = {}) {
|
|
255
|
+
return this.transport.json(`${this.path(name)}/versions/${version}/source`, "GET", undefined, options.signal);
|
|
256
|
+
}
|
|
257
|
+
/** The bytes of file `path` in the folder version `version` of manifest `name` was saved from. */
|
|
258
|
+
async sourceFile(name, version, path, options = {}) {
|
|
259
|
+
const response = await this.transport.request(`${this.path(name)}/versions/${version}/source/files/${segment(path)}`, {
|
|
260
|
+
method: "GET",
|
|
261
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
262
|
+
});
|
|
263
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
264
|
+
}
|
|
212
265
|
/** The latest version of manifest `name`, or version `version`. */
|
|
213
266
|
async get(name, version, options = {}) {
|
|
214
267
|
return this.transport.json(`${this.path(name)}${version === undefined ? "" : `/versions/${version}`}`, "GET", undefined, options.signal);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `checkFolder`: compiles an agent folder offline with the compiler the Runtime runs
|
|
3
|
+
* (`@nylorun/core/folder`). Its YAML parser loads Node modules, so the client entry imports this
|
|
4
|
+
* module only when a folder is sent.
|
|
5
|
+
*/
|
|
6
|
+
import { type CompiledAgentFolder } from "@nylorun/core/folder";
|
|
7
|
+
import type { PackedFolder } from "./folder.js";
|
|
8
|
+
/**
|
|
9
|
+
* Compiles a packed folder offline, as the Runtime will: the manifest when it compiles, and every
|
|
10
|
+
* diagnostic either way. With `name`, the name it will be saved as, `AGENT.md`'s must match.
|
|
11
|
+
*/
|
|
12
|
+
export declare function checkFolder(packed: PackedFolder, options?: {
|
|
13
|
+
name?: string;
|
|
14
|
+
}): Promise<CompiledAgentFolder>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `checkFolder`: compiles an agent folder offline with the compiler the Runtime runs
|
|
3
|
+
* (`@nylorun/core/folder`). Its YAML parser loads Node modules, so the client entry imports this
|
|
4
|
+
* module only when a folder is sent.
|
|
5
|
+
*/
|
|
6
|
+
import { AGENT_FOLDER_LIMITS, compileAgentFolder, folderTextPaths, } from "@nylorun/core/folder";
|
|
7
|
+
/**
|
|
8
|
+
* Compiles a packed folder offline, as the Runtime will: the manifest when it compiles, and every
|
|
9
|
+
* diagnostic either way. With `name`, the name it will be saved as, `AGENT.md`'s must match.
|
|
10
|
+
*/
|
|
11
|
+
export async function checkFolder(packed, options = {}) {
|
|
12
|
+
const text = new Map();
|
|
13
|
+
for (const path of folderTextPaths(packed.entries)) {
|
|
14
|
+
const source = packed.files.get(path);
|
|
15
|
+
// The compiler reports a file over its limit; it needs no bytes for it.
|
|
16
|
+
if (source.size <= AGENT_FOLDER_LIMITS.textFileBytes)
|
|
17
|
+
text.set(path, await source.read());
|
|
18
|
+
}
|
|
19
|
+
return compileAgentFolder({ entries: packed.entries, files: text, ...(options.name === undefined ? {} : { name: options.name }) });
|
|
20
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type PackedFolder } from "./folder.js";
|
|
2
|
+
/** Whether `dir` holds an `AGENT.md`. */
|
|
3
|
+
export declare function isAgentFolder(dir: string): Promise<boolean>;
|
|
4
|
+
/** Lists the agent folder at `dir` with each file's size and SHA-256. */
|
|
5
|
+
export declare function packFolder(dir: string): Promise<PackedFolder>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Packing an agent folder (track T6): `packFolder(dir)` lists every file with its size and SHA-256
|
|
3
|
+
* and reads its bytes on demand. It skips `.git`, `.hg`, `.svn` and `node_modules` and follows no
|
|
4
|
+
* symbolic link. Node only: it reads the folder.
|
|
5
|
+
*/
|
|
6
|
+
import { createHash } from "node:crypto";
|
|
7
|
+
import { readdir, readFile, stat } from "node:fs/promises";
|
|
8
|
+
import { join, relative, resolve, sep } from "node:path";
|
|
9
|
+
import { AGENT_FOLDER_LIMITS } from "@nylorun/core/folder";
|
|
10
|
+
import { AGENT_FOLDER_FILE, FolderError } from "./folder.js";
|
|
11
|
+
const SKIPPED_DIRECTORIES = new Set([".git", ".hg", ".svn", "node_modules"]);
|
|
12
|
+
/** Whether `dir` holds an `AGENT.md`. */
|
|
13
|
+
export async function isAgentFolder(dir) {
|
|
14
|
+
return stat(join(dir, AGENT_FOLDER_FILE)).then((found) => found.isFile(), () => false);
|
|
15
|
+
}
|
|
16
|
+
/** Lists the agent folder at `dir` with each file's size and SHA-256. */
|
|
17
|
+
export async function packFolder(dir) {
|
|
18
|
+
const root = resolve(dir);
|
|
19
|
+
const entries = [];
|
|
20
|
+
const files = new Map();
|
|
21
|
+
const skipped = [];
|
|
22
|
+
let bytes = 0;
|
|
23
|
+
const walk = async (at) => {
|
|
24
|
+
const children = await readdir(at, { withFileTypes: true }).catch((error) => {
|
|
25
|
+
throw new FolderError(root, `Cannot read ${at}: ${error.code ?? error}`);
|
|
26
|
+
});
|
|
27
|
+
for (const child of children.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0))) {
|
|
28
|
+
const full = join(at, child.name);
|
|
29
|
+
const path = relative(root, full).split(sep).join("/");
|
|
30
|
+
if (child.isDirectory()) {
|
|
31
|
+
if (!SKIPPED_DIRECTORIES.has(child.name))
|
|
32
|
+
await walk(full);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (child.isSymbolicLink()) {
|
|
36
|
+
skipped.push(`${path}: a symbolic link, not followed`);
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (!child.isFile())
|
|
40
|
+
continue;
|
|
41
|
+
if (entries.length >= AGENT_FOLDER_LIMITS.files)
|
|
42
|
+
throw new FolderError(root, `${dir} holds more than ${AGENT_FOLDER_LIMITS.files} files, an agent folder's limit`);
|
|
43
|
+
const data = await readFile(full);
|
|
44
|
+
bytes += data.byteLength;
|
|
45
|
+
if (bytes > AGENT_FOLDER_LIMITS.totalBytes)
|
|
46
|
+
throw new FolderError(root, `${dir} holds more than ${AGENT_FOLDER_LIMITS.totalBytes} bytes, an agent folder's limit`);
|
|
47
|
+
entries.push({ path, size: data.byteLength, sha256: createHash("sha256").update(data).digest("hex") });
|
|
48
|
+
files.set(path, { size: data.byteLength, read: () => readFile(full) });
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
await walk(root);
|
|
52
|
+
entries.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
|
|
53
|
+
return { dir: root, entries, files, skipped };
|
|
54
|
+
}
|
package/dist/folder.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent folders (track T6, D79): a folder of Markdown and JSON (`AGENT.md`, `mcp.json`,
|
|
3
|
+
* `runtime.json`, `skills/`, `tools/`, `triggers/`, `files/`) that `@nylorun/core/folder`
|
|
4
|
+
* compiles into agent manifest v8. `packFolder(dir)` (`folder-pack.ts`, Node only) lists it with
|
|
5
|
+
* each file's size and hash, `checkFolder` compiles it offline with the same compiler the Runtime
|
|
6
|
+
* runs, and `client.agentManifests.saveFolder` sends it by hash, so the Runtime keeps the folder
|
|
7
|
+
* as the version's source. This module loads neither the file system nor the compiler, whose
|
|
8
|
+
* YAML parser needs Node: the client entry loads it, and `checkFolder` (`folder-check.ts`) only
|
|
9
|
+
* when a folder is sent.
|
|
10
|
+
*/
|
|
11
|
+
import type { AgentFolderDiagnostic, AgentFolderEntry } from "@nylorun/core/folder";
|
|
12
|
+
import type { SkillFileSource } from "@nylorun/core/define";
|
|
13
|
+
/** The file that makes a folder an agent folder. */
|
|
14
|
+
export declare const AGENT_FOLDER_FILE = "AGENT.md";
|
|
15
|
+
/** A folder as the Runtime takes it: each file's path, size and hash, and a way to read its bytes. */
|
|
16
|
+
export interface PackedFolder {
|
|
17
|
+
/** The folder's absolute path. */
|
|
18
|
+
readonly dir: string;
|
|
19
|
+
/** Every file, sorted by path, with `/` between segments. */
|
|
20
|
+
readonly entries: readonly AgentFolderEntry[];
|
|
21
|
+
/** Each file's bytes, by path. */
|
|
22
|
+
readonly files: ReadonlyMap<string, SkillFileSource>;
|
|
23
|
+
/** What packing left out, and why (a symbolic link). */
|
|
24
|
+
readonly skipped: readonly string[];
|
|
25
|
+
}
|
|
26
|
+
/** A folder that cannot be packed: too many files, or more bytes than an agent folder holds. */
|
|
27
|
+
export declare class FolderError extends Error {
|
|
28
|
+
readonly dir: string;
|
|
29
|
+
constructor(dir: string, message: string);
|
|
30
|
+
}
|
|
31
|
+
/** A folder that does not compile; `diagnostics` holds every problem, by file, line and column. */
|
|
32
|
+
export declare class FolderInvalidError extends Error {
|
|
33
|
+
readonly dir: string;
|
|
34
|
+
readonly diagnostics: readonly AgentFolderDiagnostic[];
|
|
35
|
+
constructor(dir: string, diagnostics: readonly AgentFolderDiagnostic[]);
|
|
36
|
+
}
|
|
37
|
+
/** A diagnostic as a person reads it: `file:line:column severity message`. */
|
|
38
|
+
export declare function diagnosticLine(diagnostic: AgentFolderDiagnostic): string;
|
package/dist/folder.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** The file that makes a folder an agent folder. */
|
|
2
|
+
export const AGENT_FOLDER_FILE = "AGENT.md";
|
|
3
|
+
/** A folder that cannot be packed: too many files, or more bytes than an agent folder holds. */
|
|
4
|
+
export class FolderError extends Error {
|
|
5
|
+
dir;
|
|
6
|
+
constructor(dir, message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.dir = dir;
|
|
9
|
+
this.name = "FolderError";
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/** A folder that does not compile; `diagnostics` holds every problem, by file, line and column. */
|
|
13
|
+
export class FolderInvalidError extends Error {
|
|
14
|
+
dir;
|
|
15
|
+
diagnostics;
|
|
16
|
+
constructor(dir, diagnostics) {
|
|
17
|
+
const errors = diagnostics.filter((item) => item.severity === "error");
|
|
18
|
+
super(`Agent folder ${dir} does not compile:\n${errors.map((item) => ` ${diagnosticLine(item)}`).join("\n")}`);
|
|
19
|
+
this.dir = dir;
|
|
20
|
+
this.diagnostics = diagnostics;
|
|
21
|
+
this.name = "FolderInvalidError";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/** A diagnostic as a person reads it: `file:line:column severity message`. */
|
|
25
|
+
export function diagnosticLine(diagnostic) {
|
|
26
|
+
const where = diagnostic.file
|
|
27
|
+
? `${diagnostic.file}${diagnostic.line ? `:${diagnostic.line}${diagnostic.column ? `:${diagnostic.column}` : ""}` : ""} `
|
|
28
|
+
: "";
|
|
29
|
+
return `${where}${diagnostic.severity}: ${diagnostic.message}`;
|
|
30
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -16,6 +16,10 @@ export type { ArtifactBody, ArtifactPageOptions, ArtifactLinkWithUrl, UploadArti
|
|
|
16
16
|
export type { ActAsOptions, AgentSource, SessionView, CommandOptions, CreateSessionOptions, SessionSandbox, AgentFilter, AgentInput, AgentSecretInput, RuntimeFileBytes, AgentManifestDocument, AgentManifestFromAgent, AgentManifestInput, AgentManifestSaveOptions, ComposedManifest, DefinitionFiles, } from "./client.js";
|
|
17
17
|
export { AgentInstancesClient, AgentManifestsClient, AgentMemoriesClient, AgentSecretsClient, ManifestGapsError, agentManifest, requirementGaps, runtimeFiles, } from "./client.js";
|
|
18
18
|
export { composeManifest, manifestFolders, ComposeError, AGENT_MANIFEST_FILE } from "./compose.js";
|
|
19
|
+
export { AGENT_FOLDER_FILE, FolderError, FolderInvalidError, diagnosticLine } from "./folder.js";
|
|
20
|
+
export { checkFolder } from "./folder-check.js";
|
|
21
|
+
export type { PackedFolder } from "./folder.js";
|
|
22
|
+
export { packFolder, isAgentFolder } from "./folder-pack.js";
|
|
19
23
|
export { AccessClient } from "./access.js";
|
|
20
24
|
export { resolveConnection, ConnectionError } from "./connection.js";
|
|
21
25
|
export type { ResolvedConnection } from "./connection.js";
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,9 @@ export { ArtifactsClient } from "./artifacts.js";
|
|
|
10
10
|
export { FilesClient, definitionFileHash } from "./files.js";
|
|
11
11
|
export { AgentInstancesClient, AgentManifestsClient, AgentMemoriesClient, AgentSecretsClient, ManifestGapsError, agentManifest, requirementGaps, runtimeFiles, } from "./client.js";
|
|
12
12
|
export { composeManifest, manifestFolders, ComposeError, AGENT_MANIFEST_FILE } from "./compose.js";
|
|
13
|
+
export { AGENT_FOLDER_FILE, FolderError, FolderInvalidError, diagnosticLine } from "./folder.js";
|
|
14
|
+
export { checkFolder } from "./folder-check.js";
|
|
15
|
+
export { packFolder, isAgentFolder } from "./folder-pack.js";
|
|
13
16
|
export { AccessClient } from "./access.js";
|
|
14
17
|
export { resolveConnection, ConnectionError } from "./connection.js";
|
|
15
18
|
export { RuntimeError, IncompatibleRuntimeError } from "./http.js";
|
package/dist/session-client.d.ts
CHANGED
|
@@ -421,4 +421,6 @@ export { AccessClient } from "./access.js";
|
|
|
421
421
|
export { AgentInstancesClient, AgentMemoriesClient, AgentSecretsClient, runtimeFiles } from "./agents.js";
|
|
422
422
|
export type { AgentFilter, AgentInput, AgentSecretInput, RuntimeFileBytes } from "./agents.js";
|
|
423
423
|
export { AgentManifestsClient, ManifestGapsError, agentManifest, requirementGaps } from "./agent-manifests.js";
|
|
424
|
+
export { AGENT_FOLDER_FILE, FolderError, FolderInvalidError, diagnosticLine } from "./folder.js";
|
|
425
|
+
export type { PackedFolder } from "./folder.js";
|
|
424
426
|
export type { AgentManifestDocument, AgentManifestFromAgent, AgentManifestInput, AgentManifestSaveOptions, AgentSource, ComposedManifest, DefinitionFiles, } from "./agent-manifests.js";
|
package/dist/session-client.js
CHANGED
|
@@ -336,3 +336,4 @@ export { RuntimeError, IncompatibleRuntimeError } from "./http.js";
|
|
|
336
336
|
export { AccessClient } from "./access.js";
|
|
337
337
|
export { AgentInstancesClient, AgentMemoriesClient, AgentSecretsClient, runtimeFiles } from "./agents.js";
|
|
338
338
|
export { AgentManifestsClient, ManifestGapsError, agentManifest, requirementGaps } from "./agent-manifests.js";
|
|
339
|
+
export { AGENT_FOLDER_FILE, FolderError, FolderInvalidError, diagnosticLine } from "./folder.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nylorun/agents",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.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.
|
|
46
|
+
"@nylorun/core": "0.27.0-beta",
|
|
47
47
|
"gray-matter": "^4.0.3"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|