@nylorun/agents 0.27.0-beta → 0.29.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 +62 -0
- package/README.md +26 -12
- package/dist/agent-manifests.d.ts +8 -1
- package/dist/agent-manifests.js +8 -0
- package/dist/compose.d.ts +5 -0
- package/dist/compose.js +14 -2
- package/dist/folder-check.d.ts +2 -0
- package/dist/folder-check.js +2 -0
- package/dist/folder-ignore.d.ts +13 -0
- package/dist/folder-ignore.js +113 -0
- package/dist/folder-pack.d.ts +2 -0
- package/dist/folder-pack.js +117 -7
- package/dist/folder.d.ts +9 -1
- package/dist/folder.js +4 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.js +8 -4
- package/dist/plugins/load.js +2 -1
- package/dist/session-client.d.ts +71 -3
- package/dist/session-client.js +28 -5
- package/dist/skills/load.js +7 -0
- package/dist/skills/skills.d.ts +4 -3
- package/dist/skills/skills.js +12 -3
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,67 @@
|
|
|
1
1
|
# @nylorun/agents
|
|
2
2
|
|
|
3
|
+
## 0.29.0-beta
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- d99b3ac: A session can have its own model (optional Host feature `session-model`). `PUT /v1/sessions/{id}/model` chooses one of a connected provider's models for that session's turns, `DELETE` clears it, and `GET` answers the model the next turn uses (`source: "session"` or `"tenant"`). The Tenant's model and every other session's stay as they are. The Model Gate calls the session's model on its provider's credential (a refreshed OAuth credential is written back to that provider), `turn.start.model` describes it, and the usage ledger records it. A choice names a connected provider and a model its catalog lists, is set by the application only (a trusted issuer's token can read it, not change it), and is refused while a turn runs or waits (`409 session_busy`); while its provider is disconnected the session uses the Tenant's model. The SDK adds `session.model()`, `setModel({ provider, model })` and `clearModel()`.
|
|
8
|
+
|
|
9
|
+
Studio's model picker under the message box now sets the model of that session only, instead of the Tenant's: "Tenant model" clears the session's choice, the picker is locked while a turn runs, it groups models by provider, and its search keeps what is typed. A Runtime without `session-model` shows no picker.
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Pin core to the tested release.
|
|
14
|
+
- Updated dependencies [95f98f1]
|
|
15
|
+
- Updated dependencies [d99b3ac]
|
|
16
|
+
- @nylorun/core@0.29.0-beta
|
|
17
|
+
|
|
18
|
+
## 0.28.0-beta
|
|
19
|
+
|
|
20
|
+
### Minor Changes
|
|
21
|
+
|
|
22
|
+
- 498d972: `@nylorun/agents` exports `harnessSectionOf` and `sectionGaps` from its main entry, as its README documents: the harness section `agentManifests.save` makes from a built agent or flow, and the gaps the Runtime refuses between a manifest's two sections. Both were only in `@nylorun/agents/define` and `@nylorun/core/define`.
|
|
23
|
+
|
|
24
|
+
`nylo` no longer says nylorun 0.19 removes it, which 0.19 does not: its message ends "A later nylorun release removes nylo."
|
|
25
|
+
|
|
26
|
+
- b3dad06: Agent folders convert both ways (track T7, D80). `folderOfManifest(manifest)` in `@nylorun/core/folder` (also exported by `@nylorun/agents`) writes the agent folder that compiles back to the same manifest: name, description, metadata, harness, memory, loop and server and skill settings in `AGENT.md`, the first instruction as its body and the rest as `instructions/NN-*.md`, HTTP tools as `tools/<name>.json`, subagents as `agents/<name>/`, a flow as `flow.json` (v3's node tree, naming agents by id and tools by file name) with one flat `agents/<id>/` for its agents and its sub-flows' (a sub-flow is an `agents/<id>/` with its own `flow.json`, and a tool stage or HTTP verifier a `tools/<name>.json` beside the `flow.json` that names it), MCP servers in `mcp.json` with `Bearer ${SLOT}` or `${SLOT}` for a credential, triggers as `triggers/<id>.md`, and `runtime.json` without the slots the compiler derives the same way. It checks itself by compiling the folder, says whether the hash is the same (`exact`), and notes what it writes differently. Skills are written from their bytes when given them, else as a placeholder with the files to copy in. An embedded agent's display `name` is left out with a note, since a folder names an agent by its id.
|
|
27
|
+
|
|
28
|
+
`nylorun export --folder <name>[@<version>]` writes the folder of a saved manifest, from JSON or TypeScript; `nylorun migrate folder <dir>` writes the folder of a three-file folder, offline, with its skills; `nylorun pull <name>[@<version>]` writes back the folder a version was saved from, byte for byte, and says to use `export --folder` for a version saved as JSON. None writes over a file with other bytes.
|
|
29
|
+
|
|
30
|
+
Deprecated, still read: the three-file format (`agent.manifest.json`), whose `composeManifest` and `nylorun apply` now warn and name `nylorun migrate folder`, and `secrets` in a `SKILL.md`, which the skills loader warns about (a `DeprecationWarning` from `.skills()`): an agent folder lists a skill's secrets in `AGENT.md`.
|
|
31
|
+
|
|
32
|
+
- b3dad06: Both ends of the agent folder loop (track T7).
|
|
33
|
+
|
|
34
|
+
- `nylorun init <path>` names the agent after the path's last segment (`init agents/hello`). `--example` adds `tools/get_order.json`, `mcp.json` (a `Bearer ${ORDERS_MCP_TOKEN}` header) and `triggers/new-order.md`, explained in the folder's README.md, and the result passes `nylorun check`. The first run in a project (the nearest directory with `.git`) writes `.gitattributes` (`* text=auto eol=lf`), ignores `.nylorun/credentials.json` in `.gitignore`, and maps the folder files to their JSON Schemas in `.vscode/settings.json`, merged into an existing file. Its hint names `apply --agent` and `chat`.
|
|
35
|
+
- `nylorun apply --agent [<id>]` makes the Agent from the manifest after a save (or on an unchanged one), or moves it, as `nylorun agents put` does, and prints what it needs a person for.
|
|
36
|
+
- `nylorun apply --watch [--save]` checks and plans again on each change to the folders (and the files their `runtime.json` extends); it saves only with `--save`.
|
|
37
|
+
- A plan's change to the instructions prints their text diff, `-` and `+` lines.
|
|
38
|
+
- `nylorun chat <agent> [--session <id>] [--message <text>]` opens or reuses a session, sends `--message` or each line of standard input, and prints the reply as it comes.
|
|
39
|
+
- `nylorun check --json` prints `{ folders: [{ path, ok, manifest?, diagnostics }] }` for CI, with the same exit codes. `check` says "1 file", and `nylorun --version` prints the version.
|
|
40
|
+
- `packFolder` (`@nylorun/agents`) leaves out what the folder's `.nyloignore` names (gitignore rules), and resolves `extends` in `runtime.json`: the packed file is the merge, the folder's own keys winning, with `.nylorun/origin.json` naming the files it came from; a missing file or a cycle is a `FolderError`. `ignoreMatcher` is exported.
|
|
41
|
+
- `session.approve(id, approved)` and `session.respond(id, value)` take no options: each makes its own idempotency key.
|
|
42
|
+
- The Runtime's Agent view names each required secret of the version it runs that has no value, as an attention item (`secret_unset: required secret ORDERS_TOKEN unset`, state `input_required`), until it is set.
|
|
43
|
+
- The compiler's refusal of an unresolved `extends` says to resolve it before uploading.
|
|
44
|
+
|
|
45
|
+
- b3dad06: Compare two versions' agent folders, and set what a folder may hold per Tenant (track T7).
|
|
46
|
+
|
|
47
|
+
- **`GET /v1/agent-manifests/{name}/source/diff?from=<v>&to=<v>`** answers the files added, removed and changed (by SHA-256) from one version's source to another's, each list sorted by path, and how many stayed the same (`AgentManifestSourceDiff` in `@nylorun/core/contracts`). A version saved as JSON has no source (`404`); a `from` or `to` that is not a version number is `400`. `client.agentManifests.sourceDiff(name, from, to)` in `@nylorun/agents` calls it.
|
|
48
|
+
- **Folder limits are Tenant settings**: the Runtime limits `folderFiles` (2,000), `folderBytes` (50 MiB) and `folderTextFileBytes` (1 MiB), T6's fixed values, set with `PUT /v1/tenant/limits` like the others. A folder save with more files or bytes is `413 limit_exceeded` (`details.limit` names the limit), and the compiler holds each Markdown or JSON file to `folderTextFileBytes` (`422 folder_invalid`).
|
|
49
|
+
- `compileAgentFolder({ entries, files, name, limits })` takes the limits as an optional input (`AgentFolderLimits`); each left out is `AGENT_FOLDER_LIMITS`'.
|
|
50
|
+
- `@nylorun/agents` also exports the types `AgentManifestSource`, `AgentManifestSourceDiff`, `AgentManifestSourceSummary` and `AgentFolderDiagnostic`, and `AGENT_FOLDER_MEDIA_TYPE`.
|
|
51
|
+
|
|
52
|
+
### Patch Changes
|
|
53
|
+
|
|
54
|
+
- 7bcdc85: `composeManifest` reads the `mcp.json` a `harness.manifest.json` names in a folder reached through a symlink, such as macOS's temporary directory (`/var` → `/private/var`). The package boundary is now resolved through `realpath` like the file, so it is no longer refused as "not a file inside the package".
|
|
55
|
+
- Pin core to the tested release.
|
|
56
|
+
- Updated dependencies [498d972]
|
|
57
|
+
- Updated dependencies [d23304e]
|
|
58
|
+
- Updated dependencies [b3dad06]
|
|
59
|
+
- Updated dependencies [b3dad06]
|
|
60
|
+
- Updated dependencies [b3dad06]
|
|
61
|
+
- Updated dependencies [498d972]
|
|
62
|
+
- Updated dependencies [b3dad06]
|
|
63
|
+
- @nylorun/core@0.28.0-beta
|
|
64
|
+
|
|
3
65
|
## 0.27.0-beta
|
|
4
66
|
|
|
5
67
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -150,6 +150,8 @@ management key (`PUT /v1/tenant/tool-policy`, `admin.toolPolicy.put()` in
|
|
|
150
150
|
journals the call, before anything is sent, and its Tool Gate runs only a call
|
|
151
151
|
the journal holds running, so a harness cannot skip it.
|
|
152
152
|
|
|
153
|
+
<!-- doc-test: tool-policy -->
|
|
154
|
+
<!-- doc-test: typescript -->
|
|
153
155
|
```ts
|
|
154
156
|
import { createAdmin } from "@nylorun/admin";
|
|
155
157
|
|
|
@@ -157,23 +159,27 @@ await createAdmin().toolPolicy.put({
|
|
|
157
159
|
rules: [
|
|
158
160
|
{ effect: "ask", tool: "refund_order", reason: "Refunds need a person" },
|
|
159
161
|
{ effect: "deny", tool: "refund_order", when: { amount: { gt: 10_000 } } },
|
|
160
|
-
{ effect: "ask", server: "github", tool: "
|
|
162
|
+
{ effect: "ask", server: "github", tool: "delete_*" },
|
|
161
163
|
],
|
|
162
164
|
});
|
|
163
165
|
```
|
|
164
166
|
|
|
165
|
-
A rule matches a call when every selector it sets matches: `tool` (the
|
|
166
|
-
|
|
167
|
+
A rule matches a call when every selector it sets matches: `tool` (the tool's
|
|
168
|
+
own name: an MCP tool's name on its server, such as `delete_file`, or an HTTP
|
|
169
|
+
tool's declared name; `*` matches any run of characters), `server` (an MCP
|
|
167
170
|
server's name), `kind` (`mcp` or `http`), `agent` (the agent that calls it) and
|
|
168
171
|
`when` (fields of the call's input by dotted path, each tested with `equals`,
|
|
169
172
|
`in`, `gt`, `gte`, `lt`, `lte` or `matches`). The strictest matching rule
|
|
170
|
-
decides, and a call no rule matches runs
|
|
173
|
+
decides, and a call no rule matches runs. Name an MCP tool by `server` and
|
|
174
|
+
`tool`, never by the name the model calls it by (`github__delete_file`): a
|
|
175
|
+
harness chooses that name, and a `tool` with `__` in it is refused (`400`).
|
|
171
176
|
|
|
172
177
|
- `deny`: the call is never sent; the model gets `tool.denied` with the rule's
|
|
173
178
|
`reason`.
|
|
174
179
|
- `ask`: the turn pauses (`turn.paused`, with the tool, its input and the
|
|
175
180
|
reason). `session.approve(interactionId, true)` sends that exact call;
|
|
176
|
-
`false` fails it to the model as `tool.denied`, unsent
|
|
181
|
+
`false` fails it to the model as `tool.denied`, unsent (pass
|
|
182
|
+
`{ idempotencyKey }` as a third argument to retry an answer safely). Nothing holds a
|
|
177
183
|
process while it waits, so an approval can wait a day, or a Runtime restart.
|
|
178
184
|
Studio shows an approval card; AG-UI ends the run with a `tool_approval`
|
|
179
185
|
interrupt.
|
|
@@ -252,7 +258,7 @@ const assistant = Agent({ id: "assistant", name: "Order assistant" })
|
|
|
252
258
|
.skills("./assistant-skills");
|
|
253
259
|
```
|
|
254
260
|
|
|
255
|
-
Each subdirectory under the catalog must contain a `SKILL.md` with YAML frontmatter (`name`, `description`) per [Agent Skills](https://agentskills.io/home). Its `secrets` (`secrets: [GH_TOKEN]`, environment variable names) are the skill's `secrets` in the harness section: the `runtime.secrets` slots its scripts read, of type `environment_secret` or `environment_variable`; a skill whose `secrets` is not such a list is skipped with a warning. Every file of a skill's folder, binary included (not `.git/`, `node_modules/`, OS files like `.DS_Store`, or `.env` and `.env.*` files, which hold secrets), is part of the definition: the manifest names each one by path and SHA-256 (`skills.<name>.files`). A skill's files may hold together at most the Tenant's skill limit (`PUT /v1/tenant/skills`, default 2 GiB); no file and no count of files has a limit of its own, and a manifest's runtime section may set a lower limit for its Agents' sessions (`runtime.limits.skills.totalBytes`). Skills need a sandbox: the runtime section's kind is `virtual` or `pod`, which the Runtime checks when the manifest is saved. `agentManifests.save` uploads the files the Runtime does not hold (`client.files`, `PUT /v1/files/sha256:<hex>`) before it saves the manifest, which the Runtime refuses while it names a file it lacks (`400 definition_files_missing`) or a skill past the Tenant's limit (`413 limit_exceeded`). `read_skill_resource` reads files of up to 10 MiB into the model's context; a larger one is used in the sandbox.
|
|
261
|
+
Each subdirectory under the catalog must contain a `SKILL.md` with YAML frontmatter (`name`, `description`) per [Agent Skills](https://agentskills.io/home). Its `secrets` (`secrets: [GH_TOKEN]`, environment variable names) are the skill's `secrets` in the harness section: the `runtime.secrets` slots its scripts read, of type `environment_secret` or `environment_variable`; a skill whose `secrets` is not such a list is skipped with a warning. `secrets` is not an Agent Skills key, so it is deprecated in `SKILL.md` (still read, with a `DeprecationWarning`): an agent folder lists a skill's secrets in `AGENT.md`'s skills block (`skills: { <name>: { secrets: [GH_TOKEN] } }`). Every file of a skill's folder, binary included (not `.git/`, `node_modules/`, OS files like `.DS_Store`, or `.env` and `.env.*` files, which hold secrets), is part of the definition: the manifest names each one by path and SHA-256 (`skills.<name>.files`). A skill's files may hold together at most the Tenant's skill limit (`PUT /v1/tenant/skills`, default 2 GiB); no file and no count of files has a limit of its own, and a manifest's runtime section may set a lower limit for its Agents' sessions (`runtime.limits.skills.totalBytes`). Skills need a sandbox: the runtime section's kind is `virtual` or `pod`, which the Runtime checks when the manifest is saved. `agentManifests.save` uploads the files the Runtime does not hold (`client.files`, `PUT /v1/files/sha256:<hex>`) before it saves the manifest, which the Runtime refuses while it names a file it lacks (`400 definition_files_missing`) or a skill past the Tenant's limit (`413 limit_exceeded`). `read_skill_resource` reads files of up to 10 MiB into the model's context; a larger one is used in the sandbox.
|
|
256
262
|
|
|
257
263
|
The Runtime serves the skills itself, with no call to your process. When our harness runs a turn, it adds the skill catalog to the instructions and two tools: `load_skill` returns a skill's `SKILL.md` body and the paths of its other files, and `read_skill_resource` returns a text file. A session with a sandbox also has each skill's files read-only under `/skills/<name>@<hash>/`, so the model can run a skill's scripts with `bash`.
|
|
258
264
|
|
|
@@ -301,12 +307,13 @@ await client.agents.secrets.put("users/alice/analyst", "GH_TOKEN", { value: proc
|
|
|
301
307
|
const session = await client.createSession({ id: "alice-q3", agent: "users/alice/analyst", ownerUserId: "alice" });
|
|
302
308
|
```
|
|
303
309
|
|
|
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.
|
|
310
|
+
`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`, exported for code that builds a manifest itself, 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
311
|
|
|
306
312
|
### Agent folders: `packFolder`, `checkFolder`, `saveFolder`
|
|
307
313
|
|
|
308
|
-
An agent
|
|
314
|
+
An agent is best written as a folder of Markdown and JSON (tracks T6 and T7): `AGENT.md` with `name`, `description` and the instructions, and beside it `mcp.json`, `runtime.json`, `skills/`, `tools/`, `triggers/` and `files/` as it needs them. `agents/<id>/` holds subagents (each offered to the agent as a tool), `flow.json` makes the folder a flow whose stages name those agents by id, and `plugins/<name>/` vendors an Agent Plugin; [the CLI's README](../../cli/nylorun/README.md#agent-manifests-apply) shows each. `packFolder(dir)` (Node only) lists every file with its size and SHA-256, skipping `.git`, `node_modules` and what the folder's `.nyloignore` names (gitignore rules, `ignoreMatcher`), and resolves `"extends"` in `runtime.json` (the packed file is the merge, the folder's own keys winning); `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, and `sourceDiff(name, from, to)` compares two versions' sources: the files added, removed and changed, by hash. `folderOfManifest(manifest)` goes the other way: the folder that compiles back to a saved manifest (what `nylorun export --folder` writes).
|
|
309
315
|
|
|
316
|
+
<!-- doc-test: typescript -->
|
|
310
317
|
```ts
|
|
311
318
|
import { createClient, packFolder } from "@nylorun/agents";
|
|
312
319
|
|
|
@@ -316,9 +323,11 @@ console.log(plan.manifest, plan.diagnostics); // the compiled manifest, and e.g.
|
|
|
316
323
|
await client.agentManifests.saveFolder(await packFolder("agents/triage"), { ifMatch: plan.from ?? 0 });
|
|
317
324
|
```
|
|
318
325
|
|
|
319
|
-
### Three files: `composeManifest`
|
|
326
|
+
### Three files: `composeManifest` (deprecated)
|
|
320
327
|
|
|
321
|
-
|
|
328
|
+
The three-file form is deprecated in favour of agent folders: `composeManifest` and `nylorun apply` still read it, and warn, and `npx nylorun migrate folder <dir>` writes the agent folder of a three-file folder, with its skills, offline. A folder's `runtime.json` shares a runtime between agents with `"extends"`.
|
|
329
|
+
|
|
330
|
+
A manifest can be authored as three 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.
|
|
322
331
|
|
|
323
332
|
```jsonc
|
|
324
333
|
// agents/triage/agent.manifest.json
|
|
@@ -413,7 +422,12 @@ the manifest, then `createSession({ agent })` and `session.input(value)`. `input
|
|
|
413
422
|
`content` and other JSON as `data`. On the wire a flow agent is a workflow manifest v3
|
|
414
423
|
(`kind: "workflow"`, `workflowSchemaVersion: 3`) that embeds the agents it runs, so one
|
|
415
424
|
document and one manifest hash cover the whole flow; its agents are not listed on their own.
|
|
416
|
-
In an agent manifest it is the harness section, `harness.kind: "workflow"`.
|
|
425
|
+
In an agent manifest it is the harness section, `harness.kind: "workflow"`. A flow can also
|
|
426
|
+
be an [agent folder](#agent-folders-packfolder-checkfolder-savefolder): `flow.json` holds the
|
|
427
|
+
same node tree, naming its agents (each an `agents/<id>/` folder) by id, and the compiler
|
|
428
|
+
embeds each agent where the manifest needs it
|
|
429
|
+
([`examples/agents/repo-brief-folder/`](../../examples/agents/repo-brief-folder/) is the Repo
|
|
430
|
+
brief flow as a folder).
|
|
417
431
|
|
|
418
432
|
A flow passes data between its stages itself: the first stage gets the flow's input, and
|
|
419
433
|
every later stage gets the previous stage's output. So each agent returns, through its
|
|
@@ -542,7 +556,7 @@ default release registry). Host contract:
|
|
|
542
556
|
[HOST_CONTRACT.md](../../runtime/harness/HOST_CONTRACT.md). Vocabulary (turn loop vs
|
|
543
557
|
workflow Loop): [runtime/harness/src/CONTEXT.md](../../runtime/harness/src/CONTEXT.md).
|
|
544
558
|
|
|
545
|
-
Use `session.observe({ cursor, signal })` for resumable canonical events, `session.inspect()` for waiting/uncertain state, and `approve`, `respond
|
|
559
|
+
Use `session.observe({ cursor, signal })` for resumable canonical events, `session.inspect()` for waiting/uncertain state, and `approve`, `respond` or `cancel` to answer or stop a turn. `cancel` takes an idempotency key; `approve(interactionId, approved)` and `respond(interactionId, value)` make one of their own when given none, so give them a stable one (`{ idempotencyKey }`) when you may retry. Retry the same semantic command with the same key. `ownerUserId` must come from trusted server authentication. Application credentials are not browser credentials; browser applications need an authorized backend. Definition authoring is browser-bundleable.
|
|
546
560
|
|
|
547
561
|
Connection defaults follow `resolveConnection` (options → environment → Project
|
|
548
562
|
link). Agents do not hash the manifest; a session pins the manifest version its Agent runs
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* upload its files by hash, and send the folder, which the Runtime compiles again and keeps as the
|
|
20
20
|
* version's source.
|
|
21
21
|
*/
|
|
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";
|
|
22
|
+
import { AGENT_MANIFEST_V8_SCHEMA_VERSION, type AgentManifestPlan, type AgentManifestSource, type AgentManifestSourceDiff, type AgentManifestV8, type AgentMemorySettings, type AgentManifestVersions, type AgentManifestView, type DeleteAgentManifestResponse, type RuntimeSection, type SaveAgentManifestResponse } from "@nylorun/core/contracts";
|
|
23
23
|
import { type AgentManifest, type BuiltAgent, type BuiltWorkflow, type JsonObject, type SkillFileSource, type WorkflowManifest } from "@nylorun/core/define";
|
|
24
24
|
import { type PackedFolder } from "./folder.js";
|
|
25
25
|
import { type Transport } from "./http.js";
|
|
@@ -136,6 +136,13 @@ export declare class AgentManifestsClient {
|
|
|
136
136
|
sourceFile(name: string, version: number, path: string, options?: {
|
|
137
137
|
signal?: AbortSignal;
|
|
138
138
|
}): Promise<Uint8Array>;
|
|
139
|
+
/**
|
|
140
|
+
* What changed from version `from`'s source to version `to`'s: the files added, removed and
|
|
141
|
+
* changed by path and hash. A version saved as JSON has no source (`404`).
|
|
142
|
+
*/
|
|
143
|
+
sourceDiff(name: string, from: number, to: number, options?: {
|
|
144
|
+
signal?: AbortSignal;
|
|
145
|
+
}): Promise<AgentManifestSourceDiff>;
|
|
139
146
|
/** The latest version of manifest `name`, or version `version`. */
|
|
140
147
|
get(name: string, version?: number, options?: {
|
|
141
148
|
signal?: AbortSignal;
|
package/dist/agent-manifests.js
CHANGED
|
@@ -262,6 +262,14 @@ export class AgentManifestsClient {
|
|
|
262
262
|
});
|
|
263
263
|
return new Uint8Array(await response.arrayBuffer());
|
|
264
264
|
}
|
|
265
|
+
/**
|
|
266
|
+
* What changed from version `from`'s source to version `to`'s: the files added, removed and
|
|
267
|
+
* changed by path and hash. A version saved as JSON has no source (`404`).
|
|
268
|
+
*/
|
|
269
|
+
async sourceDiff(name, from, to, options = {}) {
|
|
270
|
+
const query = new URLSearchParams({ from: String(from), to: String(to) });
|
|
271
|
+
return this.transport.json(`${this.path(name)}/source/diff?${query}`, "GET", undefined, options.signal);
|
|
272
|
+
}
|
|
265
273
|
/** The latest version of manifest `name`, or version `version`. */
|
|
266
274
|
async get(name, version, options = {}) {
|
|
267
275
|
return this.transport.json(`${this.path(name)}${version === undefined ? "" : `/versions/${version}`}`, "GET", undefined, options.signal);
|
package/dist/compose.d.ts
CHANGED
|
@@ -6,6 +6,11 @@ export declare class ComposeError extends Error {
|
|
|
6
6
|
readonly path: string;
|
|
7
7
|
constructor(path: string, message: string);
|
|
8
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* The warning every three-file folder gets (D80): agent folders replace it. It is still read, and
|
|
11
|
+
* removed in a later release.
|
|
12
|
+
*/
|
|
13
|
+
export declare function threeFileDeprecation(dir: string): string;
|
|
9
14
|
/**
|
|
10
15
|
* Composes the agent manifest in `dir` (its `agent.manifest.json`, or `options.file`): the v8
|
|
11
16
|
* document to save, the bytes of every file it names by hash (skills, plugins' skills, runtime
|
package/dist/compose.js
CHANGED
|
@@ -16,10 +16,13 @@
|
|
|
16
16
|
* `{ path, files }`.
|
|
17
17
|
*
|
|
18
18
|
* Each path is relative to the file that names it. Node only: it reads the files.
|
|
19
|
+
*
|
|
20
|
+
* Deprecated (D80): agent folders (`AGENT.md`) replace the three files, and every composed
|
|
21
|
+
* manifest carries a warning naming `nylorun migrate folder`. It is still read.
|
|
19
22
|
*/
|
|
20
23
|
import { existsSync, statSync } from "node:fs";
|
|
21
24
|
import { readdir, readFile } from "node:fs/promises";
|
|
22
|
-
import { dirname, join, relative, resolve } from "node:path";
|
|
25
|
+
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
23
26
|
import { AGENT_MANIFEST_V8_SCHEMA_VERSION, AgentManifestFileSchema, AgentManifestV8Schema, HarnessManifestFileSchema, RuntimeManifestFileSchema, WorkflowHarnessSectionSchema, } from "@nylorun/core/contracts";
|
|
24
27
|
import { sectionGaps } from "@nylorun/core/define";
|
|
25
28
|
import { requirementGaps } from "./agent-manifests.js";
|
|
@@ -198,6 +201,15 @@ function agentHarness(file, base, where, files, warnings) {
|
|
|
198
201
|
...(file.memory === undefined ? {} : { memory: file.memory }),
|
|
199
202
|
};
|
|
200
203
|
}
|
|
204
|
+
/**
|
|
205
|
+
* The warning every three-file folder gets (D80): agent folders replace it. It is still read, and
|
|
206
|
+
* removed in a later release.
|
|
207
|
+
*/
|
|
208
|
+
export function threeFileDeprecation(dir) {
|
|
209
|
+
const inside = relative(process.cwd(), dir);
|
|
210
|
+
const where = inside === "" ? "." : inside.startsWith("..") || isAbsolute(inside) ? dir : inside;
|
|
211
|
+
return `${AGENT_MANIFEST_FILE} (the three-file format) is deprecated: nylorun migrate folder ${where} writes it as an agent folder (AGENT.md), which saves the same manifest. It is still read until a later release.`;
|
|
212
|
+
}
|
|
201
213
|
/**
|
|
202
214
|
* Composes the agent manifest in `dir` (its `agent.manifest.json`, or `options.file`): the v8
|
|
203
215
|
* document to save, the bytes of every file it names by hash (skills, plugins' skills, runtime
|
|
@@ -210,7 +222,7 @@ export async function composeManifest(dir, options = {}) {
|
|
|
210
222
|
const base = dirname(agentFile);
|
|
211
223
|
const authored = await readFileAs(agentFile, AgentManifestFileSchema, relativeTo(agentFile));
|
|
212
224
|
const files = new Map();
|
|
213
|
-
const warnings = [];
|
|
225
|
+
const warnings = [threeFileDeprecation(base)];
|
|
214
226
|
const runtime = authored.runtime === undefined ? undefined : await runtimeSection(authored.runtime, base, agentFile, files, warnings);
|
|
215
227
|
let harness;
|
|
216
228
|
let requires;
|
package/dist/folder-check.d.ts
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { type CompiledAgentFolder } from "@nylorun/core/folder";
|
|
7
7
|
import type { PackedFolder } from "./folder.js";
|
|
8
|
+
/** The agent folder of a manifest (track T7): what `nylorun export --folder` writes. */
|
|
9
|
+
export { folderOfManifest, type FolderOfManifest, type FolderOfManifestOptions } from "@nylorun/core/folder";
|
|
8
10
|
/**
|
|
9
11
|
* Compiles a packed folder offline, as the Runtime will: the manifest when it compiles, and every
|
|
10
12
|
* diagnostic either way. With `name`, the name it will be saved as, `AGENT.md`'s must match.
|
package/dist/folder-check.js
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* module only when a folder is sent.
|
|
5
5
|
*/
|
|
6
6
|
import { AGENT_FOLDER_LIMITS, compileAgentFolder, folderTextPaths, } from "@nylorun/core/folder";
|
|
7
|
+
/** The agent folder of a manifest (track T7): what `nylorun export --folder` writes. */
|
|
8
|
+
export { folderOfManifest } from "@nylorun/core/folder";
|
|
7
9
|
/**
|
|
8
10
|
* Compiles a packed folder offline, as the Runtime will: the manifest when it compiles, and every
|
|
9
11
|
* diagnostic either way. With `name`, the name it will be saved as, `AGENT.md`'s must match.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `.nyloignore` (track T7): the files `packFolder` leaves out of an agent folder, in gitignore
|
|
3
|
+
* syntax. Blank lines and `#` comments are skipped; `!` re-includes what an earlier line left out;
|
|
4
|
+
* a trailing `/` matches directories only; a pattern with a `/` before its end is anchored at the
|
|
5
|
+
* folder, and one without matches a name at any depth; `*`, `?`, `[…]` and `**` glob as in git.
|
|
6
|
+
* The last line that matches decides, and nothing below a directory left out comes back, as in
|
|
7
|
+
* git. Pure: no Node module, so it also reads a project's `.gitignore`.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* A matcher for an ignore file's text: whether `path` (relative, `/` between segments) is left
|
|
11
|
+
* out, a directory when `directory` is true. A path below a directory left out is left out too.
|
|
12
|
+
*/
|
|
13
|
+
export declare function ignoreMatcher(text: string): (path: string, directory?: boolean) => boolean;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `.nyloignore` (track T7): the files `packFolder` leaves out of an agent folder, in gitignore
|
|
3
|
+
* syntax. Blank lines and `#` comments are skipped; `!` re-includes what an earlier line left out;
|
|
4
|
+
* a trailing `/` matches directories only; a pattern with a `/` before its end is anchored at the
|
|
5
|
+
* folder, and one without matches a name at any depth; `*`, `?`, `[…]` and `**` glob as in git.
|
|
6
|
+
* The last line that matches decides, and nothing below a directory left out comes back, as in
|
|
7
|
+
* git. Pure: no Node module, so it also reads a project's `.gitignore`.
|
|
8
|
+
*/
|
|
9
|
+
/** A glob's body as a regular expression's source: `*` and `?` stop at `/`, `**` crosses it. */
|
|
10
|
+
function globSource(glob) {
|
|
11
|
+
let source = "";
|
|
12
|
+
for (let index = 0; index < glob.length; index += 1) {
|
|
13
|
+
const char = glob[index];
|
|
14
|
+
if (char === "\\" && index + 1 < glob.length) {
|
|
15
|
+
source += escapeChar(glob[++index]);
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
if (char === "*") {
|
|
19
|
+
if (glob[index + 1] === "*") {
|
|
20
|
+
const before = index === 0 || glob[index - 1] === "/";
|
|
21
|
+
const after = index + 2 === glob.length || glob[index + 2] === "/";
|
|
22
|
+
if (before && after) {
|
|
23
|
+
if (index + 2 === glob.length)
|
|
24
|
+
source += ".*"; // `a/**`: everything inside
|
|
25
|
+
else {
|
|
26
|
+
source += "(?:.*/)?"; // `**/` and `/**/`: zero or more directories
|
|
27
|
+
index += 1; // the `/` after `**`
|
|
28
|
+
}
|
|
29
|
+
index += 1;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
source += "[^/]*";
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (char === "?") {
|
|
37
|
+
source += "[^/]";
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (char === "[") {
|
|
41
|
+
const end = glob.indexOf("]", index + 2);
|
|
42
|
+
if (end > 0) {
|
|
43
|
+
let body = glob.slice(index + 1, end);
|
|
44
|
+
if (body.startsWith("!"))
|
|
45
|
+
body = `^${body.slice(1)}`;
|
|
46
|
+
source += `[${body.replace(/\\/g, "\\\\")}]`;
|
|
47
|
+
index = end;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
source += escapeChar(char);
|
|
52
|
+
}
|
|
53
|
+
return source;
|
|
54
|
+
}
|
|
55
|
+
const escapeChar = (char) => char.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&");
|
|
56
|
+
/** The rules of an ignore file's text. */
|
|
57
|
+
function parseIgnore(text) {
|
|
58
|
+
const rules = [];
|
|
59
|
+
for (const raw of text.split(/\r?\n/)) {
|
|
60
|
+
// Trailing spaces go, unless the last is escaped.
|
|
61
|
+
let line = raw.replace(/(?<!\\)\s+$/, "");
|
|
62
|
+
if (line === "" || line.startsWith("#"))
|
|
63
|
+
continue;
|
|
64
|
+
let negate = false;
|
|
65
|
+
if (line.startsWith("!")) {
|
|
66
|
+
negate = true;
|
|
67
|
+
line = line.slice(1);
|
|
68
|
+
}
|
|
69
|
+
else if (line.startsWith("\\!") || line.startsWith("\\#"))
|
|
70
|
+
line = line.slice(1);
|
|
71
|
+
const directoryOnly = line.endsWith("/");
|
|
72
|
+
if (directoryOnly)
|
|
73
|
+
line = line.slice(0, -1);
|
|
74
|
+
if (line === "")
|
|
75
|
+
continue;
|
|
76
|
+
const anchored = line.includes("/");
|
|
77
|
+
if (line.startsWith("/"))
|
|
78
|
+
line = line.slice(1);
|
|
79
|
+
const body = globSource(line);
|
|
80
|
+
rules.push({
|
|
81
|
+
negate,
|
|
82
|
+
directoryOnly,
|
|
83
|
+
pattern: new RegExp(anchored ? `^${body}$` : `(?:^|/)${body}$`),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return rules;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* A matcher for an ignore file's text: whether `path` (relative, `/` between segments) is left
|
|
90
|
+
* out, a directory when `directory` is true. A path below a directory left out is left out too.
|
|
91
|
+
*/
|
|
92
|
+
export function ignoreMatcher(text) {
|
|
93
|
+
const rules = parseIgnore(text);
|
|
94
|
+
const decide = (path, directory) => {
|
|
95
|
+
let ignored = false;
|
|
96
|
+
for (const rule of rules) {
|
|
97
|
+
if (rule.directoryOnly && !directory)
|
|
98
|
+
continue;
|
|
99
|
+
if (rule.negate === ignored && rule.pattern.test(path))
|
|
100
|
+
ignored = !rule.negate;
|
|
101
|
+
}
|
|
102
|
+
return ignored;
|
|
103
|
+
};
|
|
104
|
+
return (path, directory = false) => {
|
|
105
|
+
if (rules.length === 0)
|
|
106
|
+
return false;
|
|
107
|
+
const segments = path.split("/");
|
|
108
|
+
for (let depth = 1; depth < segments.length; depth += 1)
|
|
109
|
+
if (decide(segments.slice(0, depth).join("/"), true))
|
|
110
|
+
return true;
|
|
111
|
+
return decide(path, directory);
|
|
112
|
+
};
|
|
113
|
+
}
|
package/dist/folder-pack.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { type PackedFolder } from "./folder.js";
|
|
2
|
+
/** The file, at a folder's top, that names what packing leaves out (gitignore syntax). */
|
|
3
|
+
export declare const FOLDER_IGNORE_FILE = ".nyloignore";
|
|
2
4
|
/** Whether `dir` holds an `AGENT.md`. */
|
|
3
5
|
export declare function isAgentFolder(dir: string): Promise<boolean>;
|
|
4
6
|
/** Lists the agent folder at `dir` with each file's size and SHA-256. */
|
package/dist/folder-pack.js
CHANGED
|
@@ -1,24 +1,110 @@
|
|
|
1
1
|
/**
|
|
2
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
|
|
4
|
-
*
|
|
3
|
+
* and reads its bytes on demand. It skips `.git`, `.hg`, `.svn` and `node_modules`, what the
|
|
4
|
+
* folder's `.nyloignore` names (gitignore rules, `folder-ignore.ts`; track T7), and follows no
|
|
5
|
+
* symbolic link.
|
|
6
|
+
*
|
|
7
|
+
* `extends` in `runtime.json` (track T7, design §3.9) names a shared JSON file, inside the folder
|
|
8
|
+
* or outside it, by a path relative to the file that names it. The packed `runtime.json` is the
|
|
9
|
+
* merge, the folder's own keys winning (objects merge, arrays replace), so every saved version is
|
|
10
|
+
* self-contained and its source is what was sent; `.nylorun/origin.json` in the pack names the
|
|
11
|
+
* files it was merged from. A chain of `extends` resolves in turn; a cycle, a missing file or
|
|
12
|
+
* one that is not a JSON object is a `FolderError`. Node only: it reads the folder.
|
|
5
13
|
*/
|
|
6
14
|
import { createHash } from "node:crypto";
|
|
7
15
|
import { readdir, readFile, stat } from "node:fs/promises";
|
|
8
|
-
import { join, relative, resolve, sep } from "node:path";
|
|
16
|
+
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
9
17
|
import { AGENT_FOLDER_LIMITS } from "@nylorun/core/folder";
|
|
10
18
|
import { AGENT_FOLDER_FILE, FolderError } from "./folder.js";
|
|
19
|
+
import { ignoreMatcher } from "./folder-ignore.js";
|
|
11
20
|
const SKIPPED_DIRECTORIES = new Set([".git", ".hg", ".svn", "node_modules"]);
|
|
21
|
+
/** The file, at a folder's top, that names what packing leaves out (gitignore syntax). */
|
|
22
|
+
export const FOLDER_IGNORE_FILE = ".nyloignore";
|
|
23
|
+
const RUNTIME_FILE = "runtime.json";
|
|
24
|
+
const ORIGIN_FILE = ".nylorun/origin.json";
|
|
12
25
|
/** Whether `dir` holds an `AGENT.md`. */
|
|
13
26
|
export async function isAgentFolder(dir) {
|
|
14
27
|
return stat(join(dir, AGENT_FOLDER_FILE)).then((found) => found.isFile(), () => false);
|
|
15
28
|
}
|
|
29
|
+
const sha256 = (data) => createHash("sha256").update(data).digest("hex");
|
|
30
|
+
const isObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
31
|
+
const byPath = (a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
32
|
+
/** `over` merged onto `under`: `over`'s keys win, objects merge, arrays and the rest replace. */
|
|
33
|
+
function merge(under, over) {
|
|
34
|
+
const merged = { ...under };
|
|
35
|
+
for (const [key, value] of Object.entries(over)) {
|
|
36
|
+
const below = merged[key];
|
|
37
|
+
merged[key] = isObject(below) && isObject(value) ? merge(below, value) : value;
|
|
38
|
+
}
|
|
39
|
+
return merged;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* `runtime.json` with its `extends` resolved, and each file it was merged from (absolute paths,
|
|
43
|
+
* nearest first); undefined when it says no `extends`, or is not a JSON object (the compiler
|
|
44
|
+
* reports that with its position).
|
|
45
|
+
*/
|
|
46
|
+
async function resolveExtends(root, text) {
|
|
47
|
+
let local;
|
|
48
|
+
try {
|
|
49
|
+
local = JSON.parse(text);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
if (!isObject(local) || !("extends" in local))
|
|
55
|
+
return undefined;
|
|
56
|
+
const shown = (file) => relative(root, file).split(sep).join("/");
|
|
57
|
+
const chain = [join(root, RUNTIME_FILE)];
|
|
58
|
+
const layers = [];
|
|
59
|
+
let current = local;
|
|
60
|
+
for (;;) {
|
|
61
|
+
const { extends: target, ...own } = current;
|
|
62
|
+
// The extended file's `$schema` names that file's kind; the folder's own file has none.
|
|
63
|
+
if (layers.length > 0)
|
|
64
|
+
delete own.$schema;
|
|
65
|
+
layers.push(own);
|
|
66
|
+
if (target === undefined)
|
|
67
|
+
break;
|
|
68
|
+
const from = chain.at(-1);
|
|
69
|
+
if (typeof target !== "string" || target.trim() === "")
|
|
70
|
+
throw new FolderError(root, `${shown(from)}: extends is the path of a JSON file, such as "../../platform/pod.runtime.json"`);
|
|
71
|
+
const file = resolve(dirname(from), target);
|
|
72
|
+
if (chain.includes(file))
|
|
73
|
+
throw new FolderError(root, `${RUNTIME_FILE}: extends makes a cycle: ${[...chain, file].map(shown).join(" → ")}`);
|
|
74
|
+
let body;
|
|
75
|
+
try {
|
|
76
|
+
body = await readFile(file, "utf8");
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
const code = error.code;
|
|
80
|
+
throw new FolderError(root, code === "ENOENT"
|
|
81
|
+
? `${shown(from)}: extends ${target}, which does not exist (${file})`
|
|
82
|
+
: `${shown(from)}: cannot read ${target}, which it extends: ${code ?? error}`);
|
|
83
|
+
}
|
|
84
|
+
let parsed;
|
|
85
|
+
try {
|
|
86
|
+
parsed = JSON.parse(body);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
throw new FolderError(root, `${shown(file)}, which ${shown(from)} extends, is not JSON: ${error.message}`);
|
|
90
|
+
}
|
|
91
|
+
if (!isObject(parsed))
|
|
92
|
+
throw new FolderError(root, `${shown(file)}, which ${shown(from)} extends, is not a JSON object`);
|
|
93
|
+
chain.push(file);
|
|
94
|
+
current = parsed;
|
|
95
|
+
}
|
|
96
|
+
// The farthest file first, each nearer one over it.
|
|
97
|
+
const value = layers.reduceRight((under, layer) => merge(under, layer), {});
|
|
98
|
+
return { value, from: chain.slice(1) };
|
|
99
|
+
}
|
|
16
100
|
/** Lists the agent folder at `dir` with each file's size and SHA-256. */
|
|
17
101
|
export async function packFolder(dir) {
|
|
18
102
|
const root = resolve(dir);
|
|
19
103
|
const entries = [];
|
|
20
104
|
const files = new Map();
|
|
21
105
|
const skipped = [];
|
|
106
|
+
const ignoreText = await readFile(join(root, FOLDER_IGNORE_FILE), "utf8").catch(() => undefined);
|
|
107
|
+
const ignored = ignoreText === undefined ? () => false : ignoreMatcher(ignoreText);
|
|
22
108
|
let bytes = 0;
|
|
23
109
|
const walk = async (at) => {
|
|
24
110
|
const children = await readdir(at, { withFileTypes: true }).catch((error) => {
|
|
@@ -28,10 +114,12 @@ export async function packFolder(dir) {
|
|
|
28
114
|
const full = join(at, child.name);
|
|
29
115
|
const path = relative(root, full).split(sep).join("/");
|
|
30
116
|
if (child.isDirectory()) {
|
|
31
|
-
if (!SKIPPED_DIRECTORIES.has(child.name))
|
|
117
|
+
if (!SKIPPED_DIRECTORIES.has(child.name) && !ignored(path, true))
|
|
32
118
|
await walk(full);
|
|
33
119
|
continue;
|
|
34
120
|
}
|
|
121
|
+
if (ignored(path))
|
|
122
|
+
continue;
|
|
35
123
|
if (child.isSymbolicLink()) {
|
|
36
124
|
skipped.push(`${path}: a symbolic link, not followed`);
|
|
37
125
|
continue;
|
|
@@ -44,11 +132,33 @@ export async function packFolder(dir) {
|
|
|
44
132
|
bytes += data.byteLength;
|
|
45
133
|
if (bytes > AGENT_FOLDER_LIMITS.totalBytes)
|
|
46
134
|
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:
|
|
135
|
+
entries.push({ path, size: data.byteLength, sha256: sha256(data) });
|
|
48
136
|
files.set(path, { size: data.byteLength, read: () => readFile(full) });
|
|
49
137
|
}
|
|
50
138
|
};
|
|
51
139
|
await walk(root);
|
|
52
|
-
|
|
53
|
-
|
|
140
|
+
// `extends`: the packed runtime.json is the merge, and origin.json names what it came from.
|
|
141
|
+
let extended;
|
|
142
|
+
const runtime = files.get(RUNTIME_FILE);
|
|
143
|
+
if (runtime && runtime.size <= AGENT_FOLDER_LIMITS.textFileBytes) {
|
|
144
|
+
const resolved = await resolveExtends(root, new TextDecoder().decode(await runtime.read()));
|
|
145
|
+
if (resolved) {
|
|
146
|
+
extended = resolved.from;
|
|
147
|
+
const put = (path, text) => {
|
|
148
|
+
const data = new TextEncoder().encode(text);
|
|
149
|
+
const at = entries.findIndex((entry) => entry.path === path);
|
|
150
|
+
const entry = { path, size: data.byteLength, sha256: sha256(data) };
|
|
151
|
+
if (at < 0)
|
|
152
|
+
entries.push(entry);
|
|
153
|
+
else
|
|
154
|
+
entries[at] = entry;
|
|
155
|
+
files.set(path, { size: data.byteLength, read: async () => data });
|
|
156
|
+
};
|
|
157
|
+
put(RUNTIME_FILE, `${JSON.stringify(resolved.value, null, 2)}\n`);
|
|
158
|
+
const relativeTo = (file) => relative(root, file).split(sep).join("/");
|
|
159
|
+
put(ORIGIN_FILE, `${JSON.stringify({ [RUNTIME_FILE]: { extends: resolved.from.map(relativeTo) } }, null, 2)}\n`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
entries.sort(byPath);
|
|
163
|
+
return { dir: root, entries, files, skipped, ...(extended ? { extended } : {}) };
|
|
54
164
|
}
|
package/dist/folder.d.ts
CHANGED
|
@@ -22,8 +22,16 @@ export interface PackedFolder {
|
|
|
22
22
|
readonly files: ReadonlyMap<string, SkillFileSource>;
|
|
23
23
|
/** What packing left out, and why (a symbolic link). */
|
|
24
24
|
readonly skipped: readonly string[];
|
|
25
|
+
/**
|
|
26
|
+
* The files `runtime.json` was merged from (its `extends`, nearest first), as absolute paths;
|
|
27
|
+
* absent when it extends none.
|
|
28
|
+
*/
|
|
29
|
+
readonly extended?: readonly string[];
|
|
25
30
|
}
|
|
26
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* A folder that cannot be packed: too many files, more bytes than an agent folder holds, or an
|
|
33
|
+
* `extends` in `runtime.json` that does not resolve.
|
|
34
|
+
*/
|
|
27
35
|
export declare class FolderError extends Error {
|
|
28
36
|
readonly dir: string;
|
|
29
37
|
constructor(dir: string, message: string);
|
package/dist/folder.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/** The file that makes a folder an agent folder. */
|
|
2
2
|
export const AGENT_FOLDER_FILE = "AGENT.md";
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* A folder that cannot be packed: too many files, more bytes than an agent folder holds, or an
|
|
5
|
+
* `extends` in `runtime.json` that does not resolve.
|
|
6
|
+
*/
|
|
4
7
|
export class FolderError extends Error {
|
|
5
8
|
dir;
|
|
6
9
|
constructor(dir, message) {
|
package/dist/index.d.ts
CHANGED
|
@@ -3,11 +3,11 @@ export type { ProtocolFeature, ProtocolRange, Compatibility, ErrorCode, } from "
|
|
|
3
3
|
export { checkHealth, describeIncompatibility, parseProtocolRange, } from "@nylorun/core/transport";
|
|
4
4
|
export type { HealthCheck, Incompatibility } from "@nylorun/core/transport";
|
|
5
5
|
export { Agent, AgentBuilder } from "./builder.js";
|
|
6
|
-
export { AgentBuildError, AgentLifecycleError, VerdictSchema, isVerdict, WorkflowBuildError, isBuiltWorkflow, flow, tool, http, defineSchema, ToolError, } from "@nylorun/core/define";
|
|
6
|
+
export { AgentBuildError, AgentLifecycleError, VerdictSchema, isVerdict, WorkflowBuildError, isBuiltWorkflow, flow, tool, http, defineSchema, ToolError, harnessSectionOf, sectionGaps, } from "@nylorun/core/define";
|
|
7
7
|
export type { FlowAgentBuilder, Flow, Named, LoopOptions, StageOptions, HttpTarget, } from "@nylorun/core/define";
|
|
8
8
|
export type { AgentOptions, AgentManifest, BuiltAgent, BuiltWorkflow, Verdict, WorkflowBinding, WorkflowManifest, ToolDefinition, ToolExecutionContext, ToolOutcome, JsonValue, JsonObject, } from "@nylorun/core/define";
|
|
9
|
-
export type { SessionPage, SessionListItem, SessionManifestView, HistoryPage, SessionUsageTotals, CostSplit, ModelCall, ModelCallsPage, AgentPage, ToolCall, ToolCallsPage, LiveEvent, SessionEvent, SessionEventOf, EventType, SessionCommand, SubjectScope, AgentView, AgentState, AgentAttention, AgentEvent, AgentEventType, EgressRunningTurn, EnvironmentKind, EnvironmentRecipe, EnvironmentSecretSlot, AgentSecretView, AgentSnapshot, AgentRestore, AgentDiskVersions, RestoreAgentRequest, AgentForkPlan, AgentMovePlan, RestartAgentRequest, ForkAgentRequest, ForkAgentResponse, DeleteAgentResponse, PutAgentRequest, PutAgentSecretRequest, AgentManifestV8, AgentManifestFile, HarnessManifestFile, RuntimeManifestFile, RuntimeSection, AgentManifestChange, AgentManifestWarning, AgentManifestPlan, AgentManifestView, AgentManifestVersions, SaveAgentManifestResponse, DeleteAgentManifestResponse, MessagePart, ArtifactView, ArtifactKind, ArtifactListItem, ArtifactPage, ArtifactVersionView, ArtifactLink, UploadArtifactResponse, DefinitionFileView, ArtifactTree, ArtifactDiff, FolderEntry, } from "@nylorun/core/contracts";
|
|
10
|
-
export { AGENT_EVENT_TYPES, AGENT_STATES, COMPUTE_STATES, RUNTIME_LIMIT_DEFAULTS, SUBJECT_SCOPES, } from "@nylorun/core/contracts";
|
|
9
|
+
export type { SessionPage, SessionListItem, SessionManifestView, HistoryPage, SessionUsageTotals, SessionModelChoice, SessionModelView, CostSplit, ModelCall, ModelCallsPage, AgentPage, ToolCall, ToolCallsPage, LiveEvent, SessionEvent, SessionEventOf, EventType, SessionCommand, SubjectScope, AgentView, AgentState, AgentAttention, AgentEvent, AgentEventType, EgressRunningTurn, EnvironmentKind, EnvironmentRecipe, EnvironmentSecretSlot, AgentSecretView, AgentSnapshot, AgentRestore, AgentDiskVersions, RestoreAgentRequest, AgentForkPlan, AgentMovePlan, RestartAgentRequest, ForkAgentRequest, ForkAgentResponse, DeleteAgentResponse, PutAgentRequest, PutAgentSecretRequest, AgentManifestV8, AgentManifestFile, HarnessManifestFile, RuntimeManifestFile, RuntimeSection, AgentManifestChange, AgentManifestWarning, AgentManifestPlan, AgentManifestSource, AgentManifestSourceDiff, AgentManifestSourceSummary, AgentFolderDiagnostic, AgentManifestView, AgentManifestVersions, SaveAgentManifestResponse, DeleteAgentManifestResponse, MessagePart, ArtifactView, ArtifactKind, ArtifactListItem, ArtifactPage, ArtifactVersionView, ArtifactLink, UploadArtifactResponse, DefinitionFileView, ArtifactTree, ArtifactDiff, FolderEntry, } from "@nylorun/core/contracts";
|
|
10
|
+
export { AGENT_EVENT_TYPES, AGENT_FOLDER_MEDIA_TYPE, AGENT_STATES, COMPUTE_STATES, RUNTIME_LIMIT_DEFAULTS, SUBJECT_SCOPES, } from "@nylorun/core/contracts";
|
|
11
11
|
export type { AgentHarnessSection, HarnessSection, HarnessSkill, HarnessMcpServer, WorkflowHarnessSection, } from "@nylorun/core";
|
|
12
12
|
export { AgentsClient, SessionClient, createClient } from "./client.js";
|
|
13
13
|
export { ArtifactsClient } from "./artifacts.js";
|
|
@@ -17,9 +17,10 @@ export type { ActAsOptions, AgentSource, SessionView, CommandOptions, CreateSess
|
|
|
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
19
|
export { AGENT_FOLDER_FILE, FolderError, FolderInvalidError, diagnosticLine } from "./folder.js";
|
|
20
|
-
export { checkFolder } from "./folder-check.js";
|
|
20
|
+
export { checkFolder, folderOfManifest, type FolderOfManifest, type FolderOfManifestOptions } from "./folder-check.js";
|
|
21
21
|
export type { PackedFolder } from "./folder.js";
|
|
22
|
-
export { packFolder, isAgentFolder } from "./folder-pack.js";
|
|
22
|
+
export { packFolder, isAgentFolder, FOLDER_IGNORE_FILE } from "./folder-pack.js";
|
|
23
|
+
export { ignoreMatcher } from "./folder-ignore.js";
|
|
23
24
|
export { AccessClient } from "./access.js";
|
|
24
25
|
export { resolveConnection, ConnectionError } from "./connection.js";
|
|
25
26
|
export type { ResolvedConnection } from "./connection.js";
|
package/dist/index.js
CHANGED
|
@@ -3,16 +3,20 @@ export { hashManifest, PROTOCOL_VERSION, PROTOCOL_FEATURES, HOST_PROTOCOL, DEFIN
|
|
|
3
3
|
// The `/health` compatibility probe every Runtime client shares (Studio's server uses it).
|
|
4
4
|
export { checkHealth, describeIncompatibility, parseProtocolRange, } from "@nylorun/core/transport";
|
|
5
5
|
export { Agent, AgentBuilder } from "./builder.js";
|
|
6
|
-
export { AgentBuildError, AgentLifecycleError, VerdictSchema, isVerdict, WorkflowBuildError, isBuiltWorkflow, flow, tool, http, defineSchema, ToolError,
|
|
7
|
-
|
|
6
|
+
export { AgentBuildError, AgentLifecycleError, VerdictSchema, isVerdict, WorkflowBuildError, isBuiltWorkflow, flow, tool, http, defineSchema, ToolError,
|
|
7
|
+
// The harness section `agentManifests.save` makes from a built agent or flow, and the gaps the
|
|
8
|
+
// Runtime refuses between a manifest's two sections, for code that builds a manifest itself.
|
|
9
|
+
harnessSectionOf, sectionGaps, } from "@nylorun/core/define";
|
|
10
|
+
export { AGENT_EVENT_TYPES, AGENT_FOLDER_MEDIA_TYPE, AGENT_STATES, COMPUTE_STATES, RUNTIME_LIMIT_DEFAULTS, SUBJECT_SCOPES, } from "@nylorun/core/contracts";
|
|
8
11
|
export { AgentsClient, SessionClient, createClient } from "./client.js";
|
|
9
12
|
export { ArtifactsClient } from "./artifacts.js";
|
|
10
13
|
export { FilesClient, definitionFileHash } from "./files.js";
|
|
11
14
|
export { AgentInstancesClient, AgentManifestsClient, AgentMemoriesClient, AgentSecretsClient, ManifestGapsError, agentManifest, requirementGaps, runtimeFiles, } from "./client.js";
|
|
12
15
|
export { composeManifest, manifestFolders, ComposeError, AGENT_MANIFEST_FILE } from "./compose.js";
|
|
13
16
|
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";
|
|
17
|
+
export { checkFolder, folderOfManifest } from "./folder-check.js";
|
|
18
|
+
export { packFolder, isAgentFolder, FOLDER_IGNORE_FILE } from "./folder-pack.js";
|
|
19
|
+
export { ignoreMatcher } from "./folder-ignore.js";
|
|
16
20
|
export { AccessClient } from "./access.js";
|
|
17
21
|
export { resolveConnection, ConnectionError } from "./connection.js";
|
|
18
22
|
export { RuntimeError, IncompatibleRuntimeError } from "./http.js";
|
package/dist/plugins/load.js
CHANGED
|
@@ -291,7 +291,8 @@ function realInside(root, candidate) {
|
|
|
291
291
|
return undefined;
|
|
292
292
|
try {
|
|
293
293
|
const real = realpathSync(candidate);
|
|
294
|
-
|
|
294
|
+
// Both sides real: a package under a symlinked directory (macOS's /var → /private/var) is inside itself.
|
|
295
|
+
return isInside(realpathSync(root), real) ? real : undefined;
|
|
295
296
|
}
|
|
296
297
|
catch {
|
|
297
298
|
return undefined;
|
package/dist/session-client.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* which resolves the connection from the environment or the Project link.
|
|
4
4
|
*/
|
|
5
5
|
import type { JsonValue, SandboxToolName } from "@nylorun/core/define";
|
|
6
|
-
import { type SessionItemsResponse, type SessionSummary, type SubjectScope, type MessageDelivery, type MessagePart, type SessionCommand } from "@nylorun/core/contracts";
|
|
6
|
+
import { type SessionModelChoice, type SessionItemsResponse, type SessionSummary, type SubjectScope, type MessageDelivery, type MessagePart, type SessionCommand } from "@nylorun/core/contracts";
|
|
7
7
|
import { SessionsReadClient } from "./reads.js";
|
|
8
8
|
import { AccessClient } from "./access.js";
|
|
9
9
|
import { AgentInstancesClient } from "./agents.js";
|
|
@@ -152,7 +152,8 @@ export declare class SessionClient {
|
|
|
152
152
|
cursor: string | null;
|
|
153
153
|
requestId: string;
|
|
154
154
|
}>;
|
|
155
|
-
|
|
155
|
+
/** Approve (or deny) a held tool call. Without an idempotency key, a new one is made. */
|
|
156
|
+
approve(interactionId: string, approved: boolean, options?: Partial<CommandOptions>): Promise<{
|
|
156
157
|
status: "accepted";
|
|
157
158
|
turnId: string | null;
|
|
158
159
|
cursor: string | null;
|
|
@@ -164,7 +165,8 @@ export declare class SessionClient {
|
|
|
164
165
|
cursor: string | null;
|
|
165
166
|
requestId: string;
|
|
166
167
|
}>;
|
|
167
|
-
|
|
168
|
+
/** Answer a question the agent asked. Without an idempotency key, a new one is made. */
|
|
169
|
+
respond(interactionId: string, value: JsonValue, options?: Partial<CommandOptions>): Promise<{
|
|
168
170
|
status: "accepted";
|
|
169
171
|
turnId: string | null;
|
|
170
172
|
cursor: string | null;
|
|
@@ -202,6 +204,72 @@ export declare class SessionClient {
|
|
|
202
204
|
manifestHash: string;
|
|
203
205
|
manifest: Record<string, unknown>;
|
|
204
206
|
}>;
|
|
207
|
+
/**
|
|
208
|
+
* The model the session's next turn uses (Host feature `session-model`): its own while its
|
|
209
|
+
* provider is connected, else the Tenant's.
|
|
210
|
+
*/
|
|
211
|
+
model(options?: {
|
|
212
|
+
signal?: AbortSignal;
|
|
213
|
+
}): Promise<{
|
|
214
|
+
configured: false;
|
|
215
|
+
choice?: {
|
|
216
|
+
provider: string;
|
|
217
|
+
model: string;
|
|
218
|
+
} | undefined;
|
|
219
|
+
} | {
|
|
220
|
+
configured: true;
|
|
221
|
+
source: "session" | "tenant";
|
|
222
|
+
provider: string;
|
|
223
|
+
model: string;
|
|
224
|
+
baseUrl?: string | undefined;
|
|
225
|
+
choice?: {
|
|
226
|
+
provider: string;
|
|
227
|
+
model: string;
|
|
228
|
+
} | undefined;
|
|
229
|
+
}>;
|
|
230
|
+
/**
|
|
231
|
+
* Chooses one of a connected provider's models for this session's turns; the Tenant's model and
|
|
232
|
+
* other sessions' stay as they are. Refused while a turn runs or waits (`409 session_busy`).
|
|
233
|
+
*/
|
|
234
|
+
setModel(choice: SessionModelChoice, options?: {
|
|
235
|
+
signal?: AbortSignal;
|
|
236
|
+
}): Promise<{
|
|
237
|
+
configured: false;
|
|
238
|
+
choice?: {
|
|
239
|
+
provider: string;
|
|
240
|
+
model: string;
|
|
241
|
+
} | undefined;
|
|
242
|
+
} | {
|
|
243
|
+
configured: true;
|
|
244
|
+
source: "session" | "tenant";
|
|
245
|
+
provider: string;
|
|
246
|
+
model: string;
|
|
247
|
+
baseUrl?: string | undefined;
|
|
248
|
+
choice?: {
|
|
249
|
+
provider: string;
|
|
250
|
+
model: string;
|
|
251
|
+
} | undefined;
|
|
252
|
+
}>;
|
|
253
|
+
/** Clears the session's own model: its next turns use the Tenant's. */
|
|
254
|
+
clearModel(options?: {
|
|
255
|
+
signal?: AbortSignal;
|
|
256
|
+
}): Promise<{
|
|
257
|
+
configured: false;
|
|
258
|
+
choice?: {
|
|
259
|
+
provider: string;
|
|
260
|
+
model: string;
|
|
261
|
+
} | undefined;
|
|
262
|
+
} | {
|
|
263
|
+
configured: true;
|
|
264
|
+
source: "session" | "tenant";
|
|
265
|
+
provider: string;
|
|
266
|
+
model: string;
|
|
267
|
+
baseUrl?: string | undefined;
|
|
268
|
+
choice?: {
|
|
269
|
+
provider: string;
|
|
270
|
+
model: string;
|
|
271
|
+
} | undefined;
|
|
272
|
+
}>;
|
|
205
273
|
usage(options?: {
|
|
206
274
|
turnId?: string;
|
|
207
275
|
signal?: AbortSignal;
|
package/dist/session-client.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SANDBOX_TOOL_NAMES, isSandboxToolName } from "@nylorun/core/define";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { SCOPES_HEADER, SUBJECT_HEADER } from "@nylorun/core/compatibility";
|
|
4
|
-
import { SessionManifestViewSchema, SessionUsageTotalsSchema, ModelCallsPageSchema, ToolCallsPageSchema, TurnTraceSchema, parseSessionEvent, parseSubjectHeaders, } from "@nylorun/core/contracts";
|
|
4
|
+
import { SessionManifestViewSchema, SessionModelViewSchema, SessionUsageTotalsSchema, ModelCallsPageSchema, ToolCallsPageSchema, TurnTraceSchema, parseSessionEvent, parseSubjectHeaders, } from "@nylorun/core/contracts";
|
|
5
5
|
import { SessionsReadClient } from "./reads.js";
|
|
6
6
|
import { AccessClient } from "./access.js";
|
|
7
7
|
import { AgentInstancesClient } from "./agents.js";
|
|
@@ -168,22 +168,24 @@ export class SessionClient {
|
|
|
168
168
|
...(options.delivery ? { delivery: options.delivery } : {}),
|
|
169
169
|
}, options.signal);
|
|
170
170
|
}
|
|
171
|
-
|
|
171
|
+
/** Approve (or deny) a held tool call. Without an idempotency key, a new one is made. */
|
|
172
|
+
approve(interactionId, approved, options = {}) {
|
|
172
173
|
return this.command({
|
|
173
174
|
type: "approve",
|
|
174
175
|
interactionId,
|
|
175
176
|
approved,
|
|
176
177
|
requestId: options.requestId ?? id(),
|
|
177
|
-
idempotencyKey: options.idempotencyKey,
|
|
178
|
+
idempotencyKey: options.idempotencyKey ?? id(),
|
|
178
179
|
}, options.signal);
|
|
179
180
|
}
|
|
180
|
-
|
|
181
|
+
/** Answer a question the agent asked. Without an idempotency key, a new one is made. */
|
|
182
|
+
respond(interactionId, value, options = {}) {
|
|
181
183
|
return this.command({
|
|
182
184
|
type: "respond",
|
|
183
185
|
interactionId,
|
|
184
186
|
value,
|
|
185
187
|
requestId: options.requestId ?? id(),
|
|
186
|
-
idempotencyKey: options.idempotencyKey,
|
|
188
|
+
idempotencyKey: options.idempotencyKey ?? id(),
|
|
187
189
|
}, options.signal);
|
|
188
190
|
}
|
|
189
191
|
cancel(options) {
|
|
@@ -198,6 +200,27 @@ export class SessionClient {
|
|
|
198
200
|
await this.transport.requireFeature("session-reads", options.signal);
|
|
199
201
|
return SessionManifestViewSchema.parse(await this.transport.json(`${this.path}/manifest`, "GET", undefined, options.signal));
|
|
200
202
|
}
|
|
203
|
+
/**
|
|
204
|
+
* The model the session's next turn uses (Host feature `session-model`): its own while its
|
|
205
|
+
* provider is connected, else the Tenant's.
|
|
206
|
+
*/
|
|
207
|
+
async model(options = {}) {
|
|
208
|
+
await this.transport.requireFeature("session-model", options.signal);
|
|
209
|
+
return SessionModelViewSchema.parse(await this.transport.json(`${this.path}/model`, "GET", undefined, options.signal));
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Chooses one of a connected provider's models for this session's turns; the Tenant's model and
|
|
213
|
+
* other sessions' stay as they are. Refused while a turn runs or waits (`409 session_busy`).
|
|
214
|
+
*/
|
|
215
|
+
async setModel(choice, options = {}) {
|
|
216
|
+
await this.transport.requireFeature("session-model", options.signal);
|
|
217
|
+
return SessionModelViewSchema.parse(await this.transport.json(`${this.path}/model`, "PUT", { provider: choice.provider, model: choice.model }, options.signal));
|
|
218
|
+
}
|
|
219
|
+
/** Clears the session's own model: its next turns use the Tenant's. */
|
|
220
|
+
async clearModel(options = {}) {
|
|
221
|
+
await this.transport.requireFeature("session-model", options.signal);
|
|
222
|
+
return SessionModelViewSchema.parse(await this.transport.json(`${this.path}/model`, "DELETE", undefined, options.signal));
|
|
223
|
+
}
|
|
201
224
|
async usage(options = {}) {
|
|
202
225
|
await this.transport.requireFeature("session-reads", options.signal);
|
|
203
226
|
const query = new URLSearchParams();
|
package/dist/skills/load.js
CHANGED
|
@@ -91,6 +91,13 @@ export function loadSkillsFromDirectory(directory, diagnostics, options = {}) {
|
|
|
91
91
|
});
|
|
92
92
|
continue;
|
|
93
93
|
}
|
|
94
|
+
if (parsed.secrets !== undefined)
|
|
95
|
+
diagnostics.push({
|
|
96
|
+
severity: "warning",
|
|
97
|
+
code: `${prefix}.skill-secrets`,
|
|
98
|
+
message: `Skill '${parsed.name}': secrets in SKILL.md is deprecated (D80), since it is not an Agent Skills key: an agent folder lists a skill's secrets in AGENT.md's skills block, where nylorun migrate folder moves them. It is still read until a later release.`,
|
|
99
|
+
path: skillFile,
|
|
100
|
+
});
|
|
94
101
|
skills[parsed.name] = {
|
|
95
102
|
name: parsed.name,
|
|
96
103
|
description: parsed.description,
|
package/dist/skills/skills.d.ts
CHANGED
|
@@ -19,9 +19,10 @@ export interface SkillsPart extends PartDeclaration {
|
|
|
19
19
|
*
|
|
20
20
|
* Sets `skills`: each skill's name, description and files (every file of its folder, binary
|
|
21
21
|
* included, by content hash). A SKILL.md's frontmatter `secrets` (environment variable names)
|
|
22
|
-
* become the skill's `secrets` in the harness section when the agent's manifest is saved
|
|
23
|
-
*
|
|
24
|
-
* the
|
|
22
|
+
* become the skill's `secrets` in the harness section when the agent's manifest is saved; that key
|
|
23
|
+
* is deprecated (D80), so it also emits a `DeprecationWarning`. The client uploads the files the
|
|
24
|
+
* Runtime lacks when it saves the agent's manifest; the Runtime adds the skill catalog and serves
|
|
25
|
+
* `load_skill` / `read_skill_resource` from them.
|
|
25
26
|
*/
|
|
26
27
|
export declare function skills(directory: string): SkillsPart;
|
|
27
28
|
/**
|
package/dist/skills/skills.js
CHANGED
|
@@ -14,14 +14,23 @@ import { loadSkillsFromDirectory, resolveSkillsRoot, } from "./load.js";
|
|
|
14
14
|
*
|
|
15
15
|
* Sets `skills`: each skill's name, description and files (every file of its folder, binary
|
|
16
16
|
* included, by content hash). A SKILL.md's frontmatter `secrets` (environment variable names)
|
|
17
|
-
* become the skill's `secrets` in the harness section when the agent's manifest is saved
|
|
18
|
-
*
|
|
19
|
-
* the
|
|
17
|
+
* become the skill's `secrets` in the harness section when the agent's manifest is saved; that key
|
|
18
|
+
* is deprecated (D80), so it also emits a `DeprecationWarning`. The client uploads the files the
|
|
19
|
+
* Runtime lacks when it saves the agent's manifest; the Runtime adds the skill catalog and serves
|
|
20
|
+
* `load_skill` / `read_skill_resource` from them.
|
|
20
21
|
*/
|
|
21
22
|
export function skills(directory) {
|
|
22
23
|
const root = resolveSkillsRoot(directory);
|
|
23
24
|
const diagnostics = [];
|
|
24
25
|
const loaded = loadSkillsFromDirectory(root, diagnostics);
|
|
26
|
+
// `secrets` in SKILL.md is deprecated (D80): building or saving the agent says so.
|
|
27
|
+
for (const diagnostic of diagnostics)
|
|
28
|
+
if (diagnostic.code === "skills.skill-secrets")
|
|
29
|
+
process.emitWarning(diagnostic.message, {
|
|
30
|
+
type: "DeprecationWarning",
|
|
31
|
+
code: diagnostic.code,
|
|
32
|
+
...(diagnostic.path === undefined ? {} : { detail: diagnostic.path }),
|
|
33
|
+
});
|
|
25
34
|
const listed = Object.values(loaded);
|
|
26
35
|
const id = `skills:${basename(root)}`;
|
|
27
36
|
if (listed.length === 0) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nylorun/agents",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.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.29.0-beta",
|
|
47
47
|
"gray-matter": "^4.0.3"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|