Agent versioning and publishing

Use Agent versioning to keep an immutable record of an Agent’s configuration at a point in time, and to return the Agent to one of those snapshots.

How versioning works

When you edit a saved Agent, you work on a draft. Click Save to write your changes to the Agent’s saved configuration and record a draft snapshot.

Publishing records an immutable version snapshot and marks it as the Agent’s published version. Further edits create another draft.

Publishing does not gate what runs. Products, Surfaces, schedules, and every other entry point that names the Agent by ID execute the Agent’s saved configuration, so a save reaches them immediately, whether or not you publish afterwards.

Run a specific version

A request can name the version it wants instead of taking the saved configuration. On POST /v1/dispatch and POST /v1/agents/{agentId}/execute, supply exactly one selector beside the Agent identity:

  • agent.versionId runs that immutable version snapshot.
  • agent.alias runs whichever version the release alias currently points at. The live alias follows the saved configuration.

A selector runs a stored snapshot, so it cannot be combined with inline definition fields such as model, systemPrompt, or tools. Naming both alias and versionId is rejected, and an alias that does not exist answers 404 rather than falling back to the saved configuration. Selectors are not available for external or Claude Managed Agents.

A conversation binds to the first pointer it runs. A later turn on that same conversationId naming a different version or alias is refused with 409 and code: "session_pointer_mismatch"; start a new conversation for the other pointer.

Evaluate a version before deploying it

An eval run can name the version it evaluates, so the artifact you grade is the artifact you deploy. Supply the same selector on the eval request:

$curl -X POST https://api.runtype.com/v1/eval/suites/YOUR_SUITE_ID/run \
> -H "Authorization: Bearer YOUR_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{"agent": {"alias": "pr-482"}}'

Replace the following:

  • YOUR_SUITE_ID: the eval suite you want to run
  • YOUR_API_KEY: your Runtype API key
  • pr-482: the release alias you want to evaluate

POST /v1/eval/suites/{suiteId}/run, POST /v1/eval/run, and POST /v1/eval/submit all accept agent.alias or agent.versionId, and the MCP run_eval_suite and submit_eval tools expose them as agent_alias and agent_version_id. The CLI’s runtype eval run takes --alias <alias> or --version-id <agent-version-id>.

The run resolves the selector once, before its first case. Moving the alias while the run is in flight does not change what its later cases execute, and a retry re-runs the same version. The selector is a property of the run, not of the suite: a suite is a fixed case set, and which version you run it against is a run-time choice.

Every run records what it evaluated, and the eval read surfaces return it:

  • agentVersionId: the exact version every case executed.
  • agentTargetResolution: alias, version, or legacy-live-row for a run with no selector.
  • agentTargetAlias: the release alias the run resolved through.
  • caseManifestHash: SHA-256 over the case set the run used, so an edited or reordered suite is distinguishable from the one that produced the evidence.
  • evaluatorFingerprint: SHA-256 over the judges and grading configuration the run scored with.

Selectors are not available for external or Claude Managed Agents, on evals as on execution: an eval request naming one answers 422 with code: "selector_unsupported_agent_type". A suite targeting a Flow has no version selector and answers 400.

Memory namespaces and release pointers

Agent memory is keyed by the subject it is about, never by the Agent’s configuration version. A new version, a preview alias, and a request pinned to a versionId all read and write the same memory the Agent already had. Sharing is the default, and it stays correct until a preview alias starts writing memory that live traffic should not see.

Isolation is something you author. Three read-only variables carry the pointer that an execution resolved through into the memory profile template:

VariableValuePresent when
{{_agent.alias}}The release alias the request named, including liveThe request supplied agent.alias
{{_agent.versionId}}The immutable version the execution is pinned toThe request supplied agent.alias or agent.versionId
{{_agent.version}}That version’s human label, when the version carries one. Display only: free text, not unique, absent on an unlabelled versionThe pinned version row has a label

All three are absent when the request named no selector, and {{_agent.alias}} is absent when the request pinned a bare agent.versionId, because a version id belongs to no particular alias. A template variable Runtype cannot resolve disables memory for that execution rather than falling back to a shared bucket, so write a fallback ({{_agent.alias || "live"}}) unless you want memory to switch off on those runs.

The namespace ladder

Climb one rung only when the product needs it. Each rung shows the value the template resolves to for an end user dana on an Agent whose live alias and pr-482 preview both run. Runtype then prefixes that value with the owning organization and sanitizes the result into the stored profile ID, so the values below are the part you control.

1. Shared. Omit profileTemplate. Every version, alias, and pinned deployment shares one bucket per Agent.

profileTemplate: (unset, defaults to {{_agent.id}})
live -> agent_01j...
pr-482 -> agent_01j... (the same bucket)

Move on when the Agent serves more than one end user.

2. Per end user. Required on any Product Surface that serves many end users: Runtype disables memory outright when a multi-end-user surface resolves an identity-free template.

profileTemplate: {{_endUser.id}}
live -> dana
pr-482 -> dana (the same bucket)

Move on when a preview alias starts writing memory.

3. Previews isolated, live shared. Each preview alias gets its own bucket; live keeps the one it already had, so production memory is untouched by preview traffic.

profileTemplate: {{_endUser.id}}/{{_agent.alias || "live"}}
live -> dana/live
pr-482 -> dana/pr-482
no selector -> dana/live
agent.versionId -> dana/live

Changing the template changes the bucket, so the memory an Agent already accrued on rung 2 stays under the old profile and the live alias starts from empty at dana/live. Move up the ladder before the Agent has memory you care about, or copy the old bucket forward with the memory tools.

The || "live" fallback is what keeps ordinary traffic on one bucket: a request with no selector and a request pinned to a bare agent.versionId both resolve the fallback. If you want production traffic pinned to an exact version to stay on the shared bucket, deploy that version to an alias and select the alias, rather than sending a raw versionId.

Move on when behavior has to be reproducible per version.

4. Per version. Every version starts empty. Use this for regulated or evaluation-driven setups where a version’s behavior must not depend on memory another version wrote.

profileTemplate: {{_endUser.id}}/{{_agent.versionId || "live"}}
live -> dana/agentver_01j...
pr-482 -> dana/agentver_01k...
no selector -> dana/live
agent.versionId -> dana/agentver_01j...

Keep the fallback unless you mean it. Written as a bare {{_endUser.id}}/{{_agent.versionId}}, this rung disables memory entirely for every request that selects no version, which today is all ordinary traffic: selectors have no SDK, CLI, or dashboard surface yet. Validation reports MEMORY_POINTER_TEMPLATE_UNGUARDED when a pointer variable carries no fallback.

Key on {{_agent.versionId}}, never {{_agent.version}}. The label is free text, is not unique across versions, and is absent on a version nobody labelled, so it makes a poor bucket key.

5. Environment-mapped. When one Agent serves several environments, map the pointer to the namespace you want rather than adding a rung: {{_endUser.id}}/{{_agent.alias || "live"}} already does this for alias-per-environment setups, and a per-dispatch agentInput.memory.profileTemplate override lets a CI run choose its own bucket without editing the Agent. Two separate Agents remain the strongest separation, because they share nothing at all.

Seeding a preview bucket from live history is not automatic. Today it is a manual copy through the memory tools.

What happens on rollback and retargeting

The bucket follows the template, so:

  • Rollback (moving live back to an earlier version) keeps the same bucket on rungs 1 to 3, and moves back to that version’s own bucket on rung 4, where the earlier version reads what it wrote before.
  • Retargeting an alias (pointing pr-482 at a newer version) keeps the alias’s bucket on rungs 1 to 3, and starts a fresh bucket on rung 4.
  • Archiving or expiring a preview alias does not delete what it wrote. The bucket stays until you remove it.

Agents with a Tenancy Strategy

When an Agent sets a tenancyStrategy, Runtype computes the namespace from the resolved tenant and end user and ignores profileTemplate entirely, so the variables above have nothing to act on. Set memory.pointerScope instead:

  • "shared" (the default) keeps one bucket across every version and alias.
  • "alias" gives each release alias its own bucket.
  • "version" gives each version its own bucket.

pointerScope applies only to an Agent that sets a tenancyStrategy. On any other Agent the field is inert, because memory there resolves the template; Runtype reports MEMORY_POINTER_SCOPE_IGNORED rather than letting it look like isolation. A run that resolved no such pointer, such as a request with no selector, contributes no pointer segment and uses the shared bucket. The pointer is contamination hygiene, not an authorization boundary: preview aliases still share the organization’s credentials, records, and integration connections.

The validators report MEMORY_SHARED_ACROSS_POINTERS as a recommendation while an Agent enables memory and keys it on neither the variables nor pointerScope.

Version numbers

Runtype assigns an increasing number to each version snapshot. The interface displays labels such as v1 and v2. Saving creates a draft before you publish, so the first published version does not necessarily use v1.

Version entries can include a label, notes, a configuration summary, and a creation time.

Draft and published versions

The following table compares draft and published versions:

DraftPublished
Content that you can edit before publishingImmutable content that you can return to
Created when you save changes to an existing AgentCreated when you publish
Can become the published versionRemains in version history after replacement

View version history

To view version history, follow these steps:

  1. Open the Agent.
  2. Click More save actions, the arrow beside Save.
  3. Click View versions.

The Version History panel lists version labels, status badges, configuration summaries, creation times, and notes when available. The current published version has a live badge. A superseded published version has a previously published badge.

When you have unsaved changes, the panel shows a Current draft entry with an unsaved badge. Publishing a version discards those changes, so save first if you need them.

Load or publish a previous version

Each version has two actions:

  • Load retrieves the version’s stored snapshot and confirms it in a notification. It does not change the Agent or the editor.
  • Publish makes the version the Agent’s live configuration and marks it as published. Publish is unavailable on the version that is already live.

To return an Agent to an earlier version, follow these steps:

  1. In the Agent editor, click More save actions, then click View versions.
  2. Find the version that you want to return to.
  3. Click Publish beside it.

Publishing writes the version’s configuration back to the Agent and marks the version as published in one operation. The editor then reloads the saved Agent. Products, Surfaces, schedules, and other entry points run the restored configuration right away.

For Runtype and external Agents, the write covers the Agent’s name, description, icon, runtime configuration, capabilities, and external connection configuration recorded in the snapshot. Runtime configuration is replaced rather than merged, so settings added after the version was saved do not stay active accidentally.

Publishing also clears tool attachments that the version did not record: tools, MCP servers, runtime tools, subagent and code mode blocks, and approval settings. A tool that you attached after the version was saved does not survive publishing that version.

A version without a stored configuration cannot be published. The dashboard reports this when you click Publish.

What publishing cannot restore

Publishing restores every recorded field that can be changed atomically. It preserves these fields and reports them in applied and warnings:

  • Claude Managed runtime settings and capabilities. Runtype cannot atomically update its database and the hosted Anthropic Agent. Publishing therefore preserves the current runtime configuration, hosted settings, credential custody, and capabilities instead of claiming an unsafe partial synchronization. Update and save the Agent to synchronize a historical hosted definition.
  • Capabilities whose backing resource was deleted. If a historical capability refers to a Flow, Agent, or tool that no longer exists, Runtype preserves the current capability set and names the unavailable capability in a warning.
  • Fields missing from older snapshots. A historical snapshot without runtime configuration or capabilities preserves the current value and reports that coverage gap.

Publishing an Agent managed in code

When an Agent is managed by ensure, the editor shows a Managed in code badge. Publishing rejects the write by default so it cannot silently detach the Agent from code management. The dashboard opens a confirmation dialog; non-interactive clients must explicitly select overwrite. The next ensure run then detects the change as an external modification. For the conflict options, see Manage agents as code.

Publish from the API

POST /v1/agent-versions/{agentId}/publish atomically applies the stored snapshot to the live Agent and marks that version as published. Send versionId in the request body. The TypeScript SDK exposes the same operation:

1const result = await client.agentVersions.publish(agentId, versionId)
2console.log(result.applied, result.warnings, result.lastModifiedSource)

For an Agent managed by code, the default returns 409 with code: "managed_by_code_conflict". After explicit confirmation, use the spelling for your client:

  • REST and TypeScript SDK: onConflict: "overwrite"
  • CLI: runtype agent-versions publish AGENT_ID --version VERSION_ID --overwrite-managed
  • MCP: publish_agent_version with on_conflict: "overwrite"
  • Code Mode: { onConflict: "overwrite" }

Always inspect applied and warnings to see exactly which parts of the snapshot were restored.

POST /v1/agents/{id}/publish snapshots the Agent’s current saved configuration as a new published version.

Best practices

Use these practices to manage Agent changes:

  • Save small, incremental changes so each draft snapshot is easy to return to.
  • Remember that saving is what changes live behavior. Test the Agent before you save, and try a change on a non-production Product before you save it on an Agent that production traffic uses.
  • Review conversations and activity after you save.
  • To go back to an earlier configuration, click Publish beside that version in Version History, then read the notification for anything the publish could not restore.

Next steps

Continue with these Agent guides: