Skip to content

Native Agents and Durable Circuits

Sema has two multi-agent constructs:

  • agent declares a typed, bounded model actor.
  • circuit declares durable orchestration using ordinary Sema control flow.

That is the entire language addition. There is no separate graph DSL, mailbox language, or keyword for every orchestration pattern. Assignments carry values, parallel creates fan-out/fan-in, spawn creates an owned task, if and match choose paths, and contracts form gates.

Surface Meaning
agent name(input) -> Output by model: A typed model/tool loop with a role instruction, hard contracts, and a budget
circuit name(input) -> Output !{effects}: A durable function whose calls and control flow form a work graph
parallel [worker(x) for x in xs] Ordered, bounded fan-out followed by fan-in
spawn worker(input) An owned Task[Output] that may run independently
task.join()? Wait for the task and propagate a typed task failure
Agent.build(spec, under=envelope)? Validate a dynamically proposed specialist inside fixed authority

agent and circuit are soft keywords. They are recognized only where a declaration can begin, preserving the wider identifier namespace.

An agent looks like a function, but the runtime owns its bounded model↔tool loop. The declaration fixes its input, output, role, model, tools, budget, and completion contracts:

def search(query: str) -> str !{}:
return "source:" + query
agent researcher(question: str) -> str by research_model:
sem "Collect one attributable finding and separate fact from inference"
use tools [search]
budget model_calls=2, tokens=512
ensure len(result) >= 1

Tools remain ordinary effect-typed Sema functions. The agent’s effect row is derived from its model, selected tools, and delegated child pool; it is not duplicated in the declaration. At assure silver or higher, every agent must have an explicit model_calls limit.

The call completes only when the model output decodes to the declared type and its hard contracts pass. Budget, decode, policy, stall, and contract failures remain typed failures; a model judge cannot overrule them.

A circuit is normal Sema with durable agent calls:

agent writer(evidence: list[str]) -> str by writer_model:
sem "Synthesize evidence with provenance and explicit uncertainty"
budget model_calls=1, tokens=512
ensure len(result) >= 1
circuit synthesize(questions: list[str]) -> str !{model.invoke}:
budget agents=8, spawn_depth=0, model_calls=16, tokens=8000
evidence = parallel [researcher(question) for question in questions]
return writer(evidence)

The comprehension is the topology:

┌─ researcher(question 1) ─┐
questions ── parallel ───├─ researcher(question 2) ─┼── evidence ── writer
└─ researcher(question 3) ─┘

parallel [expression for item in items] is the canonical ordered comprehension form. There is deliberately no par alias; par remains an ordinary identifier. Sema may dispatch static read-only or disjoint agents in isolated child sessions. Dynamic agents and work with overlapping or unknown mutation scope are conservatively serialized.

Circuits do not need display-oriented syntax. Ordinary constructs carry the meaning:

if issue.failed:
specialist = Agent.build(spec, under=envelope)?
finding = (spawn specialist(issue)).join()?
draft = writer(finding)
verification = verifier(draft)
ensure verification.passed
return draft

The runtime derives:

Program construct Observed graph shape
parallel fork and merge
value dependency edge between work units
spawn / join task edge and synchronization point
if / match decision and selected path
loop until / bounded loops repeated, bounded subgraph
ensure, policy, approval, completion policy gate with pass, wait, or fail state

This makes generator→reviewer→repair, panels, routing, monitor-triggered intervention, and recursive orchestrator→worker patterns library patterns over one language rather than new syntax.

Use spawn when the parent should continue before synchronizing:

circuit deliver(request: ChangeRequest) -> PatchArtifact !{agent.spawn, model.invoke}:
budget agents=4, spawn_depth=1, model_calls=8, tokens=6000
evidence = explorer(request)
implementation = spawn engineer(evidence)
patch = implementation.join()?
return hardener(patch)

spawn returns an owned Task[T]. A child cannot outlive its circuit: circuit exit joins or cancels outstanding children, and cancellation propagates through the owned subtree. Lifecycle states are pending, running, awaiting_signal, suspended, complete, failed, and cancelled.

When the required role depends on runtime evidence, an orchestrator can propose an AgentSpec. Agent.build admits it only under a typed envelope:

from std.agents import AgentSpec, AgentEnvelope
spec = orchestrator(issue)
specialist = Agent.build(spec, under=envelope)?
finding = (spawn specialist(issue)).join()?

The envelope fixes input/output types, allowed models, tool subset, child limits, and sub-budget. Delegated authority is always the intersection of the sealed root, circuit policy, parent policy, envelope, and spawn-site grant:

child authority = sealed root ∩ circuit ∩ parent ∩ envelope ∩ spawn grant

A child cannot mint a tool, effect, model, policy, child pool, or fresh budget. Every child charge accrues to all enclosing budgets. Data-only AgentSpec admission is distinct from staged Code[Agent[I,O]], which additionally needs an explicit envelope plus code.exec("agent-sandbox") and agent.spawn.

The outer circuit owns one local run under .sema/runs/<run-id>/:

  • atomic session state;
  • segmented JSONL events;
  • content-keyed completed-leaf memos;
  • content-addressed artifacts.

Stable leaf identity includes the circuit symbol, callsite, agent semantic hash, serialized input digest, parent path, and dynamic ordinal. Resume reuses unchanged completed leaves. It may retry an incomplete read-only leaf, but an external mutation without recorded completion suspends as NeedsReconciliation instead of guessing.

One run’s durable life, end to end:

flowchart LR
R["sema circuit run"] --> J[".sema/runs/&lt;run-id&gt;/\natomic session state\nsegmented JSONL events\nleaf memos + artifacts"]
J -->|"crash or interrupt"| S["sema circuit resume\n&lt;run-id&gt;"]
S --> M["completed leaves reused\n(content-keyed memos)"]
M --> D["unfinished leaves re-run;\nexternal mutation without a\nrecorded completion suspends as\nNeedsReconciliation"]
D --> C["run completes"]
Flow of a durable circuit run: sema circuit run writes session state, JSONL events, leaf memos and artifacts under .sema/runs/<run-id>/; after a crash, sema circuit resume reuses completed leaves via content-keyed memos, re-runs only unfinished work (suspending as NeedsReconciliation on unrecorded external mutation), and completes the run. Flow of a durable circuit run: sema circuit run writes session state, JSONL events, leaf memos and artifacts under .sema/runs/<run-id>/; after a crash, sema circuit resume reuses completed leaves via content-keyed memos, re-runs only unfinished work (suspending as NeedsReconciliation on unrecorded external mutation), and completes the run.
Terminal window
sema circuit run examples/agent-research
sema circuit list examples/agent-research
sema circuit show examples/agent-research <run-id>
sema circuit resume examples/agent-research <run-id>
sema circuit cancel examples/agent-research <run-id>

These commands manage the durable aggregate. They are not a separate orchestration engine; the circuit remains ordinary checked Sema.

std.agents provides typed specifications, envelopes, pools, artifacts, work units, and domain-neutral role presets: Researcher, Architect, Orchestrator, Reviewer, Verifier, Writer, and Monitor, plus the scoped coding presets Explorer, Engineer, and Hardener.

std.circuits provides ordinary-library patterns for pipelines, fan-out/fan-in, specialist routing, generator→reviewer→repair, panels, monitor intervention, approval gates, and provenance-preserving artifact aggregation.

std.completion adds contract-first, bounded belief completion. Deterministic contracts and policy denials always take precedence over probabilistic evidence.

  • agent-research — parallel research fan-out, ordered evidence merge, and typed synthesis.
  • agent-software — explorer, owned engineer task, and hardener handoff.
  • agent-scientific — a failed experiment triggers a dynamically admitted proof auditor; publication stays blocked until deterministic verification passes.

For the normative details, see Native agents and durable circuits, the generated grammar, and the effects catalog.