Skip to content

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:

CallerHow it callsActs as
A sessionthe sessions and interactions tools it holdsthat session
An extensionctx.sessionsthe extension
A clientorchestration.* over the protocolits connection (a server token, or the local window)
An MCP clientthe tools at /mcp on a serverits token

Orchestrators#

Two things make a session an orchestrator. Both are set when it is created:

  • Tools. sessions drives sessions; interactions answers their prompts. A session that holds sessions is an orchestrator.
  • A subscription. The scope of sessions its events verb searches.

A scope narrows by field, and every field must match. {} is every session.

FieldMatches
projectRootSessions in this project: its root, or a folder inside it
machineSessions on this lent machine; local is the host itself
sessionIdsOnly 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#

ToolVerbDoes
sessionscreateStart 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
sendQueue a prompt. Declined when the person stopped the session and has not sent since, or their own queued prompts go first
steerGuide the current run at its next step
cancelStop the run
readTitle, folder, state (idle, running, blocked), last message
transcriptThe last turns as text
listOther sessions with their state, newest first
waitReturn once a session is idle or blocked, or after a timeout. For callers that get no events
eventsSearch what happened in other sessions: runs that ended, sessions waiting for an answer
interactionspendingThe questions, plans and tool approvals waiting for an answer
answerAnswer 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 events verb 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:

TypeScript
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.