Orchestration
Sessions that drive other sessions, the Main and orchestrators that extensions create, and the verbs every caller shares.
A session can drive other sessions. It starts them, sends them prompts, steers or cancels their runs, reads what they did, and answers the questions, plans and tool approvals they wait on. The same verbs serve four callers:
Orchestrators#
Two things make a session an orchestrator. Both are set when it is created:
- Tools.
sessionsdrives sessions;interactionsanswers their prompts. A session that holdssessionsis an orchestrator. - A subscription. The scope of sessions its
eventsverb searches.
A scope narrows by field, and every field must match. {} is every session.
| Field | Matches |
|---|---|
projectRoot | Sessions in this project: its root, or a folder inside it |
machine | Sessions on this lent machine; local is the host itself |
sessionIds | Only these sessions |
The Main#
The Main is the built-in orchestrator. It holds both tools and its scope is every session. It also gets a playbook and a list of the projects with work in progress. The Main session setting turns it on: while it is off, the Main holds no tools and hears nothing. The setting is the Main's alone. Extensions, clients and other orchestrators keep the verbs.
The verbs#
| Tool | Verb | Does |
|---|---|---|
sessions | create | Start a session in a folder with a first prompt, and return its id. With tools and a subscription it is an orchestrator, and the prompt is optional |
send | Queue a prompt. Declined when the person stopped the session and has not sent since, or their own queued prompts go first | |
steer | Guide the current run at its next step | |
cancel | Stop the run | |
read | Title, folder, state (idle, running, blocked), last message | |
transcript | The last turns as text | |
list | Other sessions with their state, newest first | |
wait | Return once a session is idle or blocked, or after a timeout. For callers that get no events | |
events | Search what happened in other sessions: runs that ended, sessions waiting for an answer | |
interactions | pending | The questions, plans and tool approvals waiting for an answer |
answer | Answer one. The first answer wins |
Events#
When a run ends, or a session starts waiting for an answer, one event goes to:
- the event log, which the
eventsverb searches; - every extension handler (
ctx.sessions.onEvent); - the orchestrator that owns the session: the one that created it, or asked for this run.
Only the owner sees the event in its conversation, at once as a row and at its next turn for its model. The event wakes it when the session is its worker, when it asked for the run, or when its worker is blocked. Events that arrive together wake it once. A blocked worker's event carries the question, plan or tool call, so the orchestrator can answer it at once.
Events from sessions an orchestrator does not own, such as your own chats, never enter its conversation and never wake it, the Main included. Every turn in an orchestrator's history is read again on each wake, so these events would make every wake cost more. It looks them up with events, list or read when it needs them.
Answering prompts#
Anything that holds the interactions verbs answers any prompt it may reach. The first answer wins: whoever comes second, the person included, is told so. The answer says who gave it. The waiting model reads it in the tool result, and the transcript shows Answered by Main (or the extension, or the token) under the call. Over the protocol and MCP, a chat token answers tool approvals too.
Rules#
- Orchestrators never watch or drive each other. A session cannot target itself or an orchestrator, and cannot create sessions with tools or a subscription. Delegation stays one level deep.
- Extensions and clients reach every session, the Main included. Side sessions are nobody's to see.
- A session whose rules exclude it from orchestration reports to no one.
- Revoking a server token stops the sessions it created and drove.
An orchestrator per project#
An extension that opens one orchestrator per project:
import { defineExtension } from 'zidane'
export default defineExtension({
name: 'project-leads',
setup(ctx) {
const leads = new Map<string, string>()
ctx.registerCommand({
name: 'lead',
description: 'Open the orchestrator of a project: /lead <project folder>',
run: async (c) => {
const root = c.args.trim()
let id = leads.get(root)
if (!id) {
id = await ctx.sessions.create({
cwd: '',
title: `Lead: ${root}`,
tools: ['sessions', 'interactions'],
subscribe: { projectRoot: root },
})
leads.set(root, id)
}
await ctx.ui.openSession(id)
},
})
},
})An orchestrator across projects is the same call with subscribe: {}. The full API is in the extensions guide.