AgentDeck 6.0 is here: serve one deck over HTTP, AG-UI or the terminal.See what's new
AgentDeckv6.0.6
Meet AgentDeck

Mental Model

AgentDeck separates what you declare from what it runs. You own the intent. It owns the machinery.

AgentToolWorkflowSkillDeckRunControlEvents

What you declare

Four kinds of thing, each a plain Python declaration with no runtime attached.

You writeWithIt becomes
AgentAgent(name=..., instructions=...)a model that decides, optionally with tools, handoffs and subagents
Tool@toola leaf capability, called by a model or by a workflow
Workflow@workflowordinary Python that coordinates executions and can suspend in place
Skilla SKILL.md on disk, named by string in Agent(skills=[...])prose the model reads on demand through a generated load_skill tool

A @tool and a @workflow execute nothing on their own: each stays inert until a Deck compiles it. An Agent also has Agent.run(), a one-shot headless call that skips the deck entirely and so gets no event log, no session and no controls. Everything below is about the path through a Deck, which is the one with the contract.

Deck: the composition root

A Deck holds the catalog. It is where declarations become things that can be named and invoked, and it is the only object you construct to get a working system.

deck = Deck(agents=[assistant], workflows=[approve])

Deck.build() resolves every name in that catalog before a run starts: handoffs, subagents, skills, MCP servers. An unresolvable name is an error you get at build time, not halfway through a user's turn. Model credentials are the provider's own: a missing or invalid one surfaces at the actual call, not from build().

Run: the thing with identity

Invoking a catalog entry produces a run, and the run is what everything else hangs off. It has a durable id, a status, a log, and the session it belongs to if it belongs to one. It outlives the handle you got it from: deck.runs.get(id) picks the same run up again, in this process or another one.

A run hasMeaning
identitya minted id, optionally a (namespace, key) you chose
statusone of running, paused, waiting_answer, completed, failed, cancelled
a sessionthe conversation it belongs to, or None when it stands alone
a logevery event it produced, in order

Control and Events: the two ways out

A live run is not a black box. Control is what you send in: pause(), resume(), cancel(), answer(). Events are what comes out: an append-only, typed record of every token, tool call, report and state transition.

The log is not a copy of the state, it is the state. A run's status is derived by folding its own events in order, so there is no second store to fall out of sync after a restart.

What AgentDeck manages, so you do not

Run identity, lifecycle transitions, persistence, event dispatch, cancellation, concurrency, and the session a turn belongs to. You never construct a run id, write a status, or decide when a paused run may resume.

Next