@nylorun/agents 0.24.0-beta → 0.26.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 +34 -1
- package/README.md +8 -7
- package/dist/agent-manifests.d.ts +16 -11
- package/dist/agent-manifests.js +37 -16
- package/dist/agents.d.ts +15 -15
- package/dist/agents.js +13 -13
- package/dist/artifacts.d.ts +1 -1
- package/dist/artifacts.js +1 -1
- package/dist/compose.js +15 -4
- package/dist/files.d.ts +1 -1
- package/dist/files.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/session-client.d.ts +20 -3
- package/dist/session-client.js +2 -2
- package/dist/skills/load.d.ts +10 -2
- package/dist/skills/load.js +22 -6
- package/dist/skills/skills.d.ts +8 -6
- package/dist/skills/skills.js +9 -5
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# @nylorun/agents
|
|
2
2
|
|
|
3
|
+
## 0.26.0-beta
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 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.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Pin core to the tested release.
|
|
12
|
+
- Updated dependencies [fce0c9b]
|
|
13
|
+
- Updated dependencies [ab7c08a]
|
|
14
|
+
- Updated dependencies [4a563e7]
|
|
15
|
+
- @nylorun/core@0.26.0-beta
|
|
16
|
+
|
|
17
|
+
## 0.25.0-beta
|
|
18
|
+
|
|
19
|
+
### Minor Changes
|
|
20
|
+
|
|
21
|
+
- ed08ba8: `agentManifests.save({ agent, runtime, memory })` turns on a built agent's memory (`harness.memory`, `{ access?, instructions? }`), and a skill folder's `SKILL.md` may list the secret slots its scripts read in its frontmatter (`secrets: [GH_TOKEN]`): `.skills()`, `.plugin()` and `composeManifest` make them the skill's `secrets` in the harness section, which the Runtime checks against `runtime.secrets` at save. A `secrets` that is not a list of environment variable names skips the skill with a warning.
|
|
22
|
+
- eeb6294: Session usage as one ledger (protocol 18). The usage ledger keeps each model call's cost by token kind (uncached input, cache read, cache write, output) at the prices the adapter applied (migration 0037). For a protocol 18 client, `GET /v1/sessions/{id}/calls/model` returns each call's `startedAt`, `endedAt` and `usage.costSplit` (null for a call recorded before), and `GET /v1/sessions/{id}/usage` adds `costSplit` with `splitCalls`, and the calls' durations added up as `modelTimeMs` with `timedCalls`. A protocol 16 or 17 client reads usage as before. Studio's Usage tab is rebuilt on them: a Session card with one tokens bar and a ledger of tokens and cost by kind adding up to a total row, then the model calls grouped by turn with the same columns (cached, uncached, output, total, time, cost), and a row menu whose View event opens the call's event in the Events tab.
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- ed08ba8: `composeManifest(dir)`, and so `nylorun apply`, keep a `harness.manifest.json`'s `memory`: the composed harness section turned the Agent's memory on only when the manifest was saved as one document.
|
|
27
|
+
- ed08ba8: Public text names no internal roadmap codes: the OpenAPI documents' descriptions, the type comments of `@nylorun/core`, `@nylorun/agents` and `@nylorun/admin`, and the CLI's help, README and generated compose file. A Runtime test fails when either OpenAPI document names one.
|
|
28
|
+
- Pin core to the tested release.
|
|
29
|
+
- Updated dependencies [ed08ba8]
|
|
30
|
+
- Updated dependencies [ed08ba8]
|
|
31
|
+
- Updated dependencies [ed08ba8]
|
|
32
|
+
- Updated dependencies [eeb6294]
|
|
33
|
+
- Updated dependencies [ed08ba8]
|
|
34
|
+
- @nylorun/core@0.25.0-beta
|
|
35
|
+
|
|
3
36
|
## 0.24.0-beta
|
|
4
37
|
|
|
5
38
|
### Minor Changes
|
|
@@ -11,7 +44,7 @@
|
|
|
11
44
|
|
|
12
45
|
The SDK has `client.agentManifests`, `client.agents` and `composeManifest(dir)` for three-file authoring; the CLI has `nylorun apply`, `nylorun agent-manifests`, `nylorun agents` and `nylorun migrate v8`; Studio has an Agents screen, an Agent page and an Agent manifests screen. See MIGRATION.md.
|
|
13
46
|
|
|
14
|
-
- 4fa160d: Agent memory, storage and API (track T5, D72). An agent manifest's `harness.memory` (`access`: `read_write` or `read_only`; `instructions`, at most 4,096 characters) turns on the Agent's memory: Markdown files under `/memories` it keeps across sessions, wakes and upgrades. The Runtime keeps them in Postgres (`memories`, `memory_versions`, migration 0036) with a version per change and who made it. `GET /v1/agents/{id}/memories` lists them (`pathPrefix`), `GET`, `PUT` (whole content, with an optional `contentSha256` precondition: `409 memory_conflict`) and `DELETE /v1/agents/{id}/memories/{path}` read, write and delete one (or a folder), and `GET /v1/agents/{id}/memory_versions` and `.../memory_versions/{versionId}` page the versions. A fork copies the memories (the fork plan adds `memories`); a delete removes them. `@nylorun/core` adds `memoryPathOf`, `memoryDescriptionOf` and the memory contracts; new Runtime limits `memoryBytes` (100
|
|
47
|
+
- 4fa160d: Agent memory, storage and API (track T5, D72). An agent manifest's `harness.memory` (`access`: `read_write` or `read_only`; `instructions`, at most 4,096 characters) turns on the Agent's memory: Markdown files under `/memories` it keeps across sessions, wakes and upgrades. The Runtime keeps them in Postgres (`memories`, `memory_versions`, migration 0036) with a version per change and who made it. `GET /v1/agents/{id}/memories` lists them (`pathPrefix`), `GET`, `PUT` (whole content, with an optional `contentSha256` precondition: `409 memory_conflict`) and `DELETE /v1/agents/{id}/memories/{path}` read, write and delete one (or a folder), and `GET /v1/agents/{id}/memory_versions` and `.../memory_versions/{versionId}` page the versions. A fork copies the memories (the fork plan adds `memories`); a delete removes them. `@nylorun/core` adds `memoryPathOf`, `memoryDescriptionOf` and the memory contracts; new Runtime limits `memoryBytes` (100 KiB) and `memoriesPerAgent` (10,000); new error code `memory_conflict`. The SDK adds `client.agents.memories`; the CLI adds `nylorun agents memories ls|cat|put|rm|versions`. The model's `memory` tool arrives in the next steps of T5.
|
|
15
48
|
- 9e60e40: Protocol 17, a queue per session (track T4, D76). A message sent while the session's turn runs or waits is queued (`{status: "queued", queuedMessageId, position}`, `message.queued`) and starts the next turn when this one ends, with every queued message, each its own user message (`queue.taken`); a cancel ends the turn, not the queue. `delivery: "reject"` keeps the refusal, now `409 session_busy`, and a protocol 16 client's messages default to it, so the Host serves protocol 16 and 17. The queue holds at most the Runtime limit `sessionQueuedMessages` (`429 queue_full`), and session reads count `queuedMessages`. Our engine takes `{kind: "messages", messages}` as a turn's input. The SDK's `input` and `inputParts` take `delivery`, and the CLI's `sessions send` takes `--reject-if-busy`. See MIGRATION.md.
|
|
16
49
|
- f85483d: Triggers (track T4, D74). An agent manifest's `runtime.triggers` declares `schedule` (cron with an IANA `timezone`) and `at` triggers, each with the session its runs go to (a session id, opened on the first run, or `new` for a session per run) and its prompt. They start active with a new Agent, fire on a durable timer per trigger with Postgres as the truth (one late fire after downtime, never a backfill, a fixed jitter per trigger), and each fire is a run (`started`, `queued`, `skipped` or `failed`, with a reason) at `GET /v1/agents/{id}/runs`. A schedule adds at most one queued message to a busy session. `GET /v1/agents/{id}/triggers` and `POST .../triggers/{triggerId}/pause`, `/resume` and `/run` (with an `Idempotency-Key`); Agent reads add `nextWakeAt`, and the Agent's stream adds `agent.run`, `agent.trigger_paused` and `agent.trigger_resumed`. New Runtime limits: `triggersPerAgent`, `triggerMinIntervalMs` and `triggerRunRetentionMs` (with floors), `triggerRunsPerHour`, `triggerJitterPercent`, `triggerJitterMaxMs`, `triggerPromptChars`; Host tuning `triggers.lateGraceMs` and `triggers.intervalCheckFires`. The SDK's `client.agents` adds `triggers`, `pauseTrigger`, `resumeTrigger`, `runTrigger` and `runs`; the CLI adds `nylorun agents triggers` and `nylorun agents runs`.
|
|
17
50
|
- ae54de7: Webhook triggers (track T4, D75). A `webhook: "standard-webhooks"` trigger has an endpoint, `POST /v1/webhooks/{endpointId}`, an anonymous, unversioned route that takes any body up to the new Runtime limit `webhookBodyBytes` and verifies Standard Webhooks (`webhook-id`, `webhook-timestamp` within the Host's `triggers.webhookToleranceMs`, `webhook-signature`) in the keys service, which alone opens the trigger's sealed secret; anything else is `401` and writes nothing. A delivery runs the trigger once per `webhook-id` (`202 {runId, status}`); a paused trigger answers a signed one `409 trigger_paused`. `POST /v1/agents/{id}/triggers/{triggerId}/secret` makes or replaces the `whsec_` secret, shown once (`agent.trigger_secret_rotated`); trigger reads show `webhookUrl` and `secretSet`. The payload reaches the model under the trigger's prompt in a labelled `<trigger-payload>` block. The SDK adds `agents.rotateWebhookSecret`, and the CLI `nylorun agents triggers new-secret`.
|
package/README.md
CHANGED
|
@@ -252,7 +252,7 @@ const assistant = Agent({ id: "assistant", name: "Order assistant" })
|
|
|
252
252
|
.skills("./assistant-skills");
|
|
253
253
|
```
|
|
254
254
|
|
|
255
|
-
Each subdirectory under the catalog must contain a `SKILL.md` with YAML frontmatter (`name`, `description`) per [Agent Skills](https://agentskills.io/home). 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.
|
|
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.
|
|
256
256
|
|
|
257
257
|
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
258
|
|
|
@@ -272,13 +272,13 @@ 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
|
|
|
279
|
-
An **agent manifest** (v8, protocol 16) is one named, versioned document with two sections: `harness`, what runs inside the agent loop (the harness's `id`, `instructions`, `tools`, `skills`, `mcpServers`, `plugins`, `outputSchema
|
|
279
|
+
An **agent manifest** (v8, protocol 16) is one named, versioned document with two sections: `harness`, what runs inside the agent loop (the harness's `id`, `instructions`, `tools`, `skills`, `mcpServers`, `plugins`, `outputSchema`, the harness's own `loop` settings and the Agent's `memory`; or a flow, `kind: "workflow"`), and `runtime`, what the Runtime provides around it (`kind`, `image`, `network`, `setup`, `files`, `limits`, `lifecycle` and its secret slots, `secrets`). The Runtime checks the two against each other when the manifest is saved and refuses every gap at once (`422 manifest_inconsistent`, `details.gaps`): a custom harness off a pod, a `credential` or a skill's `secrets` that is no declared slot of the right type, skills on a runtime without a sandbox. A manifest without `runtime` makes Agents of kind `none`.
|
|
280
280
|
|
|
281
|
-
An **Agent** is the live instance of one version of a manifest: one environment of its runtime's kind, one vault, and the sessions that run on it, with an id you choose (`users/alice/triage`), which outlives them. It is made only from a saved manifest: `manifest: "triage"` tracks the manifest (its latest version when the Agent is made), `"triage@3"` pins version 3; either way it resolves the version once and keeps it. Every session runs on one Agent (`createSession({ agent })`), and no session is special: creating an Agent opens none, and the app picks session ids
|
|
281
|
+
An **Agent** is the live instance of one version of a manifest: one environment of its runtime's kind, one vault, and the sessions that run on it, with an id you choose (`users/alice/triage`), which outlives them. It is made only from a saved manifest: `manifest: "triage"` tracks the manifest (its latest version when the Agent is made), `"triage@3"` pins version 3; either way it resolves the version once and keeps it. Every session runs on one Agent (`createSession({ agent })`), and no session is special: creating an Agent opens none, and the app picks session ids. Whether an Agent serves one person or a project is your app's choice:
|
|
282
282
|
|
|
283
283
|
```ts
|
|
284
284
|
// The manifest, from a built agent and the runtime section to run it in.
|
|
@@ -301,7 +301,7 @@ await client.agents.secrets.put("users/alice/analyst", "GH_TOKEN", { value: proc
|
|
|
301
301
|
const session = await client.createSession({ id: "alice-q3", agent: "users/alice/analyst", ownerUserId: "alice" });
|
|
302
302
|
```
|
|
303
303
|
|
|
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?, name?, description?, metadata? }`: the SDK makes the harness section from the built agent or flow (`harnessSectionOf`; `harness` is the harness's id) 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.
|
|
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
306
|
### Three files: `composeManifest`
|
|
307
307
|
|
|
@@ -314,10 +314,11 @@ A manifest can also be authored as files, so a runtime is shared by many agents
|
|
|
314
314
|
// agents/triage/harness.manifest.json
|
|
315
315
|
{
|
|
316
316
|
"instructions": ["Triage each new issue."],
|
|
317
|
-
"skills": "./skills", // each subfolder with a SKILL.md is a skill
|
|
317
|
+
"skills": "./skills", // each subfolder with a SKILL.md is a skill; its secrets, the frontmatter's
|
|
318
318
|
"mcpServers": "./mcp.json", // the Agent Plugins format
|
|
319
319
|
"plugins": ["../../plugins/github"], // folded in as .plugin() folds it
|
|
320
320
|
"credentials": { "linear": "linear" }, // MCP server or HTTP tool → runtime.secrets slot
|
|
321
|
+
"memory": { "access": "read_write" }, // turns on the Agent's memory
|
|
321
322
|
"requires": { "sandbox": "any", "secrets": ["GH_TOKEN"] } // checked here only
|
|
322
323
|
}
|
|
323
324
|
|
|
@@ -348,7 +349,7 @@ With a sandbox (kind `virtual` or `pod`), the model gets `bash`, `read`, `write`
|
|
|
348
349
|
|
|
349
350
|
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.
|
|
350
351
|
|
|
351
|
-
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)).
|
|
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?, 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)).
|
|
352
353
|
|
|
353
354
|
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).
|
|
354
355
|
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Agent manifests (protocol 16
|
|
2
|
+
* Agent manifests (protocol 16): `client.agentManifests`. An agent manifest (v8) is one
|
|
3
3
|
* named, versioned document with what runs inside the agent loop (`harness`: the harness's id,
|
|
4
4
|
* instructions, tools, skills, MCP servers, plugins, output schema, the harness's own `loop`
|
|
5
|
-
* settings; or a flow, `kind: "workflow"`) and what the Runtime provides around it
|
|
6
|
-
* kind, image, network, setup, files, limits, secret slots). The Runtime checks the
|
|
7
|
-
* each other when it is saved (`422 manifest_inconsistent`); an Agent
|
|
8
|
-
* from one version.
|
|
5
|
+
* settings, memory; or a flow, `kind: "workflow"`) and what the Runtime provides around it
|
|
6
|
+
* (`runtime`: kind, image, network, setup, files, limits, secret slots). The Runtime checks the
|
|
7
|
+
* two against each other when it is saved (`422 manifest_inconsistent`); an Agent
|
|
8
|
+
* (`client.agents`) is made from one version.
|
|
9
9
|
*
|
|
10
10
|
* `save` takes the document, what `composeManifest` read from a folder, or a built agent or flow
|
|
11
11
|
* with the runtime section to run it in: the SDK makes the harness section from the agent's
|
|
12
|
-
* manifest (`harnessSectionOf`)
|
|
13
|
-
* Runtime does not hold (skills, a flow's
|
|
14
|
-
* sending a tool that runs your code or an
|
|
15
|
-
* manifests alone.
|
|
12
|
+
* manifest (`harnessSectionOf`), with its skills' `secrets`, `loop` and `memory`. It first
|
|
13
|
+
* uploads the files the manifest names by hash that the Runtime does not hold (skills, a flow's
|
|
14
|
+
* agents' skills, runtime files), and refuses before sending a tool that runs your code or an
|
|
15
|
+
* `approval`, as the Runtime runs agents from their manifests alone.
|
|
16
16
|
*/
|
|
17
|
-
import { AGENT_MANIFEST_V8_SCHEMA_VERSION, type AgentManifestPlan, type AgentManifestV8, type AgentManifestVersions, type AgentManifestView, type DeleteAgentManifestResponse, type RuntimeSection, type SaveAgentManifestResponse } from "@nylorun/core/contracts";
|
|
17
|
+
import { AGENT_MANIFEST_V8_SCHEMA_VERSION, type AgentManifestPlan, type AgentManifestV8, type AgentMemorySettings, type AgentManifestVersions, type AgentManifestView, type DeleteAgentManifestResponse, type RuntimeSection, type SaveAgentManifestResponse } from "@nylorun/core/contracts";
|
|
18
18
|
import { type AgentManifest, type BuiltAgent, type BuiltWorkflow, type JsonObject, type SkillFileSource, type WorkflowManifest } from "@nylorun/core/define";
|
|
19
19
|
import { type Transport } from "./http.js";
|
|
20
20
|
/** A built agent or flow, or anything with its id and manifest. */
|
|
@@ -44,6 +44,11 @@ export interface AgentManifestFromAgent {
|
|
|
44
44
|
readonly runtime?: RuntimeSection;
|
|
45
45
|
/** The harness's own settings (`harness.loop`), passed to it in `turn.start`. */
|
|
46
46
|
readonly loop?: JsonObject;
|
|
47
|
+
/**
|
|
48
|
+
* Turns on the Agent's memory (`harness.memory`): `access` `read_write` (the default) or
|
|
49
|
+
* `read_only`, and `instructions` on what to remember. An agent's only, not a flow's.
|
|
50
|
+
*/
|
|
51
|
+
readonly memory?: AgentMemorySettings;
|
|
47
52
|
}
|
|
48
53
|
/** What `composeManifest` read from a folder: the document, the bytes of the files it names, its gaps. */
|
|
49
54
|
export interface ComposedManifest {
|
|
@@ -98,7 +103,7 @@ export declare class AgentManifestsClient {
|
|
|
98
103
|
private send;
|
|
99
104
|
/**
|
|
100
105
|
* Saves the manifest's next version (`PUT /v1/agent-manifests/{name}`), or answers the latest
|
|
101
|
-
* unchanged; every Agent that tracks the manifest moves to it
|
|
106
|
+
* unchanged; every Agent that tracks the manifest moves to it. Refused before anything is sent when it
|
|
102
107
|
* misses its `requires` or, composed, its sections do not fit (`ManifestGapsError`).
|
|
103
108
|
*/
|
|
104
109
|
save(input: AgentManifestInput, options?: AgentManifestSaveOptions): Promise<SaveAgentManifestResponse>;
|
package/dist/agent-manifests.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Agent manifests (protocol 16
|
|
2
|
+
* Agent manifests (protocol 16): `client.agentManifests`. An agent manifest (v8) is one
|
|
3
3
|
* named, versioned document with what runs inside the agent loop (`harness`: the harness's id,
|
|
4
4
|
* instructions, tools, skills, MCP servers, plugins, output schema, the harness's own `loop`
|
|
5
|
-
* settings; or a flow, `kind: "workflow"`) and what the Runtime provides around it
|
|
6
|
-
* kind, image, network, setup, files, limits, secret slots). The Runtime checks the
|
|
7
|
-
* each other when it is saved (`422 manifest_inconsistent`); an Agent
|
|
8
|
-
* from one version.
|
|
5
|
+
* settings, memory; or a flow, `kind: "workflow"`) and what the Runtime provides around it
|
|
6
|
+
* (`runtime`: kind, image, network, setup, files, limits, secret slots). The Runtime checks the
|
|
7
|
+
* two against each other when it is saved (`422 manifest_inconsistent`); an Agent
|
|
8
|
+
* (`client.agents`) is made from one version.
|
|
9
9
|
*
|
|
10
10
|
* `save` takes the document, what `composeManifest` read from a folder, or a built agent or flow
|
|
11
11
|
* with the runtime section to run it in: the SDK makes the harness section from the agent's
|
|
12
|
-
* manifest (`harnessSectionOf`)
|
|
13
|
-
* Runtime does not hold (skills, a flow's
|
|
14
|
-
* sending a tool that runs your code or an
|
|
15
|
-
* manifests alone.
|
|
12
|
+
* manifest (`harnessSectionOf`), with its skills' `secrets`, `loop` and `memory`. It first
|
|
13
|
+
* uploads the files the manifest names by hash that the Runtime does not hold (skills, a flow's
|
|
14
|
+
* agents' skills, runtime files), and refuses before sending a tool that runs your code or an
|
|
15
|
+
* `approval`, as the Runtime runs agents from their manifests alone.
|
|
16
16
|
*/
|
|
17
17
|
import { AGENT_MANIFEST_V8_SCHEMA_VERSION, AgentManifestV8Schema, isAgentManifestName, } from "@nylorun/core/contracts";
|
|
18
18
|
import { approvalRefusal, codeToolRefusal, delegateOf, harnessSectionOf, isBuiltWorkflow, loopManifestOf, } from "@nylorun/core/define";
|
|
@@ -31,15 +31,20 @@ export class ManifestGapsError extends Error {
|
|
|
31
31
|
this.name = "ManifestGapsError";
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
-
/**
|
|
35
|
-
function
|
|
34
|
+
/** An agent's binding, with its parts; none for a flow. */
|
|
35
|
+
function agentBindingOf(agent) {
|
|
36
36
|
if (isBuiltWorkflow(agent) || agent.manifest.kind === "workflow")
|
|
37
|
-
return
|
|
37
|
+
return undefined;
|
|
38
38
|
const built = agent.build?.() ?? agent;
|
|
39
39
|
if (isBuiltWorkflow(built))
|
|
40
|
-
return
|
|
40
|
+
return undefined;
|
|
41
41
|
const binding = built.getBinding?.();
|
|
42
|
-
|
|
42
|
+
return binding && "parts" in binding ? binding : undefined;
|
|
43
|
+
}
|
|
44
|
+
/** The agent's declarations, then those of each agent it uses as a tool. */
|
|
45
|
+
function declarationsOf(agent) {
|
|
46
|
+
const binding = agentBindingOf(agent);
|
|
47
|
+
if (!binding)
|
|
43
48
|
return [];
|
|
44
49
|
const found = [...(binding.parts ?? [])];
|
|
45
50
|
for (const tool of binding.tools ?? []) {
|
|
@@ -64,6 +69,16 @@ function skillFilesOf(agent, into = new Map()) {
|
|
|
64
69
|
into.set(sha256, source);
|
|
65
70
|
return into;
|
|
66
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* The harness section's skills with the secret slots each reads (a SKILL.md's `secrets`, which
|
|
74
|
+
* `.skills()` and `.plugin()` keep on the agent's parts, outside its v7 manifest).
|
|
75
|
+
*/
|
|
76
|
+
function withSkillSecrets(agent, skills) {
|
|
77
|
+
const secrets = Object.assign({}, ...(agentBindingOf(agent)?.parts ?? []).map((part) => part.skillSecrets ?? {}));
|
|
78
|
+
if (skills === undefined || Object.keys(secrets).length === 0)
|
|
79
|
+
return skills;
|
|
80
|
+
return Object.fromEntries(Object.entries(skills).map(([key, skill]) => [key, secrets[key] ? { ...skill, secrets: [...secrets[key]] } : skill]));
|
|
81
|
+
}
|
|
67
82
|
const quoted = (value) => JSON.stringify(value);
|
|
68
83
|
/**
|
|
69
84
|
* Where a runtime section misses what a harness `requires` (agent manifest v7's, and
|
|
@@ -114,9 +129,15 @@ function prepare(input) {
|
|
|
114
129
|
const loop = agent.manifest;
|
|
115
130
|
refuse(loop);
|
|
116
131
|
// `harnessSectionOf` leaves out the loop manifest's identity and its `requires`, checked here.
|
|
132
|
+
const section = harnessSectionOf(loop, input.harness === undefined ? {} : { harness: input.harness });
|
|
133
|
+
if (input.memory !== undefined && "kind" in section)
|
|
134
|
+
throw new TypeError(`memory turns on an agent's memory; ${agent.id} is a flow, whose harness section has none`);
|
|
135
|
+
const skills = "kind" in section ? undefined : withSkillSecrets(agent, section.skills);
|
|
117
136
|
const harness = {
|
|
118
|
-
...
|
|
137
|
+
...section,
|
|
138
|
+
...(skills === undefined ? {} : { skills }),
|
|
119
139
|
...(input.loop === undefined ? {} : { loop: input.loop }),
|
|
140
|
+
...(input.memory === undefined ? {} : { memory: input.memory }),
|
|
120
141
|
};
|
|
121
142
|
const description = input.description ?? loop.description;
|
|
122
143
|
const metadata = input.metadata ?? loop.metadata;
|
|
@@ -168,7 +189,7 @@ export class AgentManifestsClient {
|
|
|
168
189
|
}
|
|
169
190
|
/**
|
|
170
191
|
* Saves the manifest's next version (`PUT /v1/agent-manifests/{name}`), or answers the latest
|
|
171
|
-
* unchanged; every Agent that tracks the manifest moves to it
|
|
192
|
+
* unchanged; every Agent that tracks the manifest moves to it. Refused before anything is sent when it
|
|
172
193
|
* misses its `requires` or, composed, its sections do not fit (`ManifestGapsError`).
|
|
173
194
|
*/
|
|
174
195
|
async save(input, options = {}) {
|
package/dist/agents.d.ts
CHANGED
|
@@ -4,24 +4,24 @@
|
|
|
4
4
|
* `pod` or `none`), one vault of secrets for its shell, MCP servers and HTTP tools, and the
|
|
5
5
|
* sessions that run on it. It is made only from a saved manifest, `name` (tracking) or `name@n`
|
|
6
6
|
* (pinned), and runs one version of it at a time: `put` with another version moves it, and a save
|
|
7
|
-
* moves every Agent that tracks the manifest
|
|
7
|
+
* moves every Agent that tracks the manifest. Its manifest, kind, harness and storage
|
|
8
8
|
* are fixed (`409 agent_fixed_field`): `fork` makes a new Agent to change them. It has
|
|
9
9
|
* its own id and outlives its sessions; whether it serves one person or a project is the app's
|
|
10
10
|
* choice, made by its id:
|
|
11
11
|
*
|
|
12
12
|
* - `agents.put("users/alice/triage", { manifest: "triage", owner: "alice" })`: get-or-create
|
|
13
13
|
* in one call. Write its secrets with `agents.secrets.put(id, name, { value })` and open
|
|
14
|
-
* sessions on it with `createSession({ agent: id, ... })`. Creating it opens no session
|
|
14
|
+
* sessions on it with `createSession({ agent: id, ... })`. Creating it opens no session.
|
|
15
15
|
*
|
|
16
16
|
* Ids may hold `/`; they are sent percent-encoded as one path segment. Secret values are
|
|
17
17
|
* write-only: no call reads one back.
|
|
18
18
|
*
|
|
19
|
-
* Triggers
|
|
19
|
+
* Triggers: what its manifest's `runtime.triggers` declares starts its turns with
|
|
20
20
|
* no client connected. `triggers(id)` lists them with their status and next run, `pauseTrigger`,
|
|
21
21
|
* `resumeTrigger` and `runTrigger` change or run one, `rotateWebhookSecret` makes a webhook
|
|
22
|
-
* trigger's signing secret
|
|
22
|
+
* trigger's signing secret, and `runs(id)` pages what each fire came to.
|
|
23
23
|
*
|
|
24
|
-
* Memory
|
|
24
|
+
* Memory: an Agent whose manifest sets `harness.memory` keeps Markdown files under
|
|
25
25
|
* `/memories` across sessions. `agents.memories` lists, reads, writes (to seed it), deletes and
|
|
26
26
|
* pages the versions every change leaves.
|
|
27
27
|
*
|
|
@@ -35,7 +35,7 @@ import { type Transport } from "./http.js";
|
|
|
35
35
|
export type AgentInput = Omit<PutAgentRequest, "requestId">;
|
|
36
36
|
/**
|
|
37
37
|
* One secret of an Agent's vault, as written: typed, or only its value (`{ value }`, or
|
|
38
|
-
* `{ headers }` for a `headers` secret) when its manifest declares the slot
|
|
38
|
+
* `{ headers }` for a `headers` secret) when its manifest declares the slot.
|
|
39
39
|
*/
|
|
40
40
|
export type AgentSecretInput = PutAgentSecretRequest | {
|
|
41
41
|
readonly value: string;
|
|
@@ -49,13 +49,13 @@ export interface AgentFilter {
|
|
|
49
49
|
readonly state?: string;
|
|
50
50
|
/** The agent manifest's name. */
|
|
51
51
|
readonly manifest?: string;
|
|
52
|
-
/** `true`: only Agents moving to another version of their manifest
|
|
52
|
+
/** `true`: only Agents moving to another version of their manifest; `false`: the others. */
|
|
53
53
|
readonly outdated?: boolean;
|
|
54
54
|
}
|
|
55
55
|
/** The bytes of files by hash, by `sha256:<hex>` (`runtimeFiles(...).bytes`). */
|
|
56
56
|
export type RuntimeFileBytes = Readonly<Record<string, Uint8Array>>;
|
|
57
57
|
/**
|
|
58
|
-
* A runtime section's `files` entry by content hash
|
|
58
|
+
* A runtime section's `files` entry by content hash: `contents` (each file's path under
|
|
59
59
|
* `path` and its bytes or text) hashed, as `entry` for the manifest's `runtime.files` and `bytes`
|
|
60
60
|
* for `agentManifests.save`'s `files`, which uploads them. Each file holds at most 2 GiB.
|
|
61
61
|
*/
|
|
@@ -75,7 +75,7 @@ export declare class AgentSecretsClient {
|
|
|
75
75
|
* Creates or replaces secret `name` (`PUT /v1/agents/{id}/secrets/{name}`): for
|
|
76
76
|
* `environment_secret` and `environment_variable` the name is the variable's. A second
|
|
77
77
|
* credential for a host or a URL is a 409 `credential_conflict`. When the Agent's manifest
|
|
78
|
-
* declares the secret's slot
|
|
78
|
+
* declares the secret's slot, send only `{ value }` (or `{ headers }`); a name it does
|
|
79
79
|
* not declare is `409 secret_not_in_manifest`.
|
|
80
80
|
*/
|
|
81
81
|
put(agentId: string, name: string, secret: AgentSecretInput, options?: {
|
|
@@ -95,7 +95,7 @@ export declare class AgentSecretsClient {
|
|
|
95
95
|
}>;
|
|
96
96
|
}
|
|
97
97
|
/**
|
|
98
|
-
* An Agent's memories
|
|
98
|
+
* An Agent's memories: `client.agents.memories`. A path is a memory's path under
|
|
99
99
|
* `/memories` (`conventions/release.md` or `/memories/conventions/release.md`).
|
|
100
100
|
*/
|
|
101
101
|
export declare class AgentMemoriesClient {
|
|
@@ -149,14 +149,14 @@ export declare class AgentInstancesClient {
|
|
|
149
149
|
private path;
|
|
150
150
|
/** The Agent's secrets. */
|
|
151
151
|
get secrets(): AgentSecretsClient;
|
|
152
|
-
/** The Agent's memories
|
|
152
|
+
/** The Agent's memories. */
|
|
153
153
|
get memories(): AgentMemoriesClient;
|
|
154
154
|
/**
|
|
155
155
|
* Creates the Agent, its vault and its compute from a saved agent manifest, or finds the one
|
|
156
156
|
* with this id (`PUT /v1/agents/{id}`): `{ manifest: "name" }` tracks the manifest (its latest
|
|
157
157
|
* version when the Agent is made), `"name@n"` pins version n. On an existing Agent, labels
|
|
158
158
|
* replace, `owner` sets or clears the owner guard, and `manifest` moves it to that version of
|
|
159
|
-
* its manifest
|
|
159
|
+
* its manifest: at once, at its pod's next start for a runtime change (`targetVersion`,
|
|
160
160
|
* `waitingFor: "start"`), or once a required secret the version adds is set. Another manifest,
|
|
161
161
|
* kind, harness or storage is `409 agent_fixed_field` (fork it instead).
|
|
162
162
|
*/
|
|
@@ -164,7 +164,7 @@ export declare class AgentInstancesClient {
|
|
|
164
164
|
signal?: AbortSignal;
|
|
165
165
|
}): Promise<AgentView>;
|
|
166
166
|
/**
|
|
167
|
-
* What `put` would do to the existing Agent (`PUT /v1/agents/{id}?dryRun=true
|
|
167
|
+
* What `put` would do to the existing Agent (`PUT /v1/agents/{id}?dryRun=true`): the
|
|
168
168
|
* version it runs and the one `input.manifest` names, when that applies (`now`, `next_start`,
|
|
169
169
|
* `secrets`, or `refused` with each fixed field), the changes, and the secrets it would wait
|
|
170
170
|
* for or leave unused. Nothing changes.
|
|
@@ -343,7 +343,7 @@ export declare class AgentInstancesClient {
|
|
|
343
343
|
signal?: AbortSignal;
|
|
344
344
|
}): Promise<AgentView>;
|
|
345
345
|
/**
|
|
346
|
-
* Applies a version the Agent waits to apply (`POST /v1/agents/{id}/restart
|
|
346
|
+
* Applies a version the Agent waits to apply (`POST /v1/agents/{id}/restart`): a
|
|
347
347
|
* running pod stops, so the next turn starts it on the version; a stopped one starts at once.
|
|
348
348
|
* `retry` first moves it to the version that failed to start again. Refused during a turn.
|
|
349
349
|
*/
|
|
@@ -370,7 +370,7 @@ export declare class AgentInstancesClient {
|
|
|
370
370
|
signal?: AbortSignal;
|
|
371
371
|
}): Promise<AgentRestore>;
|
|
372
372
|
/**
|
|
373
|
-
* Forks the Agent into a new one, `options.id
|
|
373
|
+
* Forks the Agent into a new one, `options.id`: how an Agent changes what is fixed for its
|
|
374
374
|
* life (its manifest, kind, harness or storage). It is made from `options.manifest` (default the source's manifest,
|
|
375
375
|
* tracking), its disk starts from `options.disk` (`latest`, the default, snapshots first; a
|
|
376
376
|
* version; or `empty`) and the secrets the new manifest declares are copied
|
package/dist/agents.js
CHANGED
|
@@ -4,24 +4,24 @@
|
|
|
4
4
|
* `pod` or `none`), one vault of secrets for its shell, MCP servers and HTTP tools, and the
|
|
5
5
|
* sessions that run on it. It is made only from a saved manifest, `name` (tracking) or `name@n`
|
|
6
6
|
* (pinned), and runs one version of it at a time: `put` with another version moves it, and a save
|
|
7
|
-
* moves every Agent that tracks the manifest
|
|
7
|
+
* moves every Agent that tracks the manifest. Its manifest, kind, harness and storage
|
|
8
8
|
* are fixed (`409 agent_fixed_field`): `fork` makes a new Agent to change them. It has
|
|
9
9
|
* its own id and outlives its sessions; whether it serves one person or a project is the app's
|
|
10
10
|
* choice, made by its id:
|
|
11
11
|
*
|
|
12
12
|
* - `agents.put("users/alice/triage", { manifest: "triage", owner: "alice" })`: get-or-create
|
|
13
13
|
* in one call. Write its secrets with `agents.secrets.put(id, name, { value })` and open
|
|
14
|
-
* sessions on it with `createSession({ agent: id, ... })`. Creating it opens no session
|
|
14
|
+
* sessions on it with `createSession({ agent: id, ... })`. Creating it opens no session.
|
|
15
15
|
*
|
|
16
16
|
* Ids may hold `/`; they are sent percent-encoded as one path segment. Secret values are
|
|
17
17
|
* write-only: no call reads one back.
|
|
18
18
|
*
|
|
19
|
-
* Triggers
|
|
19
|
+
* Triggers: what its manifest's `runtime.triggers` declares starts its turns with
|
|
20
20
|
* no client connected. `triggers(id)` lists them with their status and next run, `pauseTrigger`,
|
|
21
21
|
* `resumeTrigger` and `runTrigger` change or run one, `rotateWebhookSecret` makes a webhook
|
|
22
|
-
* trigger's signing secret
|
|
22
|
+
* trigger's signing secret, and `runs(id)` pages what each fire came to.
|
|
23
23
|
*
|
|
24
|
-
* Memory
|
|
24
|
+
* Memory: an Agent whose manifest sets `harness.memory` keeps Markdown files under
|
|
25
25
|
* `/memories` across sessions. `agents.memories` lists, reads, writes (to seed it), deletes and
|
|
26
26
|
* pages the versions every change leaves.
|
|
27
27
|
*
|
|
@@ -33,7 +33,7 @@ import { AgentPageSchema, isAgentId, } from "@nylorun/core/contracts";
|
|
|
33
33
|
import { definitionFileHash } from "./files.js";
|
|
34
34
|
import { id, segment } from "./http.js";
|
|
35
35
|
/**
|
|
36
|
-
* A runtime section's `files` entry by content hash
|
|
36
|
+
* A runtime section's `files` entry by content hash: `contents` (each file's path under
|
|
37
37
|
* `path` and its bytes or text) hashed, as `entry` for the manifest's `runtime.files` and `bytes`
|
|
38
38
|
* for `agentManifests.save`'s `files`, which uploads them. Each file holds at most 2 GiB.
|
|
39
39
|
*/
|
|
@@ -71,7 +71,7 @@ export class AgentSecretsClient {
|
|
|
71
71
|
* Creates or replaces secret `name` (`PUT /v1/agents/{id}/secrets/{name}`): for
|
|
72
72
|
* `environment_secret` and `environment_variable` the name is the variable's. A second
|
|
73
73
|
* credential for a host or a URL is a 409 `credential_conflict`. When the Agent's manifest
|
|
74
|
-
* declares the secret's slot
|
|
74
|
+
* declares the secret's slot, send only `{ value }` (or `{ headers }`); a name it does
|
|
75
75
|
* not declare is `409 secret_not_in_manifest`.
|
|
76
76
|
*/
|
|
77
77
|
async put(agentId, name, secret, options = {}) {
|
|
@@ -90,7 +90,7 @@ export class AgentSecretsClient {
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
/**
|
|
93
|
-
* An Agent's memories
|
|
93
|
+
* An Agent's memories: `client.agents.memories`. A path is a memory's path under
|
|
94
94
|
* `/memories` (`conventions/release.md` or `/memories/conventions/release.md`).
|
|
95
95
|
*/
|
|
96
96
|
export class AgentMemoriesClient {
|
|
@@ -158,7 +158,7 @@ export class AgentInstancesClient {
|
|
|
158
158
|
get secrets() {
|
|
159
159
|
return new AgentSecretsClient(this.transport, (agentId) => this.path(agentId));
|
|
160
160
|
}
|
|
161
|
-
/** The Agent's memories
|
|
161
|
+
/** The Agent's memories. */
|
|
162
162
|
get memories() {
|
|
163
163
|
return new AgentMemoriesClient(this.transport, (agentId) => this.path(agentId));
|
|
164
164
|
}
|
|
@@ -167,7 +167,7 @@ export class AgentInstancesClient {
|
|
|
167
167
|
* with this id (`PUT /v1/agents/{id}`): `{ manifest: "name" }` tracks the manifest (its latest
|
|
168
168
|
* version when the Agent is made), `"name@n"` pins version n. On an existing Agent, labels
|
|
169
169
|
* replace, `owner` sets or clears the owner guard, and `manifest` moves it to that version of
|
|
170
|
-
* its manifest
|
|
170
|
+
* its manifest: at once, at its pod's next start for a runtime change (`targetVersion`,
|
|
171
171
|
* `waitingFor: "start"`), or once a required secret the version adds is set. Another manifest,
|
|
172
172
|
* kind, harness or storage is `409 agent_fixed_field` (fork it instead).
|
|
173
173
|
*/
|
|
@@ -175,7 +175,7 @@ export class AgentInstancesClient {
|
|
|
175
175
|
return this.transport.json(this.path(agentId), "PUT", { requestId: id(), ...input }, options.signal);
|
|
176
176
|
}
|
|
177
177
|
/**
|
|
178
|
-
* What `put` would do to the existing Agent (`PUT /v1/agents/{id}?dryRun=true
|
|
178
|
+
* What `put` would do to the existing Agent (`PUT /v1/agents/{id}?dryRun=true`): the
|
|
179
179
|
* version it runs and the one `input.manifest` names, when that applies (`now`, `next_start`,
|
|
180
180
|
* `secrets`, or `refused` with each fixed field), the changes, and the secrets it would wait
|
|
181
181
|
* for or leave unused. Nothing changes.
|
|
@@ -207,7 +207,7 @@ export class AgentInstancesClient {
|
|
|
207
207
|
return this.transport.json(`${this.path(agentId)}/stop`, "POST", {}, options.signal);
|
|
208
208
|
}
|
|
209
209
|
/**
|
|
210
|
-
* Applies a version the Agent waits to apply (`POST /v1/agents/{id}/restart
|
|
210
|
+
* Applies a version the Agent waits to apply (`POST /v1/agents/{id}/restart`): a
|
|
211
211
|
* running pod stops, so the next turn starts it on the version; a stopped one starts at once.
|
|
212
212
|
* `retry` first moves it to the version that failed to start again. Refused during a turn.
|
|
213
213
|
*/
|
|
@@ -235,7 +235,7 @@ export class AgentInstancesClient {
|
|
|
235
235
|
return this.transport.json(`${this.path(agentId)}/restore`, "POST", { requestId: id(), version }, options.signal);
|
|
236
236
|
}
|
|
237
237
|
/**
|
|
238
|
-
* Forks the Agent into a new one, `options.id
|
|
238
|
+
* Forks the Agent into a new one, `options.id`: how an Agent changes what is fixed for its
|
|
239
239
|
* life (its manifest, kind, harness or storage). It is made from `options.manifest` (default the source's manifest,
|
|
240
240
|
* tracking), its disk starts from `options.disk` (`latest`, the default, snapshots first; a
|
|
241
241
|
* version; or `empty`) and the secrets the new manifest declares are copied
|
package/dist/artifacts.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* short-lived URL that needs no credential, for `<img>` tags and sharing. A message names an
|
|
6
6
|
* artifact in a file part (`session.inputParts`), and the model reads it.
|
|
7
7
|
*
|
|
8
|
-
* Folders
|
|
8
|
+
* Folders: at the end of each turn the Runtime exports `/workspace/outputs` of the
|
|
9
9
|
* session's sandbox as a version of its `outputs` folder. Read one as a tree, one file by path,
|
|
10
10
|
* a diff between versions or a zip.
|
|
11
11
|
*
|
package/dist/artifacts.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* short-lived URL that needs no credential, for `<img>` tags and sharing. A message names an
|
|
6
6
|
* artifact in a file part (`session.inputParts`), and the model reads it.
|
|
7
7
|
*
|
|
8
|
-
* Folders
|
|
8
|
+
* Folders: at the end of each turn the Runtime exports `/workspace/outputs` of the
|
|
9
9
|
* session's sandbox as a version of its `outputs` folder. Read one as a tree, one file by path,
|
|
10
10
|
* a diff between versions or a zip.
|
|
11
11
|
*
|
package/dist/compose.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Three-file authoring (protocol 16
|
|
2
|
+
* Three-file authoring (protocol 16): `composeManifest(dir)` reads `agent.manifest.json`
|
|
3
3
|
* and the files it names, and makes the one agent manifest v8 document the Runtime saves, with
|
|
4
4
|
* the bytes of every file it names by hash. `nylorun apply` runs it for each folder.
|
|
5
5
|
*
|
|
6
6
|
* - `agent.manifest.json` (`AgentManifestFileSchema`): the manifest's name, description and
|
|
7
7
|
* metadata, with `harness` and `runtime` inline or as paths to the two files below.
|
|
8
8
|
* - `harness.manifest.json` (`HarnessManifestFileSchema`): the harness section, whose `skills`
|
|
9
|
-
* may be a folder of skill folders
|
|
9
|
+
* may be a folder of skill folders (each skill's `secrets` from its SKILL.md's frontmatter),
|
|
10
|
+
* `mcpServers` an `mcp.json` (the Agent Plugins format), and
|
|
10
11
|
* `plugins` Agent Plugin folders, folded in as `.plugin()` folds them; `credentials` names the
|
|
11
12
|
* `runtime.secrets` slot each MCP server or HTTP tool authenticates with; `requires` is checked
|
|
12
13
|
* against the runtime section here, since the API never sees it.
|
|
@@ -101,6 +102,15 @@ function warningsOf(diagnostics) {
|
|
|
101
102
|
function relativeTo(path) {
|
|
102
103
|
return relative(process.cwd(), path) || path;
|
|
103
104
|
}
|
|
105
|
+
/** A skill read from its folder as the harness section declares it, with its SKILL.md's `secrets`. */
|
|
106
|
+
function harnessSkill(skill) {
|
|
107
|
+
return {
|
|
108
|
+
name: skill.name,
|
|
109
|
+
description: skill.description,
|
|
110
|
+
files: { ...skill.files },
|
|
111
|
+
...(skill.secrets === undefined ? {} : { secrets: [...skill.secrets] }),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
104
114
|
/** The agent harness section a `harness.manifest.json` describes, its paths resolved. */
|
|
105
115
|
function agentHarness(file, base, where, files, warnings) {
|
|
106
116
|
const skills = {};
|
|
@@ -120,7 +130,7 @@ function agentHarness(file, base, where, files, warnings) {
|
|
|
120
130
|
if (typeof file.skills === "string") {
|
|
121
131
|
const diagnostics = [];
|
|
122
132
|
for (const skill of Object.values(loadSkillsFromDirectory(resolve(base, file.skills), diagnostics))) {
|
|
123
|
-
addSkill(
|
|
133
|
+
addSkill(harnessSkill(skill), file.skills);
|
|
124
134
|
for (const [sha256, source] of Object.entries(skill.sources))
|
|
125
135
|
files.set(sha256, source);
|
|
126
136
|
}
|
|
@@ -156,7 +166,7 @@ function agentHarness(file, base, where, files, warnings) {
|
|
|
156
166
|
warnings.push(...warningsOf(loaded.diagnostics).map((message) => `Plugin '${loaded.name}': ${message}`));
|
|
157
167
|
const label = { name: loaded.name, ...(loaded.version === undefined ? {} : { version: loaded.version }) };
|
|
158
168
|
for (const skill of Object.values(loaded.skills)) {
|
|
159
|
-
addSkill({
|
|
169
|
+
addSkill({ ...harnessSkill(skill), plugin: label }, entry);
|
|
160
170
|
for (const [sha256, source] of Object.entries(skill.sources))
|
|
161
171
|
files.set(sha256, source);
|
|
162
172
|
}
|
|
@@ -185,6 +195,7 @@ function agentHarness(file, base, where, files, warnings) {
|
|
|
185
195
|
...(plugins.length === 0 ? {} : { plugins }),
|
|
186
196
|
...(file.outputSchema === undefined ? {} : { outputSchema: file.outputSchema }),
|
|
187
197
|
...(file.loop === undefined ? {} : { loop: file.loop }),
|
|
198
|
+
...(file.memory === undefined ? {} : { memory: file.memory }),
|
|
188
199
|
};
|
|
189
200
|
}
|
|
190
201
|
/**
|
package/dist/files.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Definition files
|
|
2
|
+
* Definition files: `client.files`. A definition names the files it needs by the
|
|
3
3
|
* SHA-256 of their bytes (a skill's folder, `SkillManifest.files`); the Runtime holds each one
|
|
4
4
|
* once, uploaded with `PUT /v1/files/sha256:<hex>`, and refuses a manifest naming a file it
|
|
5
5
|
* does not hold (`400 definition_files_missing`). `agentManifests.save` uploads them itself.
|
package/dist/files.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Definition files
|
|
2
|
+
* Definition files: `client.files`. A definition names the files it needs by the
|
|
3
3
|
* SHA-256 of their bytes (a skill's folder, `SkillManifest.files`); the Runtime holds each one
|
|
4
4
|
* once, uploaded with `PUT /v1/files/sha256:<hex>`, and refuses a manifest naming a file it
|
|
5
5
|
* does not hold (`400 definition_files_missing`). `agentManifests.save` uploads them itself.
|
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export { Agent, AgentBuilder } from "./builder.js";
|
|
|
6
6
|
export { AgentBuildError, AgentLifecycleError, VerdictSchema, isVerdict, WorkflowBuildError, isBuiltWorkflow, flow, tool, http, defineSchema, ToolError, } 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, 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";
|
|
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
10
|
export { AGENT_EVENT_TYPES, 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";
|
package/dist/session-client.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export interface SessionView {
|
|
|
26
26
|
}
|
|
27
27
|
/** `AgentsClient.createSession`'s options. */
|
|
28
28
|
export interface CreateSessionOptions {
|
|
29
|
-
/** The session's id, which the app chooses
|
|
29
|
+
/** The session's id, which the app chooses. Default: a new UUID. */
|
|
30
30
|
id?: string;
|
|
31
31
|
/**
|
|
32
32
|
* The Agent the session runs on (`client.agents`): its manifest version, its sandbox and its
|
|
@@ -221,6 +221,15 @@ export declare class SessionClient {
|
|
|
221
221
|
unpricedCalls: number;
|
|
222
222
|
unreportedCalls: number;
|
|
223
223
|
unknownQualityCalls: number;
|
|
224
|
+
costSplit: {
|
|
225
|
+
input: number;
|
|
226
|
+
cacheRead: number;
|
|
227
|
+
cacheWrite: number;
|
|
228
|
+
output: number;
|
|
229
|
+
};
|
|
230
|
+
splitCalls: number;
|
|
231
|
+
modelTimeMs: number;
|
|
232
|
+
timedCalls: number;
|
|
224
233
|
}>;
|
|
225
234
|
modelCalls(options?: {
|
|
226
235
|
turnId?: string;
|
|
@@ -237,6 +246,8 @@ export declare class SessionClient {
|
|
|
237
246
|
provider: string | null;
|
|
238
247
|
model: string | null;
|
|
239
248
|
at: string;
|
|
249
|
+
startedAt: string | null;
|
|
250
|
+
endedAt: string | null;
|
|
240
251
|
outcome: "completed" | "failed";
|
|
241
252
|
usage: {
|
|
242
253
|
inputTokens: number;
|
|
@@ -249,13 +260,19 @@ export declare class SessionClient {
|
|
|
249
260
|
duplicate: boolean;
|
|
250
261
|
tokensReported: boolean | null;
|
|
251
262
|
costKnown: boolean | null;
|
|
263
|
+
costSplit: {
|
|
264
|
+
input: number;
|
|
265
|
+
cacheRead: number;
|
|
266
|
+
cacheWrite: number;
|
|
267
|
+
output: number;
|
|
268
|
+
} | null;
|
|
252
269
|
} | null;
|
|
253
270
|
}[];
|
|
254
271
|
nextCursor: string | null;
|
|
255
272
|
asOf: string;
|
|
256
273
|
}>;
|
|
257
274
|
/**
|
|
258
|
-
* The session's tool calls (
|
|
275
|
+
* The session's tool calls (Host feature `tool-calls`), in the order they were first sent:
|
|
259
276
|
* each one's tool, input, status and sends. `attempts` above 1: a crash re-sent it.
|
|
260
277
|
*/
|
|
261
278
|
toolCalls(options?: {
|
|
@@ -292,7 +309,7 @@ export declare class SessionClient {
|
|
|
292
309
|
asOf: string;
|
|
293
310
|
}>;
|
|
294
311
|
/**
|
|
295
|
-
* A turn's trace (
|
|
312
|
+
* A turn's trace (Host feature `traces`): its spans as OTLP/HTTP JSON (`resourceSpans`), as
|
|
296
313
|
* the Runtime posts them to an OTLP collector. Post it to a collector's `/v1/traces` to backfill
|
|
297
314
|
* one, or read it without one.
|
|
298
315
|
*/
|
package/dist/session-client.js
CHANGED
|
@@ -215,7 +215,7 @@ export class SessionClient {
|
|
|
215
215
|
return ModelCallsPageSchema.parse(await this.transport.json(`${this.path}/calls/model?${query}`, "GET", undefined, options.signal));
|
|
216
216
|
}
|
|
217
217
|
/**
|
|
218
|
-
* The session's tool calls (
|
|
218
|
+
* The session's tool calls (Host feature `tool-calls`), in the order they were first sent:
|
|
219
219
|
* each one's tool, input, status and sends. `attempts` above 1: a crash re-sent it.
|
|
220
220
|
*/
|
|
221
221
|
async toolCalls(options = {}) {
|
|
@@ -228,7 +228,7 @@ export class SessionClient {
|
|
|
228
228
|
return ToolCallsPageSchema.parse(await this.transport.json(`${this.path}/calls/tool?${query}`, "GET", undefined, options.signal));
|
|
229
229
|
}
|
|
230
230
|
/**
|
|
231
|
-
* A turn's trace (
|
|
231
|
+
* A turn's trace (Host feature `traces`): its spans as OTLP/HTTP JSON (`resourceSpans`), as
|
|
232
232
|
* the Runtime posts them to an OTLP collector. Post it to a collector's `/v1/traces` to backfill
|
|
233
233
|
* one, or read it without one.
|
|
234
234
|
*/
|
package/dist/skills/load.d.ts
CHANGED
|
@@ -14,6 +14,11 @@ export declare class SkillsError extends Error {
|
|
|
14
14
|
export interface LoadedSkill {
|
|
15
15
|
readonly name: string;
|
|
16
16
|
readonly description: string;
|
|
17
|
+
/**
|
|
18
|
+
* The `runtime.secrets` slots its scripts read (`environment_secret` or `environment_variable`),
|
|
19
|
+
* from its SKILL.md's frontmatter `secrets`: sorted, each once.
|
|
20
|
+
*/
|
|
21
|
+
readonly secrets?: readonly string[];
|
|
17
22
|
/** Every file of the folder by its `/`-separated path, as `sha256:<hex>`. */
|
|
18
23
|
readonly files: Readonly<Record<string, string>>;
|
|
19
24
|
/** Each file's bytes, by `sha256:<hex>`, for the client to upload. */
|
|
@@ -32,11 +37,14 @@ export declare function loadSkillsFromDirectory(directory: string, diagnostics:
|
|
|
32
37
|
readonly boundary?: string;
|
|
33
38
|
readonly codePrefix?: string;
|
|
34
39
|
}): Readonly<Record<string, LoadedSkill>>;
|
|
35
|
-
|
|
40
|
+
/** A SKILL.md's frontmatter and instructions: `secrets` lists the secret slots its scripts read. */
|
|
41
|
+
export interface ParsedSkill {
|
|
36
42
|
name: string;
|
|
37
43
|
description: string;
|
|
44
|
+
secrets?: string[];
|
|
38
45
|
instructions: string;
|
|
39
|
-
}
|
|
46
|
+
}
|
|
47
|
+
export declare function parseSkill(text: string, directoryName: string): ParsedSkill | undefined;
|
|
40
48
|
/**
|
|
41
49
|
* Every file of `directory` (a runtime section's `files` entry with `from`), hashed as a skill's
|
|
42
50
|
* folder is: each by its `/`-separated path, with where to read its bytes. Version control, OS
|
package/dist/skills/load.js
CHANGED
|
@@ -3,6 +3,7 @@ import { closeSync, existsSync, openSync, readdirSync, readFileSync, readSync, r
|
|
|
3
3
|
import { open } from "node:fs/promises";
|
|
4
4
|
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
5
|
import { SKILL_ENTRY, skillFilePathIssue, } from "@nylorun/core/define";
|
|
6
|
+
import { ENVIRONMENT_NAME_PATTERN } from "@nylorun/core/contracts";
|
|
6
7
|
import matter from "gray-matter";
|
|
7
8
|
const SKILL_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
8
9
|
/** Version control and operating system files, never part of a skill. */
|
|
@@ -71,12 +72,12 @@ export function loadSkillsFromDirectory(directory, diagnostics, options = {}) {
|
|
|
71
72
|
});
|
|
72
73
|
continue;
|
|
73
74
|
}
|
|
74
|
-
const parsed =
|
|
75
|
-
if (
|
|
75
|
+
const parsed = readSkill(readFileSync(skillReal, "utf8"), entry.name);
|
|
76
|
+
if (typeof parsed === "string") {
|
|
76
77
|
diagnostics.push({
|
|
77
78
|
severity: "warning",
|
|
78
79
|
code: `${prefix}.skill-skipped`,
|
|
79
|
-
message: `Skipped skill '${entry.name}' because SKILL.md
|
|
80
|
+
message: `Skipped skill '${entry.name}' because SKILL.md ${parsed}`,
|
|
80
81
|
path: skillFile,
|
|
81
82
|
});
|
|
82
83
|
continue;
|
|
@@ -93,28 +94,43 @@ export function loadSkillsFromDirectory(directory, diagnostics, options = {}) {
|
|
|
93
94
|
skills[parsed.name] = {
|
|
94
95
|
name: parsed.name,
|
|
95
96
|
description: parsed.description,
|
|
97
|
+
...(parsed.secrets === undefined ? {} : { secrets: parsed.secrets }),
|
|
96
98
|
...readFiles(`skill '${parsed.name}'`, boundary, directoryReal, diagnostics, prefix),
|
|
97
99
|
};
|
|
98
100
|
}
|
|
99
101
|
return skills;
|
|
100
102
|
}
|
|
101
103
|
export function parseSkill(text, directoryName) {
|
|
104
|
+
const parsed = readSkill(text, directoryName);
|
|
105
|
+
return typeof parsed === "string" ? undefined : parsed;
|
|
106
|
+
}
|
|
107
|
+
const INVALID_SKILL = "is not a valid Agent Skill";
|
|
108
|
+
/** The skill a SKILL.md declares, or why it declares none. */
|
|
109
|
+
function readSkill(text, directoryName) {
|
|
102
110
|
let parsed;
|
|
103
111
|
try {
|
|
104
112
|
parsed = matter(text);
|
|
105
113
|
}
|
|
106
114
|
catch {
|
|
107
|
-
return
|
|
115
|
+
return INVALID_SKILL;
|
|
108
116
|
}
|
|
109
117
|
const name = asTrimmedString(parsed.data.name);
|
|
110
118
|
const description = asTrimmedString(parsed.data.description);
|
|
111
119
|
if (!name || !description || description.length > 1024)
|
|
112
|
-
return
|
|
120
|
+
return INVALID_SKILL;
|
|
113
121
|
if (name.length > 64 || !SKILL_NAME.test(name) || name !== directoryName)
|
|
114
|
-
return
|
|
122
|
+
return INVALID_SKILL;
|
|
123
|
+
const secrets = parsed.data.secrets;
|
|
124
|
+
if (secrets !== undefined &&
|
|
125
|
+
(!Array.isArray(secrets) ||
|
|
126
|
+
!secrets.every((item) => typeof item === "string" && ENVIRONMENT_NAME_PATTERN.test(item))))
|
|
127
|
+
return "has secrets that are not a list of environment variable names";
|
|
115
128
|
return {
|
|
116
129
|
name,
|
|
117
130
|
description,
|
|
131
|
+
...(secrets === undefined || secrets.length === 0
|
|
132
|
+
? {}
|
|
133
|
+
: { secrets: [...new Set(secrets)].sort() }),
|
|
118
134
|
instructions: parsed.content.replace(/^\r?\n/, ""),
|
|
119
135
|
};
|
|
120
136
|
}
|
package/dist/skills/skills.d.ts
CHANGED
|
@@ -18,13 +18,15 @@ export interface SkillsPart extends PartDeclaration {
|
|
|
18
18
|
* ```
|
|
19
19
|
*
|
|
20
20
|
* Sets `skills`: each skill's name, description and files (every file of its folder, binary
|
|
21
|
-
* included, by content hash).
|
|
22
|
-
*
|
|
23
|
-
*
|
|
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. The
|
|
23
|
+
* client uploads the files the Runtime lacks when it saves the agent's manifest; the Runtime adds
|
|
24
|
+
* the skill catalog and serves `load_skill` / `read_skill_resource` from them.
|
|
24
25
|
*/
|
|
25
26
|
export declare function skills(directory: string): SkillsPart;
|
|
26
27
|
/**
|
|
27
|
-
* The manifest's skills
|
|
28
|
-
*
|
|
28
|
+
* The manifest's skills, the bytes behind their files and the secret slots their SKILL.md lists
|
|
29
|
+
* (`secrets`, the harness section's when the agent is saved). A plugin's skills carry its label,
|
|
30
|
+
* so a tool call can be traced to the plugin that brought the skill.
|
|
29
31
|
*/
|
|
30
|
-
export declare function skillsDeclaration(listed: readonly LoadedSkill[], plugin?: PluginLabel): Required<Pick<PartDeclaration, "skills" | "skillFiles"
|
|
32
|
+
export declare function skillsDeclaration(listed: readonly LoadedSkill[], plugin?: PluginLabel): Required<Pick<PartDeclaration, "skills" | "skillFiles">> & Pick<PartDeclaration, "skillSecrets">;
|
package/dist/skills/skills.js
CHANGED
|
@@ -13,9 +13,10 @@ import { loadSkillsFromDirectory, resolveSkillsRoot, } from "./load.js";
|
|
|
13
13
|
* ```
|
|
14
14
|
*
|
|
15
15
|
* Sets `skills`: each skill's name, description and files (every file of its folder, binary
|
|
16
|
-
* included, by content hash).
|
|
17
|
-
*
|
|
18
|
-
*
|
|
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. The
|
|
18
|
+
* client uploads the files the Runtime lacks when it saves the agent's manifest; the Runtime adds
|
|
19
|
+
* the skill catalog and serves `load_skill` / `read_skill_resource` from them.
|
|
19
20
|
*/
|
|
20
21
|
export function skills(directory) {
|
|
21
22
|
const root = resolveSkillsRoot(directory);
|
|
@@ -29,10 +30,12 @@ export function skills(directory) {
|
|
|
29
30
|
return { id, root, ...skillsDeclaration(listed), diagnostics };
|
|
30
31
|
}
|
|
31
32
|
/**
|
|
32
|
-
* The manifest's skills
|
|
33
|
-
*
|
|
33
|
+
* The manifest's skills, the bytes behind their files and the secret slots their SKILL.md lists
|
|
34
|
+
* (`secrets`, the harness section's when the agent is saved). A plugin's skills carry its label,
|
|
35
|
+
* so a tool call can be traced to the plugin that brought the skill.
|
|
34
36
|
*/
|
|
35
37
|
export function skillsDeclaration(listed, plugin) {
|
|
38
|
+
const secrets = listed.filter((skill) => skill.secrets !== undefined).map((skill) => [skill.name, skill.secrets]);
|
|
36
39
|
return {
|
|
37
40
|
skills: Object.fromEntries(listed.map((skill) => [
|
|
38
41
|
skill.name,
|
|
@@ -44,5 +47,6 @@ export function skillsDeclaration(listed, plugin) {
|
|
|
44
47
|
},
|
|
45
48
|
])),
|
|
46
49
|
skillFiles: Object.assign({}, ...listed.map((skill) => skill.sources)),
|
|
50
|
+
...(secrets.length === 0 ? {} : { skillSecrets: Object.fromEntries(secrets) }),
|
|
47
51
|
};
|
|
48
52
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nylorun/agents",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.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.26.0-beta",
|
|
47
47
|
"gray-matter": "^4.0.3"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|