@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 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 KB) 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.
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` and the harness's own `loop` settings; 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`.
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 (D71). Whether an Agent serves one person or a project is your app's choice:
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, track T2): `client.agentManifests`. An agent manifest (v8) is one
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 (`runtime`:
6
- * kind, image, network, setup, files, limits, secret slots). The Runtime checks the two against
7
- * each other when it is saved (`422 manifest_inconsistent`); an Agent (`client.agents`) is made
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`). It first uploads the files the manifest names by hash that the
13
- * Runtime does not hold (skills, a flow's agents' skills, runtime files), and refuses before
14
- * sending a tool that runs your code or an `approval`, as the Runtime runs agents from their
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 (track T3). Refused before anything is sent when 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>;
@@ -1,18 +1,18 @@
1
1
  /**
2
- * Agent manifests (protocol 16, track T2): `client.agentManifests`. An agent manifest (v8) is one
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 (`runtime`:
6
- * kind, image, network, setup, files, limits, secret slots). The Runtime checks the two against
7
- * each other when it is saved (`422 manifest_inconsistent`); an Agent (`client.agents`) is made
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`). It first uploads the files the manifest names by hash that the
13
- * Runtime does not hold (skills, a flow's agents' skills, runtime files), and refuses before
14
- * sending a tool that runs your code or an `approval`, as the Runtime runs agents from their
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
- /** The agent's declarations, then those of each agent it uses as a tool. */
35
- function declarationsOf(agent) {
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
- if (!binding || !("parts" in binding))
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
- ...harnessSectionOf(loop, input.harness === undefined ? {} : { harness: input.harness }),
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 (track T3). Refused before anything is sent when 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 (track T3). Its manifest, kind, harness and storage
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 (D71).
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 (track T4, D74): what its manifest's `runtime.triggers` declares starts its turns with
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 (D75), and `runs(id)` pages what each fire came to.
22
+ * trigger's signing secret, and `runs(id)` pages what each fire came to.
23
23
  *
24
- * Memory (track T5, D72): an Agent whose manifest sets `harness.memory` keeps Markdown files under
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 (D64).
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 (track T3); `false`: the others. */
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 (Q6): `contents` (each file's path under
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 (D64), send only `{ value }` (or `{ headers }`); a name it does
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 (track T5, D72): `client.agents.memories`. A path is a memory's path under
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 (track T5). */
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 (track T3): at once, at its pod's next start for a runtime change (`targetVersion`,
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`, track T3): the
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`, track T3): a
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` (Q15): how an Agent changes what is fixed for its
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 (track T3). Its manifest, kind, harness and storage
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 (D71).
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 (track T4, D74): what its manifest's `runtime.triggers` declares starts its turns with
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 (D75), and `runs(id)` pages what each fire came to.
22
+ * trigger's signing secret, and `runs(id)` pages what each fire came to.
23
23
  *
24
- * Memory (track T5, D72): an Agent whose manifest sets `harness.memory` keeps Markdown files under
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 (Q6): `contents` (each file's path under
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 (D64), send only `{ value }` (or `{ headers }`); a name it does
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 (track T5, D72): `client.agents.memories`. A path is a memory's path under
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 (track T5). */
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 (track T3): at once, at its pod's next start for a runtime change (`targetVersion`,
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`, track T3): the
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`, track T3): a
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` (Q15): how an Agent changes what is fixed for its
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
@@ -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 (F8.2): at the end of each turn the Runtime exports `/workspace/outputs` of the
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 (F8.2): at the end of each turn the Runtime exports `/workspace/outputs` of the
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, track T2): `composeManifest(dir)` reads `agent.manifest.json`
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, `mcpServers` an `mcp.json` (the Agent Plugins format), and
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({ name: skill.name, description: skill.description, files: { ...skill.files } }, file.skills);
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({ name: skill.name, description: skill.description, files: { ...skill.files }, plugin: label }, entry);
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 (track R2 M4): `client.files`. A definition names the files it needs by the
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 (track R2 M4): `client.files`. A definition names the files it needs by the
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";
@@ -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 (D71). Default: a new UUID. */
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 (R5, Host feature `tool-calls`), in the order they were first sent:
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 (R7, Host feature `traces`): its spans as OTLP/HTTP JSON (`resourceSpans`), as
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
  */
@@ -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 (R5, Host feature `tool-calls`), in the order they were first sent:
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 (R7, Host feature `traces`): its spans as OTLP/HTTP JSON (`resourceSpans`), as
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
  */
@@ -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
- export declare function parseSkill(text: string, directoryName: string): {
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
- } | undefined;
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
@@ -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 = parseSkill(readFileSync(skillReal, "utf8"), entry.name);
75
- if (!parsed) {
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 is not a valid Agent Skill`,
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 undefined;
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 undefined;
120
+ return INVALID_SKILL;
113
121
  if (name.length > 64 || !SKILL_NAME.test(name) || name !== directoryName)
114
- return undefined;
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
  }
@@ -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). The client uploads the files the Runtime lacks when it saves the
22
- * agent's manifest; the Runtime adds the skill catalog and serves `load_skill` / `read_skill_resource`
23
- * from them.
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 and the bytes behind their files. A plugin's skills carry its label, so
28
- * a tool call can be traced to the plugin that brought the skill.
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">;
@@ -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). The client uploads the files the Runtime lacks when it saves the
17
- * agent's manifest; the Runtime adds the skill catalog and serves `load_skill` / `read_skill_resource`
18
- * from them.
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 and the bytes behind their files. A plugin's skills carry its label, so
33
- * a tool call can be traced to the plugin that brought the skill.
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.24.0-beta",
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.24.0-beta",
46
+ "@nylorun/core": "0.26.0-beta",
47
47
  "gray-matter": "^4.0.3"
48
48
  },
49
49
  "peerDependencies": {