AgentSession: the orchestration layer of the coding agent
AgentSession is the heaviest layer in pi-coding-agent — it doesn't run the LLM directly, nor does it render the UI. It sits between the two: upstream it accepts user input and UI events; downstream it drives pi-agent-core's Agent loop. Model registration, tool definitions, system prompt construction, context compaction, failure retries, and message queueing all live here. Think of it as the assembly workshop that wraps a generic Agent into a stateful, resumable coding assistant with skills and tools.
Responsibilities
AgentSession does four things:
- Assembly:
AgentSessionConfigtakes anAgentinstance, model registry, tool definitions, extension runtime, and system prompt builder; the constructor hooksbeforeToolCall/afterToolCalland subscribes to events. Seepackages/coding-agent/src/core/agent-session.ts:313-330. - Input intake:
prompt(text)is the main entry — it handles/slash commands, expands file-based prompt templates, turns text intoAgentMessage[], and hands it to the underlyingAgent. Seepackages/coding-agent/src/core/agent-session.ts:967-1000. - Event forwarding: subscribes to the
Agentevent stream, repackages events asAgentSessionEvent, and emits them to the UI above. Seepackages/coding-agent/src/core/agent-session.ts:330-335. - Queueing and retry: when the user keeps typing during streaming,
streamingBehaviordecides betweensteer(interrupt) andfollowUp(queue), instead of just dropping or blocking. Seepackages/coding-agent/src/core/agent-session.ts:1181-1296.
Design Motivation
Why not let the UI call Agent.prompt directly? A coding assistant needs a bunch of cross-cutting logic that a generic Agent doesn't care about: slash commands (/model, /settings), prompt template expansion, model/API key validation, auto-compaction when context overflows, auth and telemetry around tool calls. Pushing these into Agent would weigh down the generic loop; pushing them into the UI layer would duplicate them across print mode, rpc mode, and extensions. AgentSession is extracted as the single entry point: all three run modes share the same orchestration logic, and the UI only renders events.
Key Files
packages/coding-agent/src/core/agent-session.ts:121-149—AgentSessionEventandAgentSessionConfigtypes, defining the event shape and assembly interface.packages/coding-agent/src/core/agent-session.ts:244-253—class AgentSessiondeclaration and internal state fields.packages/coding-agent/src/core/agent-session.ts:313-335— constructor: hooksbeforeToolCall/afterToolCall, subscribes toAgentevents.packages/coding-agent/src/core/agent-session.ts:379-435— pre/post tool call hooks for auth and event re-emit.packages/coding-agent/src/core/agent-session.ts:967-1109—promptmain flow: command dispatch, template expansion,this.agent.prompt(messages).packages/coding-agent/src/core/agent-session.ts:1181-1296—steer/followUpqueueing semantics.packages/coding-agent/src/core/agent-session.ts:1317-1389—sendUserMessage(programmatic entry) andabort.packages/coding-agent/src/core/agent-session.ts:713-743—subscribe, how the UI gets events.
Hooking and subscribing in the constructor is the starting point of the whole orchestration:
// packages/coding-agent/src/core/agent-session.ts:313-335
constructor(config: AgentSessionConfig) {
// ... store config, initialize state ...
this._unsubscribeAgent = this.agent.subscribe(this._handleAgentEvent);
// ... etc ...
}prompt turns user text into AgentMessage[] and hands it to the underlying Agent:
// packages/coding-agent/src/core/agent-session.ts:1107-1112
try {
await this.agent.prompt(messages);
} catch (error) {
// ... failure handling ...
}Data Flow
User presses Enter in the TUI editor → InteractiveMode.defaultEditor.onSubmit → session.prompt(text):
When the user keeps typing during streaming, prompt doesn't force through — it splits:
Edges and Failures
- Slash command conflicts: commands registered by extensions can also execute immediately during streaming (they manage their own LLM interaction), bypassing the queue. If a command can't be queued, it throws directly. See
packages/coding-agent/src/core/agent-session.ts:1255-1259. - Missing model/key: the non-streaming path validates model and API key, throwing instead of sending an empty request.
- Compaction abort:
abortcancels both the main loop and the compaction/branch-summary controllers. Seepackages/coding-agent/src/core/agent-session.ts:1744-1752. - Retry:
Agent.emit()calls_handleAgentEventsynchronously, whileprompt()doeswaitForRetry(); the retry logic decides whether to re-emit the turn after a failure.
Summary
AgentSession is the assembly workshop and event bus for the coding assistant: it assembles Agent, forwards events, queues interrupts, and manages tool hooks. Upstream are the three UI modes — InteractiveMode / runPrintMode / runRpcMode. Downstream is the generic loop in pi-agent-core. For assembly internals, see createAgentSession Wiring; for the underlying loop, see Double while Loop.