JJETSYNGet in touch

The agent runtime

How a run actually executes: one inbound event spawns one process, durable resume gives it memory of the last run on the same task, and it fans out sub-agents, renders its own UI, and re-runs its dependents when an upstream changes.

A run is one execution of your employee: one inbound event wakes one process, it works the folders, and it exits. Continuity, sub-agent fan-out, live UI, and dependency-aware re-run all build on that single mechanism. The runtime is a native Claude Code spine. Jetsyn wraps it with provisioning, credential isolation, and transport, and the stock claude process assembles its own prompt from the cell's files.

The lifecycle of a run#

The employee is not a daemon. There is no long-lived agent to attach to. The cell persists on its volume; the employee is summoned per event and exits when the turn is done. Every run walks the same path:

  1. 1

    Trigger

    An inbound event reaches the cell: a chat message, a task assignment, a channel webhook, or a re-run from an upstream change.

  2. 2

    Spawn

    The runtime spawns exactly one claude process bound to the cell, keyed to a deterministic session id for that task.

  3. 3

    Work

    The process reads the folders, dispatches sub-agents, and streams output. It rewrites its live panel to ui.json and enqueues any side effects as intents.

  4. 4

    Drain

    A privileged parent drains the intents queue every few seconds, so progress streams to the customer while the run is still working.

  5. 5

    Exit

    The process finishes the turn and exits. The transcript, the Brain memory, and the run files stay on the cell volume.

  6. 6

    Resume

    The next event on the same task resumes the recorded transcript by id, and the employee continues where it left off.

run_task() is the single public entry point for a headless run. Because every caller reaches it, a gateway, the strategy engine, a re-run driver, the transport stays runtime-agnostic: the caller hands off a task and never needs to know which runtime executed it. A first touch on a task triggers a fresh run; a follow-up on the same task resumes the existing one.

Availability

Available now: the default cli runtime, a bare claude subprocess, proven in production. Coming soon: an Agent-SDK runtime selectable with JETSYN_AGENT_RUNTIME=sdk. Because run_task() is the only entry point, switching runtimes changes no caller. An unrecognized value falls back to cli.

Note

The intents queue is the credential seam: the employee proposes side effects, a privileged parent executes them, and the employee never holds your tool or provider tokens. That mechanism is covered in Gateways.

Durable resume#

Continuity comes from a deterministic session id. It is derived from the workspace and the task as a uuid5 of slug:task_id, so the same task always maps to the same id, across processes and across cell restarts. The first run on a task pins that id; a later run on the same task resumes the recorded transcript by id.

The runtime never forks the session (a fork mints a new id and loses the thread) and never disables session persistence (which would discard the transcript). Those two invariants are what make a follow-up feel like a coworker who remembers yesterday's work rather than a fresh stranger.

Resume also self-heals. If a resume points at a transcript that no longer exists, the runtime retries once with a fresh id and clears the stale transcript and any pending intents, so a missing transcript recovers instead of failing the run.

Tip

The mental model: memory is keyed to the task. A follow-up on the same task resumes and remembers; a genuinely new task starts a clean session. Reuse a task_id when you want continuity, use a new one when you want a fresh start.

Sub-agent fan-out#

A run does not do everything in one context. The main employee dispatches specialist sub-agents through its Task tool, each with its own tool scope, and composes their results. Fan-out is one level deep: sub-agents never carry the Task tool themselves, so a sub-agent cannot recursively spawn more.

Interactive: explore and worker

The interactive runtime ships two generic, composable sub-agents. The main agent picks one by intent: explore to investigate without touching anything, worker to make changes.

Sub-agentTool scopeUse it for
exploreRead, Glob, Grep (read-only)Reading the folders and gathering context with zero side effects.
workeradds Write, Edit, BashProducing files and making changes once the plan is clear.

Headless: workflow personas

In a workflow-driven run, sub-agent personas are declared in the workflow and passed to the process as validated --agents definitions. Schema validation is a hard gate: a malformed persona stops the run rather than launching a half-defined agent. Named steps run in order behind a completion gate, so a step does not begin until the prior one emits its completion signal, and the run stops rather than advancing out of order.

The agent-to-UI contract#

An employee renders its own UI as it works. It writes a ui.json file and one generic frontend renderer draws it. There is no per-agent React tile: add a new employee and its panel appears for free. The agent writes .claude/agents/<id>/ui.json, rewriting the whole file at each step (queued, working, blocks, done) so the panel fills live. Each write is atomic (temp file plus rename), so a reader never catches a half-written file.

The envelope is a small, closed object. Unknown top-level keys are rejected.

FieldRequiredNotes
vyesSchema version. Currently 1.
agentyesRole id, e.g. enrichment.
titleyesPanel title, e.g. Enrichment.
statusyesqueued | working | done | error.
headlineyesOne line: what the agent is doing right now.
progressnoInteger 0 to 100 for an optional progress bar.
blocksyesOrdered render primitives (may be empty).
updated_atyesUTC ISO-8601, whole seconds, Z suffix.

blocks is a closed set of seven primitives. Each block is a type plus exactly the fields listed, and no others.

Block typeFields
texttext
markdownmarkdown
listitems (string array)
kvitems of { key, value }
linklabel, href
previewurl, optional label
actionaction, label, optional task_id
.claude/agents/enrichment/ui.json
{
  "v": 1,
  "agent": "enrichment",
  "title": "Enrichment",
  "status": "working",
  "progress": 40,
  "headline": "Searching acme.com",
  "blocks": [
    { "type": "kv", "items": [ { "key": "Company", "value": "Acme Inc" } ] },
    { "type": "list", "items": [ "Founded 2019", "B2B SaaS" ] },
    { "type": "link", "label": "Website", "href": "https://acme.com" }
  ],
  "updated_at": "2026-07-02T12:34:56Z"
}

Note

The shape is deliberately closed so one renderer can draw every agent. To add a new render block, extend the block-type set in the shared schema and keep it closed. The generic frontend picks it up with no per-agent tile.

Dependency-aware re-run#

Employees form a dependency graph: some read what others produce. When an upstream output changes, the dependents must re-run, and in the right order. The runtime models this as a DAG of employees. Changing an upstream node computes the dirty set (that node plus everything downstream of it), and the re-run driver runs the dirty set in topological order.

Every re-run is a resume, not a fresh start. Each dependent employee picks up its own durable session and revises its prior work rather than redoing it from scratch, so a change ripples through the org chart while every employee keeps its memory.

Re-run from a chat message

A re-run can start from a plain chat message. The durable orchestrator session is itself the classifier: it decides whether an incoming message is a re-run request. A wide, strategy-tier cascade is gated behind an explicit confirmation before it fans out, so a casual message never triggers a company-wide rebuild by accident.

Tip

Recipe: add an employee to the re-run graph. Add an entry to the employee graph with its upstream edges declared, its identity flag, and a one-line description of its substeps. The re-run driver and the chat classifier pick it up as data, with no driver change, and its durable session is derived the same way, a uuid5 of slug:employee_slug.

Tuning a run#

A run reads a small set of environment variables. The stall watchdog is the one to internalize: the only kill condition is a silent stdout, and there is no total-runtime cap, so a long, legitimate job is never cut off mid-stream.

VariableDefaultEffect
JETSYN_AGENT_RUNTIMEcliSelects the headless runtime. cli is proven; an unknown value falls back to cli.
JETSYN_MODEL / JETSYN_EFFORTclaude-opus-4-8 / xhighModel and effort for a CLI run.
JETSYN_STALL_TIMEOUT_SECONDS600The only kill condition: stdout silent this long. No total-runtime cap.
JETSYN_AGENT_{MODEL,EFFORT,CWD,PERMISSION}claude-opus-4-8 / xhigh / /workspace / bypassPermissionsModel, effort, working directory, and permission mode for the interactive REPL.
bash
# Run a headless task on the default cli runtime, with a longer stall budget
export JETSYN_AGENT_RUNTIME=cli
export JETSYN_MODEL=claude-opus-4-8
export JETSYN_EFFORT=xhigh
export JETSYN_STALL_TIMEOUT_SECONDS=900