Agent manifest
agent.yaml is the deployable contract for one agent version. This page describes the current authoring fields: what each setting accepts, what it defaults to, and what rejects a manifest at registration.
For a guided introduction to the fields you touch first, read Manifest essentials.
Two rules apply everywhere:
- Unknown top-level keys are rejected. A typo fails the manifest rather than being ignored. The
one exception is a key prefixed
x-, which is accepted and carried through untouched. - A manifest is validated against a version, not an agent. Settings are captured at registration and are immutable for that version; you change them by publishing a new version.
A complete manifest
Section titled “A complete manifest”Most agents need about this much. Every field links to the section that defines it.
name: support-copilot # stable agent identityversion: 0.2.0 # labels one immutable buildframework: claude-agents-sdk # harness adapterentrypoint: agent:create_agent # python_module:functiondescription: Triage and draft responses to support requestskind: interactive # interactive | backgroundidentity_mode: service # pinned by the first version
model: claude-sonnetpermitted_models: [claude-sonnet, claude-haiku]
persistent: true # required for hosted versionssession: idle_timeout: 30m max_lifetime: 24hresources: cpu: 100m memory: 512Mi
network: egress: - api.zendesk.com # bare hostname, within the tenant ceiling
policy: approval_required_tools: [Bash] # a person decides before these run enable_ask_user: true scope_ceiling: [llm:claude-sonnet:invoke] allowed_audiences: [https://api.tanomis.com]
integrations: support: connector: global/support-desk # use an existing configured connector required: true tools: allow: [search_tickets, draft_reply] approval_required: [draft_reply]Field index
Section titled “Field index”Grouped by the section that defines each field. “Hosted: yes” means the hosted control plane requires it; lenient non-hosted profiles may default it.
Identity and packaging
Section titled “Identity and packaging”| Field | Type | Required | Default |
|---|---|---|---|
name |
string | yes | — |
version |
string | in practice | — |
framework |
enum | yes | — |
entrypoint |
string | yes | — |
description |
string | no | empty |
kind |
enum | yes | — |
Models and tools
Section titled “Models and tools”| Field | Type | Required | Default |
|---|---|---|---|
model |
string | no | resolved by the platform |
permitted_models |
list of string | no | empty |
permitted_tools |
list of string | no | empty |
Attribution and identity
Section titled “Attribution and identity”| Field | Type | Required | Default |
|---|---|---|---|
identity_mode |
enum | hosted: yes | service only in lenient non-hosted profiles |
inherits_user_permissions |
boolean | no | false |
identity.client_id |
string | no | assigned at registration |
State, sessions, and resources
Section titled “State, sessions, and resources”| Field | Type | Required | Default |
|---|---|---|---|
persistent |
boolean | hosted: yes | false only in lenient non-hosted profiles |
cr_enabled |
boolean | deprecated compatibility field | ignored for runtime selection |
settlement_mode |
enum | no | standard |
session.idle_timeout |
duration | yes | — |
session.max_lifetime |
duration | yes | — |
resources.cpu |
quantity | yes | — |
resources.memory |
quantity | yes | — |
resources.cpu_limit |
quantity | no | platform burst default |
resources.memory_limit |
quantity | no | platform burst default |
Network, policy, tools, and delegation
Section titled “Network, policy, tools, and delegation”| Field | Type | Required | Default |
|---|---|---|---|
network.egress |
list of string | no | empty |
network.egress_mode |
allowlist or public |
no | allowlist |
policy.* |
block | no | see the section |
integrations |
map of requirement | no | empty |
access.github |
block | no | none |
delegation.* |
block | no | empty |
Metadata, caps, and harness
Section titled “Metadata, caps, and harness”| Field | Type | Required | Default |
|---|---|---|---|
data_classification |
list of string | no | empty |
cloud_access |
list of string | no | empty |
cost_cap_usd |
number | no | 0 |
token_cap |
integer | no | 0 |
lifecycle_policy |
block | no | empty |
harness.<framework> |
block | no | harness defaults |
autonomy |
any | no | accepted, not yet applied |
Identity and packaging
Section titled “Identity and packaging”name: support-copilotversion: 0.1.0framework: claude-agents-sdkentrypoint: agent:create_agentdescription: Triage and draft responses to support requests.name— the stable agent identity, and the name every other command refers to. Must start with a letter or digit and contain only letters, digits, underscore, dot, or dash, up to 128 characters. A deploy that names a different agent than the manifest does is rejected.version— the label for one immutable build. Validation does not require it, but deploy does: supply it here or pass--versionon the command line. A published version is immutable, so publish a new label rather than rebuilding an existing one.framework— the harness adapter. One ofclaude-agents-sdkordeepagents; confirm the set your control plane offers withtanomis agent harnesses.entrypoint—module:function, resolved inside your packaged project. Exactly one colon, with both halves non-empty.description— free text shown in listings. Optional, and worth writing: it is what a person scanning the agent catalog reads.
Behavioral classification
Section titled “Behavioral classification”kind: interactivekind is required and is one of:
| Value | Meaning |
|---|---|
interactive |
Human in the loop, turn by turn. Latency-sensitive resource profile. |
background |
Runs without a person waiting on each turn. |
autonomy is accepted and stored verbatim but is not yet applied to behavior. Set it only if
you want the declaration on record.
Models
Section titled “Models”model: "your-workspace-model-id"permitted_models: - "your-workspace-model-id"model— the default model resolved for a run.defaultfollows the organization’s default model, resolved at each session start; any other value pins that model.permitted_models— the runtime allowlist. A run that requests a model outside this list is refused. Leaving it empty does not widen access; it means the manifest declares no allowlist and the model decision falls to platform and tenant policy.
Use model IDs available to the selected tenant. Whenever you change model, update the matching
llm:<model-family>:invoke entry in policy.scope_ceiling, or the declared ceiling stops
describing the model the agent actually calls.
Attribution and identity
Section titled “Attribution and identity”identity_mode: serviceinherits_user_permissions: falseidentity_mode— the agent’s intended authority posture. One ofservice(the agent’s own machine identity),delegated(intended user subject plus agent actor), orhybrid. Hosted deployments require an explicit value and reject omission; a lenient non-hosted profile may resolve a missing value toservice. The first non-rejected version pins it across every later version of the agent; a rejected initial attempt does not establish the pin, so agree the mode with your organization administrator before the first deploy.inherits_user_permissions— the gated impersonation opt-in. Illegal withidentity_mode: service, because there is no user to inherit from; usedelegatedorhybrid. Turning it on seeds an impersonation-approval gate rather than granting anything on its own.identity.client_id— the agent’s application identity. On a hosted control plane this is assigned at registration and an author-supplied value is rejected, so omit theidentityblock entirely.
Availability: Service-mode bind-time identity is enabled per tenant; delegated and scoped authority are held contracts. See What is available where.
For the actor model, hybrid-path boundary, denial sequence, and safe attribution guidance, read Agent identity and access.
State, warm starts, and sessions
Section titled “State, warm starts, and sessions”persistent: truesession: idle_timeout: 30m max_lifetime: 24hpersistent— provisions durable tenant-scoped storage and gives each session its own directory. It is required for new hosted versions.cr_enabled— deprecated. Older manifests may retain it during the internal rolling deployment, but true, false, and omission do not select a runtime in current runtime-api. The deploy pipeline and CLI still usetruetemporarily to request a golden build; that legacy consumer is removed by the golden-policy follow-up. RAM snapshot policy is platform-owned. Until universal-worker and golden-policy cutover, remote admission requirestruefor settlement or author-declared egress; that is a rollout barrier, not a tenant-facing compatibility period.settlement_mode— one ofstandard,work, orsettlement, in that order of strength; a missing value resolves tostandard.standardis the existing behavior.workmeans the agent produces durable work records.settlementrequires the session to establish the managed worker/proxy boundary or fail activation.session.idle_timeout/session.max_lifetime— both required. Compact Go-style durations (45s,30m,24h), positive, and resolving to whole seconds.1500msis rejected.
Availability: A snapshot is an optimization, not the execution-boundary selector. Durable work and settlement are enabled per tenant; a settlement-mode session fails activation where the boundary is unavailable. See What is available where.
Resources
Section titled “Resources”resources: cpu: 100m memory: 512Mi cpu_limit: 2 memory_limit: 2Gicpu and memory are the requests — the reserved baseline that drives scheduling and density.
They are required. cpu accepts a Kubernetes CPU quantity (500m, 2); memory accepts a binary
quantity (512Mi, 2Gi) — decimal suffixes like 500M are rejected.
cpu_limit and memory_limit are the optional burst ceiling. When unset the platform applies a
generous default so a run can spike above its request. Set them to raise or pin the ceiling for one
agent, subject to tenant policy.
Leave the requests at their scaffolded values until a measurement gives you a reason to change them.
Network
Section titled “Network”network: egress: - github.com - api.example.comnetwork.egress_mode defaults to allowlist. Set it to public to permit arbitrary public
hostnames through the managed proxy. In public mode, egress must be absent or empty. An unset
tenant ceiling permits this mode; any configured hostname ceiling rejects it. Private and reserved
addresses remain blocked. Public mode requires capable node/helper versions and the external route;
see Restrict network egress.
network.egress is the list of external hostnames this agent may reach. It is part of the
version’s contract: captured at registration, immutable for that version, and enforced at run time.
Availability is per tenant. Ask your administrator whether author-declared egress is enabled for the selected tenant before relying on it; where it is not, the list is still validated and recorded but no external route exists.
What an entry may be
Section titled “What an entry may be”Each entry is a bare hostname, written in canonical form — lowercase, no surrounding whitespace, no trailing dot. Anything else is refused at deploy rather than quietly rewritten, so the value you wrote is the value that is enforced.
Refused, each with the reason named:
| Not allowed | Why |
|---|---|
*.github.com |
Wildcards are not permitted in a declared ceiling. An entry already covers its subdomains. |
https://github.com, github.com:443, github.com/path |
The match is on host alone; a scheme, port, or path would be silently ignored. |
140.82.121.4 |
An address carries no DNS identity, and the allowlist matches the host an agent asks for. |
the loopback name, svc.cluster.local, printer.local, vault.internal |
Names for the sandbox itself or the in-cluster world. Platform destinations are added for you and are not declarable. |
GitHub.com, github.com. |
Not canonical. Write github.com. |
github |
A hostname needs at least two labels. |
An entry covers its subdomains. github.com admits api.github.com and a.b.github.com. It
does not admit notgithub.com or github.com.example. Declare the narrowest name that covers what
you need.
The tenant ceiling
Section titled “The tenant ceiling”Your administrator can set a per-tenant ceiling that your declaration is intersected with. It only ever narrows: it cannot add a destination you did not declare, and it never affects the platform’s own routes.
An unconfigured tenant admits any public hostname, so an unset ceiling behaves as though only your manifest applied. Where a ceiling excludes an entry, the deploy fails naming both the entry and the ceiling that excluded it, before any image is built — so you find out in seconds rather than after a build.
A ceiling of github.com admits a declaration of api.github.com, because that is narrower than
what the administrator permitted.
What is enforced, and where
Section titled “What is enforced, and where”- At deploy. The grammar and the tenant ceiling, before a build is created.
- At rollout. The ceiling again, when the version is enabled for a tenant — the point at which a globally published agent first has a tenant to be measured against.
- At run time. Every connection is mediated. A destination outside the list is refused. A declared hostname that resolves to a private, loopback, link-local, or in-cluster address is refused too, and the connection is made to the address that was checked, so a name whose record changes cannot redirect it.
The platform’s own destinations — the managed model gateway and the governed connector routes — are always available and are never declared here. Your list can only add to them.
Requirements
Section titled “Requirements”Egress enforcement runs in the managed worker’s session-scoped proxy for both snapshot restore and
clean bootstrap. If that boundary cannot be established, activation is refused instead of running
unproxied. During the internal mixed-runtime rollout only, remote admission may require the legacy
cr_enabled: true spelling so an older runtime replica cannot accept a contract it cannot enforce.
Policy
Section titled “Policy”policy: approval_required_tools: - Bash enable_ask_user: false enable_claude_md: false enable_context_files: false scope_ceiling: - "llm:your-model-family:invoke" allowed_audiences: - "https://your-workspace-api.example" approval_thresholds: - condition: "scope:payments.*" approver: workspace-adminHuman-in-the-loop
Section titled “Human-in-the-loop”approval_required_tools— every listed tool pauses the run and emits a permission request that the console renders as an allow/deny card. Names are the harness’s own tool names.enable_ask_user— exposes the harness question flow, letting the agent ask a person a question mid-run and wait for the answer.enable_claude_md—claude-agents-sdkonly. Lets the agent read a projectCLAUDE.mdfrom its workspace as a context file. Ignored on other harnesses.enable_context_files— accepted for forward compatibility with a broader context-file surface. Today the only such surface is gated byenable_claude_md.
approval_required_tools and enable_ask_user produce the durable runtime interactions described
in Handle approval-required actions. That application flow is
separate from approval_thresholds below, whose scoped-token completion path remains held.
Authorization contract
Section titled “Authorization contract”-
scope_ceiling— the maximum set of concrete capabilities this agent’s tokens may ever carry. The broker intersects requested scopes with it; it never widens them. Each entry must match exactly one of three grammars, and wildcards are rejected:Form Example <resource>.<action>calendar.readmcp:<server>:<tool>:<verb>mcp:github:create_issue:writellm:<model>:<capability>llm:your-model-family:invokeOn a hosted control plane the ceiling is required; where it is optional, an absent ceiling means allow-all with a warning.
What the ceiling governs. It bounds what this agent may do acting as itself, in two places:
- Agent-to-agent delegation. On a delegated call the caller’s ceiling is intersected with the callee’s, and only scopes inside both are granted. An agent with no ceiling cannot receive a delegated grant at all.
- Token exchange (held contract). If a future supported flow exchanges a token against one
of the agent’s
allowed_audiences, a requested scope outside the ceiling is refused. No supported public runtime workflow currently exposes this scoped exchange.
What it does not govern. Three settings look like “what may this agent do”, and they are separate mechanisms — sizing the ceiling for these will not work:
Concern Where it is actually enforced Which model a run may use permitted_modelsWhich MCP tools an agent may call integrations.<key>.tools.allowintersected with the current workspace grantWhat the agent may do as itself scope_ceilingIn particular, an
mcp:<server>:<tool>:<verb>entry is a well-formed ceiling scope, but adding one does not grant an MCP tool call and omitting one does not block it — see Governed MCP connectors. Entries are captured at registration and are immutable for that version, so declare the ceiling you need and widen it in a later version rather than padding it defensively. -
allowed_audiences— thehttps://resource indicators the agent’s minted tokens may target. Required oncescope_ceilingis non-empty, and each entry must be anhttps://URI. Use the audience issued for your tenant/environment; never copy one from another environment. -
approval_thresholds— maps a sensitivity condition to an approver. In the held delegated exchange contract, matching a sensitive scope classifies the request as approval-required; no supported durable approval ID, decision endpoint, or resume path currently completes that exchange.conditionis required and is a sensitivity pattern, so unlike a grant it may use wildcards.approveris optional and defaults to the invoking user. See Agent identity and access before designing around these fields.
Both scope_ceiling and allowed_audiences are re-checked for presence on each new version, so a
new version can widen them — which is exactly the decision an agent’s author should be making
deliberately.
Declared capability metadata
Section titled “Declared capability metadata”permitted_tools: []data_classification: - internalcloud_access: - object-storagecost_cap_usd: 25token_cap: 2000000lifecycle_policy: required_gates: - regression-suite - prompt-injection-suite gate_categories: prompt-injection-suite: adversarial rollback: manualpermitted_tools— the tool allowlist carried into the policy decision point. An empty list declares no manifest-level restriction; enumerate tools to narrow what the agent may run.data_classificationandcloud_access— the data sensitivity and cloud-resource classes the agent declares. Recorded on the version and surfaced through the catalog for review.cost_cap_usdandtoken_cap— declared budgets for the version. Must not be negative.lifecycle_policy.required_gates— the evaluation gates that must pass before the gate-guarded promotion.lifecycle_policy.rollbackismanualorauto; empty resolves tomanual.lifecycle_policy.gate_categoriesmaps a required gate to the kind of eval suite behind it:quality,robustness, oradversarial. A gate left out isquality. Each key must name a gate inrequired_gates, and any other value is refused at registration. The version’s gate listing (GET /v1/agents/{name}/versions/{version}/gates) returns the category on every gate so robustness and adversarial suites can be shown apart from quality evals.
data_classification, cloud_access, cost_cap_usd, and token_cap are recorded as version
metadata and exposed for governance review. Treat them as the agent’s declaration of intent, and
keep the enforcement in permitted_models, permitted_tools, and policy.scope_ceiling.
Governed MCP connectors
Section titled “Governed MCP connectors”An agent references a configured connector directly. No catalog association, contract publication, or connection-selection step is needed.
integrations: finance: connector: global/alphavantage-workspace required: true tools: allow: [SYMBOL_SEARCH, COMPANY_OVERVIEW, EARNINGS]Find the connector and its approved tools:
tanomis mcp connector listtanomis mcp connector tools CONNECTOR_IDUse global/NAME for a platform connector, tenant/NAME for a connector owned by the
agent’s workspace, or an exact connector ID. An unqualified name works when unambiguous.
A global agent can reference global connectors; publish a tenant-scoped agent when it needs a
tenant-owned connector. The integration map key is just the agent’s local name.
The connector supplies the endpoint, credentials and shared/personal account mode. Personal
connectors still require each caller to connect their own account. Credentials never belong in
the manifest. An optional identity: workspace or identity: user can assert the expected mode;
it must match the connector.
tools.allow is the maximum set the agent may use. Optional tools.approval_required lists tools
that require confirmation for each call and must be a subset of tools.allow. The connector’s
current permitted tools and account access can narrow this set, never expand it.
At registration, the platform records the connector ID and approved tool schemas automatically. These platform-owned details appear separately from author configuration in the Manifest tab. Deleting and recreating a connector with the same name does not redirect an existing version.
GitHub repositories
Section titled “GitHub repositories”An agent names the GitHub repositories it may use, and what it may do in each, under
access.github. Each run gets a GitHub token for exactly those repositories and capabilities;
git and gh use it automatically.
access: github: repos: anyfind/agent-platform-website-tanomis: can: [read, push, pull_request] anyfind/docs: can: [read]| Capability | Lets the agent | GitHub App permission |
|---|---|---|
read |
clone, fetch, read files and metadata (always included) | contents: read |
push |
push branches | contents: write |
pull_request |
open and update pull requests | pull requests: write |
issues |
create and comment on issues | issues: write |
There is no separate merge capability: push and pull_request together can merge a pull
request unless branch protection forbids it.
A global agent cannot know a customer’s repositories, so it declares a named slot instead. Each
workspace chooses the repository for the slot when it enables the agent (Agent → Integrations,
or tanomis agent access NAME --bind target=owner/name):
access: github: choose: target: can: [read, push, pull_request]What decides access:
- The tenant’s GitHub ceiling. A workspace admin sets which repositories agents may be given, and
how far, in Settings → Organization → GitHub (or
tanomis github ceiling set). A declaration outside the ceiling is refused at deploy or enablement with the gap named. Lowering the ceiling later makes affected runs fail with the same reason. - The GitHub App installation. A repository the workspace’s connected installation cannot see is refused at deploy.
- Every run. The intersection is checked again when a run starts and on every token request, and tokens are revoked when the run ends.
Rules:
- Repository names are GitHub
owner/name;owner/*is a ceiling pattern, not a declaration. - Slot names use lowercase letters, digits,
-and_. github.comandapi.github.comare added tonetwork.egressautomatically. That opens those hosts for every path: the run’s token limits what the agent can do on GitHub with platform credentials, not which GitHub repositories it can send data to.asks_firstis not supported yet and is rejected rather than ignored.- The retired top-level
repository:block is rejected with a rewrite hint, including when an older version that carries it is cloned or fanned out.
See GitHub repositories for connecting GitHub and setting the ceiling.
Changes and availability
Section titled “Changes and availability”- Changing the connector or allowed tools requires a new agent version.
- Rotating credentials on the same connector does not require a new version.
- Changed tool schemas require review and a new agent version.
- Agent → Integrations shows the declared connector, readiness and expandable allowed tools. Operators can disable or enable access for the agent; they cannot substitute another connector.
- A required connector that is unavailable blocks execution. An unavailable optional connector is omitted.
- Disabled access remains disabled when publishing another version or migrating an old declaration.
Existing catalog-based agent versions remain compatible. New agents should use the direct connector declaration above. No attachment creation or approval workflow is involved.
Agent-to-agent delegation
Section titled “Agent-to-agent delegation”# callerdelegation: calls: - summarizer - agent: reconciler skills: [reconcile]
# calleedelegation: accepts: - tenant skills: # optional - name: summarize description: Summarize a support thread and return the key facts and the customer's ask.The block has two independent halves. An agent may call, be called, both, or neither, and declaring neither is the closed default. The manifest is the grant: a caller names the agents it may hand work to, a callee names who may call it, and the intersection of the two, evaluated against the versions currently enabled for the tenant, is the authority. Nothing is created out of band and nothing has to be redone when either side deploys a new version.
-
calls— the agents this agent may delegate to. Naming a target puts the delegation tools on this agent’s bind, and it is the caller-side ceiling: an agent cannot reach a callee it did not name. An entry is a bare agent name, or a mapping withagentand askillslist that narrows the target to those skills. No wildcards. -
accepts— who may delegate to this agent. Empty means nobody.Value Meaning tenantAny agent in the same tenant. The widest class there is. selfOther versions of this same agent only. <agent-name>That one named agent. -
skills— optional. A callee that declares none exposes one implicit skill,default, whose input is apromptplus any further fields the caller passes, described by the agent’s owndescription. Declare skills only when the agent offers several distinct typed operations.nameis lowercase alphanumeric with underscores or dashes and unique within the list; renaming one breaks every caller that lists it.descriptionis required and functional, not decoration: a calling model reads it to choose a skill.input_schemais optional; when present it is enforced on every call. Schema hashes are derived by the CLI, never written by hand.
skills without accepts is rejected: the skills would be unreachable. accepts without
skills is fine and is the common case.
Harness settings
Section titled “Harness settings”Keep only the block matching framework. Unknown keys inside a harness block are rejected at
warmup with the offending key named.
claude-agents-sdk
Section titled “claude-agents-sdk”harness: claude-agents-sdk: {}The Claude Agent SDK harness takes no manifest options today; the empty block is the marker. Its
human-in-the-loop behavior comes from policy.approval_required_tools, policy.enable_ask_user,
and policy.enable_claude_md.
deepagents
Section titled “deepagents”harness: deepagents: is_async: true stream_modes: [messages, updates, custom] checkpointer: workspace-sqlite backend: workspace-composite recursion_limit: 50| Key | Values | Default |
|---|---|---|
is_async |
boolean | true |
stream_modes |
any of messages, updates, custom, values |
[messages, updates, custom] |
checkpointer |
workspace-sqlite, in-memory |
workspace-sqlite |
checkpointer_options.delta_channels |
boolean | true |
store |
workspace-sqlite, none |
none |
backend |
workspace-composite, author-provided |
workspace-composite |
allow_agent_model_override |
boolean | false |
allow_author_checkpointer |
boolean | false |
allow_author_store |
boolean | false |
recursion_limit |
positive integer | 200 |
recursion_limit_ceiling |
positive integer | 1000 |
enable_repl_middleware |
boolean | false |
subagent_event_attribution |
nested, flat |
nested |
recursion_limit must be less than or equal to recursion_limit_ceiling. The runtime clamps the
effective limit to the ceiling, so the ceiling is the real bound on a single run’s step count.
Leave backend, checkpointer, and the three allow_author_* switches at their defaults unless
you are deliberately taking ownership of workspace state; the defaults are what keep the workspace
and lifecycle under the runtime’s control.
Cross-field rules
Section titled “Cross-field rules”These are the checks that reject a manifest which is individually well-formed:
- New hosted versions require
persistent: true. - During the internal mixed-runtime rollout only, remote admission may require legacy
cr_enabled: truefor settlement or author-declared egress. The manifest validator itself does not make that field authoritative. inherits_user_permissions: trueis illegal withidentity_mode: service.- A non-empty
policy.scope_ceilingrequires a non-emptypolicy.allowed_audiences. integrations.<key>.tools.approval_requiredmust be a subset of that integration’stools.allow.delegation.skillsrequires a non-emptydelegation.accepts.harness.deepagents.recursion_limitmust not exceedrecursion_limit_ceiling.
Validate before you build
Section titled “Validate before you build”tanomis agent deploy . --tenant northwind-sandbox --dry-run--dry-run runs the full preflight, including the authorization contract, and packages locally
without creating a build or uploading anything. Use it: on the remote build path, registration is
the last step, so a manifest error found there costs you an image that has already been built
and pushed.