dystopic docs is in beta — content is actively being added.
dystopic
Reference

Register the agent

The full mechanics of agent registration — the SDK helpers and raw /api/agents endpoints, the draft→active lifecycle, the per-mode config shapes on the wire, the merge-vs-replace rules for updates, and the minimal manifest for the secondary dystopic agents push path.

Registering an agent creates (or updates) an agent row on the platform: a name, a mode, and a mode-specific config describing how the platform runs it. The primary surface is the SDK and the raw REST API — you build the payload in code and POST /api/agents. The web dashboard wraps the same endpoints for click-through registration. A secondary dystopic agents push path builds the identical payload from a small local manifest.

The walkthrough in /docs/flow/register-agent is the happy path. This page is the lookup surface: exactly what each call sends, how the mode is translated, and the sharp edges — code-mode wholesale replace, the update merge rules — that bite when you go off the golden path.

A "code agent" (a repo-backed Python agent) ships as mode: "sandbox" on the wire.

Register with the SDK

create_code_agent(...) builds the code-agent payload from a source directory (or an in-memory {path: text} mapping), POSTs it to /api/agents, and returns the created row. It lives in dystopic.odyssey.registration, which ships in the optional [odyssey] extra:

pip install 'dystopic[odyssey]'
from dystopic import DystopicClient
from dystopic.odyssey.registration import create_code_agent

agent = create_code_agent(
    name="research-agent",
    entrypoint="run",                 # top-level callable in entrypoint_file
    entrypoint_file="main.py",        # must be a key in the walked source tree
    source_dir="./agent_src",         # walked into {path: text}; .py only
    requirements=["httpx>=0.27", "trafilatura"],
    python_version="3.12",
    tools_schema=[...],               # OpenAI-style tool declarations
    ledger_schema=None,               # None → fully-open ledger (default)
    concurrency_cap=5,                # per-org concurrency (1–100)
    run_timeout_s=300,                # per-run wall clock (1–1800s)
    api_key="pk_live_…",              # org key from Settings → API Keys
)
agent_id = agent["id"]

Registration lands the agent as a draft. Move it to active with publish_agent:

client = DystopicClient(api_key="pk_live_…")   # base_url defaults to https://api.pipelines.tech
client.publish_agent(agent_id)

update_code_agent(*, agent_id, api_key, ...) mirrors create_code_agent but PUTs to /api/agents/{agent_id}. Its arguments are all optional so you can update a single field — but code-config changes carry the wholesale-replace rule below. build_code_agent_payload(...) is the pure payload builder underneath both, for callers that want the dict without the HTTP round-trip.

create_code_agent / update_code_agent register the agent only. They do not set the gate or the CI suite binding — those are separate endpoints.

Register with the raw REST API

The typed client exposes the three endpoints directly (dystopic.DystopicClient):

MethodEndpointPurpose
client.create_agent(payload)POST /api/agentsCreate the agent row (lands as draft).
client.update_agent(id, payload)PUT /api/agents/{id}Update an existing row.
client.publish_agent(id)POST /api/agents/{id}/publishMove draftactive.

The shared top-level payload:

{
  "name": "research-agent",
  "mode": "sandbox",
  "description": "…",
  "tools_schema": [ /* OpenAI-style tool declarations */ ],
  "ledger_schema": { /* optional typed entity ontology */ },
  "concurrency_cap": 5,
  "run_timeout_s": 300,
  "config": { /* mode-specific, below */ }
}

name and mode are required; the platform assigns id and status: "draft" on create. tools_schema, ledger_schema, concurrency_cap, and run_timeout_s are top-level. config carries the mode-specific execution recipe.

Code config — a Python entrypoint

The form create_code_agent produces. config holds the full source tree plus the entrypoint coordinates:

{
  "source_files": { "main.py": "def run(…): …", "search.py": "…" },
  "entrypoint_file": "main.py",
  "entrypoint": "run",
  "requirements": ["httpx>=0.27", "trafilatura"],
  "python_version": "3.12"
}
  • entrypoint must be a single Python identifier — dotted paths are rejected.
  • entrypoint_file must be a key in source_files. The SDK defaults it to main.py.
  • requirements is an inline list of pip specifiers; python_version pins 3.93.13.
  • Source caps (enforced client-side before the round-trip, mirroring the platform validator): max 50 .py files, 200,000 bytes per file, 1,000,000 bytes total. .py only; non-UTF-8 files and case-conflicting paths (Main.py vs main.py) are rejected; only __init__.py may be empty. Directory walks respect .dystopicignore (one glob per line, # comments) at the source root, appended to the built-in defaults (__pycache__/, .venv/, .git/, …). See /docs/reference/port-agent/code-mode and /docs/reference/port-agent/gotchas.

Command-runner config — a sandbox command

A repo-backed agent can instead ship as a plain command runner (also mode: "sandbox" on the wire). config requires a non-empty run_command; source and environment fields are optional:

{
  "execution_topology": "in_sandbox",
  "run_command": "python -m myapp",
  "source_files": { "relative/path.py": "…" },
  "system_packages": ["curl"],
  "setup_command": "apt-get update",
  "env": { "LOG_LEVEL": "info" }
}

The entrypoint keys (entrypoint, entrypoint_file, requirements, python_version) belong to code config and are forbidden here — install dependencies via setup_command or a dockerfile instead.

The draft → active lifecycle

create_agent / create_code_agent      publish_agent
POST /api/agents                  →    POST /api/agents/{id}/publish
status: "draft"                        status: "active"

The status is:

  • draft on create — you cannot create an agent straight to active.
  • Untouched on updatePUT /api/agents/{id} never changes status. Publishing only moves forward; there is no draft-revert through this surface.

publish_agent never creates: an agent must exist (and thus have an id) before it can be published.

Mode normalization

The wire mode is sandbox. One friendlier spelling normalizes before it reaches the platform: codesandboxcode is a client-side alias for a Python-entrypoint agent; the backend runs it in a sandbox, so the wire mode is "sandbox".

Update: merge vs. replace

Whether an update merges or replaces depends on the field. This is the single most important distinction to get right when re-pushing an agent.

Non-config fields — always partial-safe

description, tools_schema, ledger_schema, concurrency_cap, and run_timeout_s are partial-safe on PUT: fields you omit are left untouched by the platform's model_fields_set diff.

ledger_schema (via update_code_agent) is three-state:

  • omitted → left unchanged.
  • a dict → sets/replaces the ontology.
  • explicit None → clears it (reverts to the fully-open ledger).

A tool left on ledger_write_policy='adapter' while you clear ledger_schema (or vice versa) is rejected 422 — send the matching tools_schema in the same call when you flip either side.

Code config — replaced wholesale

The platform replaces a code agent's entire config blob whenever the request includes config; it does not merge a partial source tree. Any code-config change — source / entrypoint / entrypoint_file / requirements / python_version / topology — must re-ship the full file tree in the same call. update_code_agent enforces this client-side: passing any code-config field without source_dir / files raises a ValueError rather than silently dropping files.

This is the code-mode footgun: a partial PUT that drops files will drop them from the agent. Always send the complete, current source tree when updating a code agent.

Gate and CI suite are set separately

Registration attaches an agent's execution recipe — nothing else. The gate (PUT /api/agents/{id}/gate) and the CI suite binding (PUT /api/agents/{id}/ci-suite) are their own platform-authoritative endpoints, RBAC'd and audited, and are not touched by create_code_agent / update_code_agent or by the manifest below. Author suites, scenarios, and the gate on the dashboard or via the REST endpoints documented in /docs/reference/create-suite.

Secondary: dystopic agents push

For a repo-local workflow, dystopic agents push builds the same /api/agents payload from a minimal registration manifest and creates or updates the agent row. The manifest declares only the agent's identity and execution config — name, mode, and entrypoint/source. It never carries suites, scenarios, seeds, or the gate; those live on the platform.

agents:
  research-agent:
    mode: code
    entrypoint: run
    entrypoint_file: main.py
    source:
      dir: ./agent_src
dystopic agents push research-agent            # create or update from the manifest
dystopic agents push research-agent --publish  # …and POST /publish in one shot

On create, the numeric id returned by POST /api/agents is stamped back into the manifest by a naive, line-based text edit (not a YAML re-dump), so your comments, ordering, and formatting survive:

agents:
  research-agent:
    id: 4217          # <- stamped by dystopic agents push
    mode: code
    entrypoint: run
    entrypoint_file: main.py
    source:
      dir: ./agent_src

Write-back happens only on create. On update the manifest is read but never rewritten: once an id is present, push does a PUT /api/agents/{id}. Commit the stamped id and let push manage it — a hand-edited id that no longer matches the server row is PUT against the wrong agent without a drift check.

The command runner (mode: sandbox, run_command) shape is also accepted in the manifest, and additional code-config keys (requirements, python_version) may appear alongside entrypoint/source. See /docs/reference/port-agent/code-mode for the full field list. Building the payload for a code agent or any dir/file source upload needs the [odyssey] extra:

pip install 'dystopic[odyssey]'

Error reference

WhereErrorCause
Payloadunsupported mode <mode>mode is not sandbox / code.
Payloadsandbox mode requires a non-empty 'run_command'Command-runner config with no run_command.
Payloadcode mode requires a non-empty 'entrypoint'Code config with no entrypoint.
Payload'entrypoint' must be a single Python identifier …Dotted entrypoint path.
Payload'entrypoint_file' … is not present in the resolved source filesentrypoint_file not a key in the walked tree.
Payloadreceived code-config field(s) … without 'source_dir' or 'files'Partial code update without re-shipping the tree.
Payloadneeds the optional 'odyssey' extra …Code/dir/file source without dystopic[odyssey].
HTTPAgentAPIError / DystopicAPIError (409 / 422 / 401 / 403)Name conflict, validation failure, bad key, or missing org permission.

Next

With the agent registered and active, define the environment it runs against: /docs/flow/declare-world (walkthrough) or the declare-world reference. Then author its suite and gate in /docs/reference/create-suite.