Agent now owns the full turn lifecycle: Submit, executeTurn, turnError,
autoCompactLimit, autoWarnLimit, spawnContext, and runCompact all live
on *Agent in turn.go.
Session.Submit is a thin dispatcher: slash commands are handled at
session level (they need sessionsDir, listHandlers, etc.), then
non-command input is delegated to Agent.Submit.
Session.IsRunning and Session.Interrupt delegate to Agent methods.
turnError is an overridable func field on Agent (default: hook-based
implementation in defaultTurnError). saveSession and flushSave are
injected as callbacks from Session into Agent at construction time.
This completes the agent/session ownership separation for the turn
execution path. The Agent is now a self-contained entity that can
run turns independently given its injected dependencies.
Agent now holds all dependencies needed to run turns independently:
bus, log, auditLog, sessionID, startupMessages, readPlanStep, saveSession.
emit() moved to Agent. Session no longer owns startupMessages/readPlanStep.
- uname is the agent's principal/UID, not a session property
- peers removed entirely from core: the concept needs redesign for
multi-agent. The 9P layer retains its own inter-session routing
at the Manager level.
Core Session now holds only: id, bus, env, plan, prevPrompt.
Agent holds all execution + identity state.
Agent now owns all execution-related state:
- cwd: the directory the agent operates in
- fifo: prompt queue (prompts target a specific agent)
- submitMu: serializes turns for this agent
- toolCallCount: per-agent tool usage metric
- pendingInject: injection targeting the current agent
Session retains only environment state: identity, bus, env, plan, peers,
persistence. This completes the agent/session state separation.
Agent now owns its execution state (state, reply, stateMu, changeMu,
changeCond). Session delegates State()/Reply()/WaitChange() to Agent.
This is the correct ownership: each agent has independent execution
state. In multi-agent, agents can be idle/thinking independently.
Session remains the coordination layer.
- Session.Agent() returns the active *Agent
- Agent.Name(), Agent.BackendName(), Agent.ModelName() — first-class
- Session proxy methods delegate to Agent
Consumers can now access the agent directly:
sess.Agent().Name()
sess.Agent().BackendName()
The Session struct is defined in session.go — named after what it is.
No more 'harness' concept; that was an artifact of the interface+impl
pattern we removed.
Session is now an exported struct with unexported fields.
Methods ARE the API — no interface declaration in the package.
Consumers that need mockability define their own interface
(implicit satisfaction, standard Go pattern).
The agent/ package is now session/ — because a session is the
top-level concept. The package contains:
- harness: implements Core, the session orchestrator
- Agent: the reasoning entity (history, runtime, tools)
- History: conversation accumulator
- Fifo, loop, compaction, hooks, commands
The old session/ package (which just held extracted fields) is
dissolved back into the harness. One package, clean ownership.
Extend the core session with fields that are genuinely session
concerns currently duplicated in 9p/session:
- Plan (markdown checklist, survives compaction)
- PrevPrompt (last submitted prompt text)
- Peers (bidirectional session links)
The 9p/session layer can delegate to these in a future change.
Defines Session (ID, CWD, state, env, bus, FIFO) as the runtime
environment that hosts an agent. This is the foundation for separating
session concerns from agent reasoning concerns.