# Saving manifests (/docs/run/agent-manifests)



```markdown title="agents/triage/AGENT.md"
---
name: triage
description: Sorts new GitHub issues
---
Label and route new issues.
```

```json title="agents/triage/mcp.json"
{
  "mcpServers": {
    "github": {
      "type": "streamable-http",
      "url": "https://api.githubcopilot.com/mcp/",
      "headers": { "Authorization": "Bearer ${GITHUB_TOKEN}" }
    }
  }
}
```

These files are one [agent manifest](/docs/build/agent-manifests). Check it
offline, save it, and make an Agent from it:

```sh
npx nylorun check agents/triage
npx nylorun apply agents/triage --dry-run
npx nylorun apply agents/triage
npx nylorun agents put users/alice/triage --manifest triage --owner alice
```

Saved, an agent manifest is one named, versioned document with two sections:
`harness`, the agent itself (what runs inside the agent loop), and `runtime`,
what the Runtime provides around it (the kind of sandbox, network, setup,
files, limits, secret slots, and triggers). Each save writes its next version.
An [Agent](/docs/run/agents) is made from one version, and an Agent that
tracks the manifest moves to each new version in place.

The folder above compiles to this document, which `--dry-run` prints. The
`${GITHUB_TOKEN}` header becomes the server's `credential` and a `bearer` slot
bound to its URL, and with no `runtime.json` the runtime section is kind
`none`:

```json
{
  "manifestSchemaVersion": 8,
  "name": "triage",
  "description": "Sorts new GitHub issues",
  "harness": {
    "instructions": ["Label and route new issues."],
    "mcpServers": {
      "github": {
        "name": "github",
        "type": "streamable-http",
        "url": "https://api.githubcopilot.com/mcp/",
        "credential": "GITHUB_TOKEN"
      }
    }
  },
  "runtime": {
    "kind": "none",
    "secrets": {
      "GITHUB_TOKEN": { "type": "bearer", "url": "https://api.githubcopilot.com/mcp/", "required": true }
    }
  }
}
```

The Runtime compiles the folder itself, with the compiler `check` runs, and
keeps it byte for byte as the version's source. A manifest can also be saved
from code, or as [one JSON document](#fields). The older
[three-file format](#three-files) is deprecated.
A JSON document may start with a `$schema` URL for editors; clients drop it
before they save.

## Save from code [#save-from-code]

`client.agentManifests.save` takes an agent you built and the runtime section
to run it in. This is what the starter's `src/main.ts` does:

```ts
import { Agent, createClient } from "@nylorun/agents";

const client = await createClient();

const triage = Agent({ id: "triage" })
  .instructions("Label and route new issues.")
  .build();

const { manifest, unchanged, warnings } = await client.agentManifests.save({
  agent: triage,
  runtime: { kind: "virtual", network: { allowedHosts: [] } },
});

await client.agents.put("users/alice/triage", { manifest: "triage", owner: "alice" });
```

| Field                     | Meaning                                                                                           |
| ------------------------- | ------------------------------------------------------------------------------------------------- |
| `agent`                   | A built agent or flow. Its manifest becomes the `harness` section.                                |
| `runtime`                 | The runtime section. Without it, an Agent made from the manifest is kind `none`.                  |
| `name`                    | The manifest's name. Default: the agent's id.                                                     |
| `description`, `metadata` | Default: the agent's.                                                                             |
| `harness`                 | The harness that runs the loop (`harness.id`). Default `nylorun`.                                 |
| `loop`                    | The harness's own settings (`harness.loop`).                                                      |
| `memory`                  | `{ access?, instructions? }`: turns on the Agent's [memory](/docs/run/memory) (`harness.memory`). |

`save` also takes the document itself, such as a parsed JSON file, or what
[`composeManifest`](#three-files) read from a three-file folder. An agent
folder is saved with `saveFolder(await packFolder(dir))`; see
[Agent manifests](/docs/build/agent-manifests#save-from-code). `agentManifest(input)`
checks any of them locally, against the schema the Runtime enforces, and
answers the document with `manifestSchemaVersion` filled in.

The SDK refuses before it sends anything: an agent with a tool that runs your
code (`tool({ run })`), since the Runtime runs agents from their manifests
alone; a tool's `approval`, since the Tenant's
[tool policy](/docs/run/tool-policy) decides which calls wait; and a
[`requires`](#requires) the runtime section misses (`ManifestGapsError`). It
uploads the skill and runtime files the Runtime does not hold, then saves.

`save` answers the version saved, or the latest with `unchanged: true` when
nothing differs, and the `warnings` of the save. Each version records its
`hash`, when it was saved, who saved it (`savedBy`), and how many `agents`
were made from it. **A save moves every Agent that tracks the manifest**: see
[Versions](/docs/run/agents#versions). Request and response shapes are on
[Save an agent manifest](/reference/runtime/agents/manifests/save-an-agent-manifest).

## Fields [#fields]

| Field                   | Meaning                                                                                                                                       |
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `$schema`               | For editors: `https://docs.nylorun.com/schemas/agent-manifest/v8.json`.                                                                       |
| `manifestSchemaVersion` | `8`.                                                                                                                                          |
| `name`                  | 1 to 64 letters, digits, `.`, `_`, and `-`, starting with a letter or digit. Model budgets of scope `manifest` and tool policy rules name it. |
| `description`           | Up to 1024 characters by default.                                                                                                             |
| `metadata`              | Your own JSON.                                                                                                                                |
| `harness`               | The agent. See below.                                                                                                                         |
| `runtime`               | What the Runtime provides. See below. Leave it out for kind `none`.                                                                           |

The `harness` section is what you author with the
[Agent builder](/docs/build/agent), or by hand:

| `harness` field | Meaning                                                                                                                                           |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`            | The harness that runs the loop. Default `nylorun`, ours. Another needs `runtime.kind: "pod"` with an `image`. See [Harness](/docs/build/harness). |
| `instructions`  | See [Instructions](/docs/build/instructions).                                                                                                     |
| `tools`         | HTTP tools and agents used as tools. See [Tools](/docs/build/tools).                                                                              |
| `skills`        | Each skill may list the `secrets` slots its scripts read. See [Skills](/docs/build/skills).                                                       |
| `mcpServers`    | Each server may name the `credential` slot it authenticates with. See [MCP](/docs/build/mcp).                                                     |
| `plugins`       | The plugins folded in. See [Plugins](/docs/build/plugins).                                                                                        |
| `outputSchema`  | The JSON Schema of a structured answer.                                                                                                           |
| `loop`          | The harness's own settings, passed to it with each turn.                                                                                          |
| `memory`        | `{ access, instructions }`. See [Agent memory](/docs/run/memory).                                                                                 |

A flow's `harness` section is `kind: "workflow"` with `root` and `agents`
instead; it always runs in our harness. See [Flows](/docs/build/flows).

The `runtime` section:

| `runtime` field        | Meaning                                                                                                                                                                                                                     |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `kind`                 | Required. `virtual`, `pod`, or `none`. See [Kinds](/docs/run/agents#kinds).                                                                                                                                                 |
| `image`                | Pods: the workload image. Default the Tenant's, else `python:3.13-slim`.                                                                                                                                                    |
| `resources`            | `{ cpus, memory }`, such as `{ cpus: 2, memory: "2GiB" }`. Default 2 CPUs and `1024MiB`, at most the Tenant's limits.                                                                                                       |
| `storage`              | Pods: the volume's size, default `5GiB`.                                                                                                                                                                                    |
| `network.allowedHosts` | Hosts the sandbox may reach, within the Tenant's network limit. No egress without it. Pods also take `*.example.com` patterns.                                                                                              |
| `lifecycle`            | `{ idle, deleteAfter }`. See [Lifecycle](/docs/run/agents#lifecycle).                                                                                                                                                       |
| `setup`, `files`       | Pods: prepare a new disk. See [Setup and input files](#setup-and-input-files).                                                                                                                                              |
| `limits`               | `artifacts` (`fileBytes`, `totalBytes`), `skills` (`totalBytes`), and `disk` (`versionBytes`, `versionEntries`): lower than the Tenant's limits for Agents made from this manifest. See [Runtime limits](/docs/run/limits). |
| `secrets`              | The [secret slots](#secret-slots) an Agent made from it needs.                                                                                                                                                              |
| `triggers`             | What starts its turns with no client connected. See [Triggers and wakes](/docs/run/triggers).                                                                                                                               |
| `selfWake`             | `true` lets the agent book its session's next turn. See [Triggers and wakes](/docs/run/triggers#wakes).                                                                                                                     |

Private networks, loopback, the host, and cloud metadata endpoints are always
blocked, whatever `allowedHosts` says.

## Sections that fit [#sections-that-fit]

The Runtime checks the two sections against each other when a manifest is
saved, and refuses every gap at once with `422 manifest_inconsistent`, each in
`details.gaps`:

* A custom harness (`harness.id` other than `nylorun`) needs `runtime.kind:
  "pod"` with an `image`.
* An MCP server's or HTTP tool's `credential` names a `bearer` or `headers`
  slot of `runtime.secrets` bound to its exact URL.
* A skill's `secrets` name `environment_secret` or `environment_variable`
  slots.
* Skills need a sandbox: `virtual` or `pod`.

A gap reads like
`harness.mcpServers.github.credential "GITHUB_TOKEN" is not a secret slot in runtime.secrets`.
A server that names no `credential` still matches a secret by its URL. A save
is also refused (`400`) when it breaks the Tenant's limits or names files the
Runtime does not hold (`definition_files_missing`), and a skill whose files
hold more than the skill limit is `413 limit_exceeded`.

### Requires [#requires]

`requires` is a contract a harness carries for any runtime it is combined
with. It never reaches the Runtime: `.requires({ sandbox, secrets })` on the
[Agent builder](/docs/build/agent) and a harness file's `requires` are checked
by the SDK, which throws `ManifestGapsError` and sends nothing, and by
`nylorun apply`, which saves nothing and exits 1:

| `requires`       | The runtime section needs        |
| ---------------- | -------------------------------- |
| `sandbox: "any"` | `kind` `virtual` or `pod`.       |
| `sandbox: "pod"` | `kind` `pod`.                    |
| `secrets: [...]` | Each name declared in `secrets`. |

## Check before you save [#check-before-you-save]

`plan()` saves nothing and answers what a save would do. `planFolder()` does
the same for a manifest's files, and adds the compiled `manifest` and the
compiler's `diagnostics`. `nylorun apply --dry-run` and
`PUT /v1/agent-manifests/{name}?dryRun=true` do the same.

```ts
import { readFile } from "node:fs/promises";

const document = JSON.parse(await readFile("manifests/triage.json", "utf8"));
const plan = await client.agentManifests.plan(document);

if (plan.errors.length > 0) throw new Error(plan.errors.join("\n"));
console.log(`v${plan.from} -> v${plan.to}`, plan.changes, plan.warnings, plan.moves, plan.behind);
```

| Field        | Meaning                                                                                                                                                                      |
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `from`, `to` | The latest version (`null` for a new manifest) and the version a save would write.                                                                                           |
| `unchanged`  | `true` when nothing differs from the latest.                                                                                                                                 |
| `changes`    | Each changed field: `{ section, path, op, from, to }`, `section` being `manifest`, `harness`, or `runtime`.                                                                  |
| `errors`     | Every reason the save would be refused: the gaps between the sections first, then the Tenant's limits and missing files.                                                     |
| `warnings`   | `{ code, message }`, never a refusal: `image_tag` (an image tag rather than a digest), `pods_unavailable` (kind `pod` with no sandbox pods), `agents_move`, `agents_behind`. |
| `moves`      | The tracking Agents a save moves: `now`, at their pod's next start (`nextStart`), or once their required secrets are set (`secrets`).                                        |
| `behind`     | The Agents that stay on an older version: pinned ones, and tracking ones whose kind, harness, or storage the version changes.                                                |

A plan uploads the files the manifest names, since files are stored by their
content.

## Save under If-Match [#save-under-if-match]

Pass the version you read as `ifMatch`, `0` for a manifest that does not exist
yet. When someone saved since, the save is refused with `412 manifest_changed`:
read the manifest again and retry.

```ts
const latest = await client.agentManifests.get("triage");

await client.agentManifests.save(
  { ...latest.manifest, description: "Sorts new GitHub issues" },
  { ifMatch: latest.version },
);
```

The HTTP header is `If-Match: <version>`, and the CLI flag
`--if-match <n>`. `nylorun apply` saves under `If-Match` set to the version it
planned against.

## Versions and references [#versions-and-references]

An Agent names its manifest when it is made, and again with each `put`:

| `manifest`   | The Agent                                                                   |
| ------------ | --------------------------------------------------------------------------- |
| `"triage"`   | Tracks the manifest: runs its latest version now, and moves with each save. |
| `"triage@3"` | Runs version 3 and stays there.                                             |

| Call                                               | Does                                                                                       |
| -------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `client.agentManifests.get(name)`                  | The latest version. `get(name, 3)` reads version 3.                                        |
| `client.agentManifests.list()`                     | Each manifest's latest version.                                                            |
| `client.agentManifests.versions(name)`             | Every version, newest first, with who saved it and how many Agents were made from each.    |
| `client.agentManifests.delete(name)`               | Delete every version. Refused with `409 manifest_in_use` while an Agent was made from one. |
| `client.agentManifests.source(name, version)`      | The folder a version was saved from. A version saved as JSON has none (`404`).             |
| `client.agentManifests.sourceDiff(name, from, to)` | The files added, removed, and changed between two versions' folders.                       |

A version saved from [files](/docs/build/agent-manifests#the-source)
has a `source`: its `compiler`, `files`, and `bytes`. Its files can be read
back one by one, compared with another version's (see
[Compare versions](/docs/build/agent-manifests#compare-versions)), or written
back to disk with `nylorun pull <name>[@<n>]`.

Reading a manifest takes `agents:read`, `agents:write`, or
`agent-manifests:write`. Acting for a person, saving and deleting take
`agent-manifests:write`. A trusted issuer's token reaches no manifest.

## Secret slots [#secret-slots]

`runtime.secrets` declares what an Agent made from the manifest needs: each
secret's name, its type, its binding, whether it is `required`, and a
`description`. A manifest never holds a value.

| `type`                 | Binding                                              |
| ---------------------- | ---------------------------------------------------- |
| `bearer`               | `url`, `via?`, `identity?`                           |
| `headers`              | `url`, `via?`, `identity?`                           |
| `environment_secret`   | `allowedHosts` (1 to 32 hosts by default), `inject?` |
| `environment_variable` | None                                                 |

The Agent's view lists each slot in `slots`, with `required` and whether it is
`set`. Write a value with `{ value }` (or `{ headers }` for a `headers` slot):
the binding comes from the slot, so app code cannot widen where a key goes. A
name the manifest does not declare is `409 secret_not_in_manifest`. See
[Secret slots](/docs/run/credentials#secret-slots).

```ts
await client.agents.secrets.put("users/alice/triage", "GITHUB_TOKEN", {
  value: process.env.ALICE_GITHUB_TOKEN!,
});
```

## Setup and input files [#setup-and-input-files]

A pod's disk can start prepared. `files` copies files in, then `setup` runs its
commands in order:

```ts
const requirements = await client.artifacts.upload("pandas==2.2.3\n", {
  name: "requirements.txt",
});

await client.agentManifests.save({
  name: "analyst",
  harness: { instructions: ["Analyse the data you are given with pandas."] },
  runtime: {
    kind: "pod",
    image: "python:3.13-slim",
    network: { allowedHosts: ["pypi.org", "files.pythonhosted.org"] },
    files: [
      {
        artifactId: requirements.artifact.artifactId,
        version: requirements.version.version,
        path: "/workspace/requirements.txt",
      },
    ],
    setup: [
      { command: "python -m venv .venv && .venv/bin/pip install -r requirements.txt", timeout: "15m" },
    ],
  },
});

await client.agents.put("projects/acme", { manifest: "analyst" });
```

| Field     | Meaning                                                                                                                                                                                                                                     |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `files[]` | `{ artifactId, version, path }`: a version of a Tenant-wide artifact. A file artifact is written at `path`; a folder artifact is unpacked under it. Or `{ path, files }`: [files by hash](#files-by-hash). `path` is `/workspace` or below. |
| `setup[]` | `{ command, cwd?, timeout? }`, run with `/bin/sh -c`. `cwd` defaults to `/workspace`; `timeout` defaults to `10m`, at most `30m` by default.                                                                                                |

At most 16 of each by default; the Runtime limits `environmentSetupCommands`,
`environmentFiles`, and `setupCommandMs` set these bounds (see
[Runtime limits](/docs/run/limits)). They run once per new disk (created or
reset), before the first turn, with the Agent's variables, secrets, and
egress. A resume keeps what they made. A failed command fails the pod and the
turn waiting for it, with an `agent.failed` event that carries its exit code
and the end of its output; the next turn start runs the setup again. A version
that changes `setup` or `files` applies at the pod's next start, which runs
the setup again on the same volume. A virtual Agent refuses both with `400`.

## Files by hash [#files-by-hash]

A `files` entry can name files by content hash instead of an artifact version,
so the manifest moves to another installation with its files.
`runtimeFiles(path, contents)` hashes them, and `save` uploads those the
Runtime lacks:

```ts
import { runtimeFiles } from "@nylorun/agents";

const kit = await runtimeFiles("/workspace/kit", {
  "bin/run.sh": "#!/bin/sh\necho ready\n",
});

await client.agentManifests.save(
  {
    name: "analyst",
    harness: { instructions: ["Analyse the data you are given."] },
    runtime: {
      kind: "pod",
      files: [kit.entry], // { path: "/workspace/kit", files: { "bin/run.sh": "sha256:…" } }
      setup: [{ command: "sh kit/bin/run.sh" }],
    },
  },
  { files: kit.bytes },
);
```

Each file is copied under `path` at its relative path, before `setup` runs. A
file holds at most the Tenant's skill limit (2 GiB by default), and an entry
names at most 1000 files by default (`environmentFilesPerEntry`). A file the
Tenant does not hold is refused when the manifest is saved
(`definition_files_missing`). In a [runtime file](#three-files),
`{ "path": "/workspace/kit", "from": "./kit" }` names a folder beside the file,
which `nylorun apply` hashes and uploads the same way.

## Three files [#three-files]

<Callout title="Deprecated">
  The three-file format is deprecated. Write an
  [agent manifest](/docs/build/agent-manifests) instead:
  `npx nylorun migrate folder <dir>` converts one, offline.
</Callout>

Before `AGENT.md`, a manifest written as files was three JSON files, one
folder per agent, so that many agents share one runtime file. `nylorun apply`
and `composeManifest()` still read them, keep no source, and warn on each,
naming `nylorun migrate folder`:

```text
triage: warning: agent.manifest.json (the three-file format) is deprecated: nylorun migrate folder agents/triage writes it as an agent folder (AGENT.md), which saves the same manifest. It is still read until a later release.
```

```json title="agents/triage/agent.manifest.json"
{
  "$schema": "https://docs.nylorun.com/schemas/agent-manifest/v8.json",
  "manifestSchemaVersion": 8,
  "name": "triage",
  "description": "Sorts new GitHub issues",
  "harness": "./harness.manifest.json",
  "runtime": "../../shared/virtual.runtime.json"
}
```

```json title="agents/triage/harness.manifest.json"
{
  "$schema": "https://docs.nylorun.com/schemas/harness-manifest/v1.json",
  "instructions": ["Label and route new issues."],
  "mcpServers": {
    "github": {
      "name": "github",
      "type": "streamable-http",
      "url": "https://api.githubcopilot.com/mcp/"
    }
  },
  "credentials": { "github": "GITHUB_TOKEN" },
  "requires": { "sandbox": "any" }
}
```

```json title="shared/virtual.runtime.json"
{
  "$schema": "https://docs.nylorun.com/schemas/runtime-manifest/v1.json",
  "kind": "virtual",
  "network": { "allowedHosts": [] },
  "secrets": {
    "GITHUB_TOKEN": { "type": "bearer", "url": "https://api.githubcopilot.com/mcp/", "required": true }
  }
}
```

| File                    | Holds                                                                                                                                                                                                                                                                                                                                  |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `agent.manifest.json`   | The name, description, and metadata, with `harness` and `runtime` as paths (or inline).                                                                                                                                                                                                                                                |
| `harness.manifest.json` | The harness section. `skills` may be a folder of skill folders (each `SKILL.md` may list its `secrets`), `mcpServers` a path to an `mcp.json`, and `plugins` plugin folders. `credentials` maps an MCP server or HTTP tool to its slot; `requires` is checked against the runtime; `memory` turns on [agent memory](/docs/run/memory). |
| `runtime.manifest.json` | The runtime section. A `files` entry may be `{ path, from }`: a folder beside the file.                                                                                                                                                                                                                                                |

Each path is relative to the file that names it. The two section files have
their own schemas, `https://docs.nylorun.com/schemas/harness-manifest/v1.json`
and `https://docs.nylorun.com/schemas/runtime-manifest/v1.json`.

`nylorun apply <dir>` composes the folder into one document, prints the plan
(the version it moves from and to, each change, warnings, and errors), and
saves under `If-Match`, uploading the files the Runtime does not hold. Errors,
gaps between the sections included, save nothing and exit 1. A folder with
neither an `agent.manifest.json` nor an `AGENT.md` applies each subfolder that
has one, so `npx nylorun apply agents/` saves every agent, `AGENT.md` and
three-file folders alike; every folder composes or compiles before anything is
sent. From code, `composeManifest` does the same:

```ts
import { composeManifest } from "@nylorun/agents";

const composed = await composeManifest("agents/triage");
await client.agentManifests.save(composed);
```

`composed.gaps` lists what the Runtime would refuse and what `requires` misses;
`save` refuses those with `ManifestGapsError`. A file that cannot be read or
does not validate throws `ComposeError`. To write the three files from an
agent saved before agent manifests, see
[Compatibility](/docs/compatibility). A shared runtime file has a folder
equivalent: [`extends`](/docs/build/agent-manifests#extends) in `runtime.json`.

## CLI [#cli]

| Command                                                                                     | Does                                                                                                                                                                                                                                                                                                |
| ------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `nylorun init <path> [--example]`                                                           | Write `<path>/AGENT.md`, a new [agent manifest](/docs/build/agent-manifests) named after the path's last segment. `--example` adds an HTTP tool, `mcp.json`, and a trigger.                                                                                                                         |
| `nylorun check [<dir>...] [--fix] [--json]`                                                 | Compile each agent manifest offline and print every problem by file, line, and column. `--fix` makes the fixable edits; `--json` is for CI.                                                                                                                                                         |
| `nylorun apply [<dir>...] [--dry-run] [--if-match <n>] [--agent [<id>]] [--watch [--save]]` | Plan and save each folder's manifest. `--dry-run` prints the plans, and the manifest compiled from each folder, and saves nothing; `--if-match <n>` saves only over version `n` (one folder); `--agent` then makes or moves the Agent; `--watch` plans again on each change, and `--save` saves it. |
| `nylorun chat <agent> [--session <id>] [--message <text>]`                                  | Talk to an Agent: send each line, print each reply.                                                                                                                                                                                                                                                 |
| `nylorun export --folder <name>[@<n>] [--out <dir>]`                                        | Write a saved manifest out as files, for one saved from JSON or TypeScript.                                                                                                                                                                                                                         |
| `nylorun pull <name>[@<n>] [--out <dir>]`                                                   | Write the folder a version was saved from, byte for byte.                                                                                                                                                                                                                                           |
| `nylorun migrate folder <dir> [--out <dir>]`                                                | Rewrite a [three-file](#three-files) folder as `AGENT.md` and its files, offline.                                                                                                                                                                                                                   |
| `nylorun agent-manifests list [--json]`                                                     | Each manifest's latest version: its runtime kind, harness, and the Agents made from it.                                                                                                                                                                                                             |
| `nylorun agent-manifests get <name>[@<n>]`                                                  | The latest version, or version `n`, as JSON.                                                                                                                                                                                                                                                        |
| `nylorun agent-manifests versions <name>`                                                   | Its versions, with the Agents made from each.                                                                                                                                                                                                                                                       |
| `nylorun agent-manifests rm <name>`                                                         | Delete every version.                                                                                                                                                                                                                                                                               |
| `nylorun agents put <id> --manifest <name[@n]>`                                             | Make an Agent from it, tracking or pinned.                                                                                                                                                                                                                                                          |
| `nylorun agents list --outdated`                                                            | The Agents moving to another version of their manifest.                                                                                                                                                                                                                                             |

See [CLI](/docs/run/cli#save-agent-manifests).

## In the starter [#in-the-starter]

`npm create @nylorun/agent@beta` writes `runtime/scratch.runtime.json`, a
virtual workspace with no network:

```json title="runtime/scratch.runtime.json"
{
  "$schema": "https://docs.nylorun.com/schemas/runtime-manifest/v1.json",
  "kind": "virtual",
  "network": { "allowedHosts": [] }
}
```

`src/main.ts` saves each agent in `agents/index.ts` as an agent manifest with
that runtime section, and makes the Agent its sessions run on, named after the
agent, each time `npm run dev` or `npm start` runs it:

```ts title="src/main.ts"
import { readFile } from "node:fs/promises";
import { createClient } from "@nylorun/agents";
import { agents } from "../agents/index.js";

const client = await createClient();
const { $schema: _, ...runtime } = JSON.parse(
  await readFile(new URL("../runtime/scratch.runtime.json", import.meta.url), "utf8"),
);
for (const agent of agents) {
  const { manifest } = await client.agentManifests.save({ agent, runtime });
  const made = await client.agents.put(agent.id, { manifest: agent.id });
  console.log(`Saved agent ${agent.id} v${manifest.version} (Agent ${made.id} runs v${made.resolvedVersion})`);
}
```

The Agent tracks its manifest, so each save moves it, and each session's next
turn runs your edit. See [Your project](/docs/project).

## Studio [#studio]

The Tenant page's **Manifests** tab lists the manifests in one table: each
one's name and description, latest version, the kind of Agent it makes, how
many Agents were made from it, and when it was saved. A version saved from
files is marked beside its number. A manifest opens beside the list,
with a version picker, the Agents made from the version, and two views:
**Spec**, the version's harness and runtime sections as a readable spec, and
**Source**, the folder it was saved from, each file marked when it was added
or changed since the version before. Its ⋯ menu makes an Agent pinned to the
version (**New Agent from vN**) or shows the manifest as JSON.

Studio does not edit a manifest: edit its folder in its repository and save
it with `nylorun apply`, or with **Upload folder…**, which picks a folder in
the browser, shows the plan with the compiler's diagnostics, and saves it
under `If-Match`. See [Studio](/docs/run/studio#the-tenant).

## Next step [#next-step]

<Cards>
  <Card title="Agents" description="Make Agents from a manifest and move them between versions." href="/docs/run/agents" />

  <Card title="Secrets and credentials" description="Write the values a manifest's slots declare." href="/docs/run/credentials#secret-slots" />
</Cards>
