Skip to main content

Engine and session model

Build an agent by running a persisted Session with a reusable Engine. The returned Run lets your application follow progress, answer permission requests, or cancel the work.

The three objects you hold

*agent.Engine

The engine runs the agent loop. Reuse one engine across sessions that use the same provider and model. Construct it with the model adapter, tool catalog, permission policy, and hooks, then start a run:

env := tool.MustEnvironment(
session.EnvironmentRef{Kind: session.EnvKindLocal, ID: "/workspace", Revision: "example-v1"},
workspace,
memledger.New(),
nil,
)
run := eng.Run(ctx, sess, env, agent.RunRequest{Text: "your prompt here"})

Create the engine with agent.NewEngine(agent.Deps{...}). The example uses the memledger reference adapter for the environment's required read ledger.

*session.Session

The session holds conversation history, state, usage, counters, and limits. It is the unit of persistence, so a restored session can continue where it stopped.

A session is created separately from the engine and passed in at run time:

sess := session.New(
"my-session-id",
session.ModeDefault,
session.EnvironmentRef{Kind: session.EnvKindLocal, ID: "/workspace/root", Revision: "example-v1"},
session.Limits{MaxTurns: 20, MaxToolCalls: 60},
time.Now(),
)

The same engine can run different sessions. After a session completes, you can reopen it and use the same engine or another compatible engine.

Legacy snapshots with an unknown producer remain read-only. An authenticated server can copy an eligible, owned snapshot into a new session after the operator selects its environment, provider, and model. The original snapshot remains unchanged.

*agent.Run

engine.Run(...) starts the loop in the background and returns immediately:

for ev := range run.Events() {
// observe the loop
if ev.Type == session.EvPermissionAsk {
run.Approve(ev.Ask.AskID, session.VerdictAllowAlways)
}
}
// channel closed = run is done

Run.Events() returns ordered events and closes when the run ends. Run.Approve(askID, verdict) answers a permission request. Run.Cancel() cancels the run.

The things you pass in

WhatTypeWhat it does
Environmenttool.EnvironmentBinds a non-nil workspace, an optional command runner, and the environment identity.
Catalog*tool.Catalog on DepsRegisters built-in, MCP, and custom tools. Specs(mode) defines what the model can use.
LLM providerport.LLMProvider on DepsStreams provider-neutral model output. Mecatl includes OpenAI and Anthropic adapters.

What "subagent" and "team" mean

Neither is a separate agent type. A subagent uses a child engine with its own catalog, policy, and optional model. A team uses a Supervisor to coordinate member engines. Each child still runs a session through Engine.Run.


How it fits together

agent.Deps{LLM, Catalog, Policy, Hooks, Store, ...}


*agent.Engine ← long-lived, reusable per (provider, model)

│ .Run(ctx, sess, env, req)

*agent.Run ← live handle; one per active run
/ \
.Events() .Approve()
(chan Event) (send verdict)

*session.Session ← conversation state; passed in, saved by Store
├── Conversation ← ordered []Message history
├── State ← idle / running / awaiting / completed / failed / cancelled
├── Limits ← MaxTurns, MaxToolCalls, MaxConsecutiveFailures
└── Usage ← cumulative token spend

The engine and session remain independent. You can test them with the reference adapters and resume a session in a process that did not start it.

What's next