How AO turns one task into a durable, observable coding agent.
+Agent Orchestrator (AO) is a pluggable TypeScript monorepo for spawning AI coding agents in isolated workspaces, supervising their lifecycle, wiring PR/CI/review feedback loops, and exposing all of it through a Next.js dashboard with live terminal attachment.
+packages/**One-screen architecture
+The high-level model in CLAUDE.md describes AO as parallel AI coding agents, each in an isolated git worktree with its own PR, supervised from one dashboard CLAUDE.md:3-5. The source verifies that the implementation is centered on a small core, pluggable edges, and flat-file persistence.
flowchart LR + User["Human / Orchestrator"]:::user --> CLI["ao CLI\nstart · spawn · stop · restore"]:::cli + CLI --> Core["@aoagents/ao-core\nConfig · Registry · Session Manager\nLifecycle Manager · Metadata"]:::core + Config["agent-orchestrator.yaml\nZod validated"]:::file --> Core + + subgraph Slots["Plugin extension boundary"] + Runtime["Runtime\ntmux / process"]:::runtime + Agent["Agent\nClaude Code / Codex / OpenCode / Aider / Cursor"]:::agent + Workspace["Workspace\nworktree / clone"]:::workspace + Tracker["Tracker\nGitHub / Linear / GitLab"]:::tracker + SCM["SCM\nPR · CI · reviews"]:::scm + Notifier["Notifier\ndesktop / Slack / Discord / webhook"]:::notifier + Terminal["Terminal\niTerm2 / web"]:::terminal + end + + Core --> Runtime + Core --> Agent + Core --> Workspace + Core --> Tracker + Core --> SCM + Core --> Notifier + Core --> Terminal + Core --> Lifecycle["Lifecycle\ncore state machine + poller"]:::lifecycle + Lifecycle --> Events["Events + reactions"]:::event + Events --> Notifier + + Core --> Store["Flat-file state\n~/.agent-orchestrator"]:::file + Core --> WebAPI["Next.js API routes"]:::web + WebAPI --> Mux["Mux WebSocket\nsession snapshots + terminal streams"]:::web + Mux --> Dashboard["React dashboard\nKanban + xterm.js"]:::web + + classDef core fill:#172554,stroke:#60a5fa,color:#eff6ff,stroke-width:2px; + classDef cli fill:#0f172a,stroke:#94a3b8,color:#e2e8f0; + classDef file fill:#1e293b,stroke:#64748b,color:#e2e8f0; + classDef user fill:#312e81,stroke:#a78bfa,color:#f5f3ff; + classDef runtime fill:#164e63,stroke:#22d3ee,color:#ecfeff; + classDef agent fill:#4c1d95,stroke:#a78bfa,color:#faf5ff; + classDef workspace fill:#064e3b,stroke:#34d399,color:#ecfdf5; + classDef tracker fill:#78350f,stroke:#fbbf24,color:#fffbeb; + classDef scm fill:#831843,stroke:#f472b6,color:#fdf2f8; + classDef notifier fill:#7c2d12,stroke:#fb923c,color:#fff7ed; + classDef terminal fill:#1e3a8a,stroke:#60a5fa,color:#eff6ff; + classDef lifecycle fill:#7f1d1d,stroke:#fb7185,color:#fff1f2; + classDef web fill:#0f766e,stroke:#2dd4bf,color:#f0fdfa; + classDef event fill:#3f3f46,stroke:#a1a1aa,color:#fafafa;+
Core owns orchestration
+session-manager.ts owns spawn/kill/restore/send, while lifecycle-manager.ts owns detection, state transitions, reactions, and notifications packages/core/src/session-manager.ts:1-12 packages/core/src/lifecycle-manager.ts:1-10.
Plugins own replaceable integrations
+The public contract is PluginModule<T>: a manifest, a create function, and optional detection. The registry normalizes default/named exports and loads built-ins/configured plugins packages/core/src/types.ts:1728-1753 packages/core/src/plugin-registry.ts:297-312.
Files are the database
+Session metadata lives under ~/.agent-orchestrator/projects/{projectId}/sessions/{sessionId}.json; status is computed from canonical lifecycle when read packages/core/src/metadata.ts:1-11 packages/core/src/metadata.ts:159-163.
Repository layout and build topology
+The root workspace includes both first-party packages and plugin packages via packages/* and packages/plugins/* pnpm-workspace.yaml:1-3. The root build script runs recursive pnpm builds package.json:12-27; workspace dependencies make the effective topology core → plugins → cli/web, matching the high-level docs CLAUDE.md:11-33.
flowchart TB + Root["Root workspace\nNode >=20.18.3 · pnpm 9.15.4"]:::root --> Core["packages/core\ninterfaces · config · lifecycle · metadata"]:::core + Root --> Plugins["packages/plugins/*\nagent · runtime · workspace · tracker · scm · notifier · terminal"]:::plugins + Root --> CLI["packages/cli\nao command · start/stop · supervisor"]:::app + Root --> Web["packages/web\nNext.js 15 dashboard · mux WS · xterm.js"]:::app + Root --> AO["packages/ao\nglobal CLI wrapper"]:::app + Root --> Tests["packages/integration-tests"]:::test + Root --> NotifierMac["packages/notifier-macos"]:::plugins + + Core --> Plugins + Core --> CLI + Core --> Web + Plugins --> CLI + Plugins --> Web + Web --> CLI + + classDef root fill:#0f172a,stroke:#94a3b8,color:#f8fafc; + classDef core fill:#172554,stroke:#60a5fa,color:#eff6ff,stroke-width:2px; + classDef plugins fill:#312e81,stroke:#a78bfa,color:#f5f3ff; + classDef app fill:#064e3b,stroke:#34d399,color:#ecfdf5; + classDef test fill:#78350f,stroke:#fbbf24,color:#fffbeb;+
| Layer | What lives there | Representative source |
|---|---|---|
| Core | Typed integration contracts, config loader, plugin registry, lifecycle, session manager, path and metadata helpers. | packages/core/src/types.ts:3-18 |
| Plugins | Concrete implementations. For example, tmux exports a runtime manifest/create/default module and Codex exports an agent manifest/create/detect/default module. | packages/plugins/runtime-tmux/src/index.ts:21-26 packages/plugins/agent-codex/src/index.ts:41-47 |
| CLI | Packages the command surface and depends on core, web, and many built-in plugins. | packages/cli/package.json:35-61 |
| Web | Next.js dashboard, API routes, WebSocket mux, xterm.js terminal client; depends on core, selected plugins, Next/React/ws/xterm. | packages/web/package.json:48-70 |
Why this split?
+The core stays stable and testable while every environment-specific edge is swappable. New agents, SCMs, notifiers, or terminal UIs can be introduced as packages without changing the state machine or session manager contracts.
+The plugin-slot model
+AO documents eight architectural slots, but the TypeScript PluginSlot union exposes seven loadable plugin slots; Lifecycle is intentionally core/non-pluggable CLAUDE.md:80-96 packages/core/src/types.ts:1718-1726. The interfaces are all centralized in types.ts, which makes plugin changes explicit and reviewable packages/core/src/types.ts:3-18.
| Slot | Interface lines | Responsibility | Examples / defaults |
|---|---|---|---|
| Runtime | packages/core/src/types.ts:386-424 | Create, attach to, send input to, and destroy the process environment where an agent runs. | tmux, process |
| Agent | packages/core/src/types.ts:477-576 | Define how to detect, launch, prompt, and observe a particular coding tool. | Claude Code, Codex, OpenCode, Aider, Cursor, Grok, Kimi |
| Workspace | packages/core/src/types.ts:650-688 | Create isolated code workspaces and clean/restore them. | worktree, clone |
| Tracker | packages/core/src/types.ts:707-749 | Fetch issue/task context and update external task status. | GitHub, GitLab, Linear |
| SCM | packages/core/src/types.ts:786-900 | Inspect branches, PRs, CI checks, review comments, and mergeability. | GitHub, GitLab |
| Notifier | packages/core/src/types.ts:1160-1181 | Deliver lifecycle and attention notifications to humans. | Desktop, Slack, Discord, webhook, Composio |
| Terminal | packages/core/src/types.ts:1196-1215 | Open or attach a human-facing terminal UI. | iTerm2, web terminal |
| Lifecycle | packages/core/src/lifecycle-manager.ts:1-10 | Core state machine, polling loop, reactions, escalation, cleanup. Not a registry plugin. | Built into @aoagents/ao-core |
Registry and discovery
+Built-in packages are enumerated by slot in plugin-registry.ts packages/core/src/plugin-registry.ts:38-72. The registry supports register, get, list, loadBuiltins, and loadFromConfig packages/core/src/plugin-registry.ts:420-623.
Export contract
+A plugin module exports manifest, create(config?), and optional detect. The registry also handles default-export modules packages/core/src/types.ts:1728-1753 packages/core/src/plugin-registry.ts:297-312.
// Shape verified in packages/core/src/types.ts:1728-1753
+export interface PluginModule<T> {
+ manifest: PluginManifest;
+ create(config?: Record<string, unknown>): T | Promise<T>;
+ detect?(): Promise<boolean>;
+}
+ Canonical state machine + derived legacy status
+AO separates durable lifecycle truth from UI/back-compat display status. Canonical lifecycle has eight states and structured reasons packages/core/src/types.ts:29-57; legacy SessionStatus remains a broader display/API vocabulary packages/core/src/types.ts:118-137.
stateDiagram-v2 + [*] --> not_started: metadata reserved + not_started --> working: runtime created + agent launched + + working --> idle: ready / no activity / waiting on PR + working --> needs_input: agent explicitly waits for human + working --> detecting: runtime or agent signal is uncertain + working --> done: agent reports completion without further action + working --> terminated: fatal reason + + idle --> working: new activity / human sends instruction + idle --> detecting: stale runtime suspicion + idle --> terminated: auto_cleanup / pr_merged / manually_killed + + needs_input --> working: human response sent + needs_input --> stuck: waiting too long / escalation exhausted + needs_input --> terminated: manually_killed + + detecting --> working: probe resolves alive + detecting --> stuck: probe cannot recover + detecting --> terminated: runtime_lost / probe_failure / agent_process_exited + + stuck --> working: agent recovers / restore succeeds + stuck --> terminated: manually_killed / error_in_process + + done --> [*] + terminated --> [*] + + note right of terminated + Terminal reasons include: + manually_killed, pr_merged, auto_cleanup, + runtime_lost, agent_process_exited, + probe_failure, error_in_process + end note+
Canonical storage
+createInitialCanonicalLifecycle creates new lifecycle objects, while parseCanonicalLifecycle accepts new lifecycle or legacy statePayload data packages/core/src/lifecycle-state.ts:162-193 packages/core/src/lifecycle-state.ts:415-430.
Legacy status is computed
+deriveLegacyStatus maps canonical state/reason and PR runtime metadata into UI statuses. Metadata patches deliberately do not persist status; it is computed on read packages/core/src/lifecycle-state.ts:432-488 packages/core/src/lifecycle-state.ts:490-505.
Why canonical vs legacy?
+Canonical lifecycle keeps the durable state space compact and reasoned: terminated plus runtime_lost is different from terminated plus pr_merged. The old status vocabulary can still represent dashboard buckets like ci_failed, review_pending, approved, or mergeable by deriving them from canonical lifecycle plus PR state packages/core/src/lifecycle-state.ts:432-488.
Detection and precedence
+determineStatus probes runtime, agent activity/process, PR state, fresh self-reports, idle thresholds, and uncertain detecting decisions packages/core/src/lifecycle-manager.ts:900-1397. Fresh agent reports outrank weak inference, but not runtime death, waiting-input, or SCM truth packages/core/src/lifecycle-manager.ts:1299-1328.
Polling and reactions
+The manager enriches PR metadata, checks each session, persists transitions, emits events, notifies humans, dispatches CI/review/conflict work, and performs merge cleanup last packages/core/src/lifecycle-manager.ts:530-748 packages/core/src/lifecycle-manager.ts:2438-2765.
+Poll
pollAll lists sessions, batch-enriches PRs, checks sessions concurrently, prunes old state, and can stop when all work is complete packages/core/src/lifecycle-manager.ts:2865-3043.
Decide
resolveProbeDecision is part of the path that turns uncertain runtime/activity evidence into a stable lifecycle transition packages/core/src/lifecycle-manager.ts:1202-1214.
React
Reactions can retry, escalate, send instructions to agents, and call notifiers packages/core/src/lifecycle-manager.ts:1399-1624 packages/core/src/lifecycle-manager.ts:2261-2307.
Clean
PR merge cleanup is intentionally deferred while agents are busy and delegates deletion to sessionManager.kill when safe packages/core/src/lifecycle-manager.ts:2309-2436.
Spawn, list, kill, send, restore
+The session manager is the imperative coordinator. Its own header summarizes the contract: create workspace, create runtime, launch agent, list metadata plus live runtime state, kill, cleanup, and send packages/core/src/session-manager.ts:1-12.
+flowchart LR + Task["User task / issue"] --> Resolve["Resolve project + plugins"]:::core + Resolve --> Tracker["Fetch issue context\n(optional tracker)"]:::tracker + Tracker --> Reserve["Reserve session id\nO_EXCL metadata"]:::file + Reserve --> Branch["Compute branch"]:::workspace + Branch --> Workspace["workspace.create + hooks"]:::workspace + Workspace --> Prompt["buildPrompt\nbase + config + rules + task"]:::prompt + Prompt --> AgentConfig["agent.getLaunchConfig"]:::agent + AgentConfig --> Runtime["runtime.create\nAO env vars"]:::runtime + Runtime --> Metadata["write metadata\ncanonical lifecycle"]:::file + Metadata --> Post["postLaunchSetup / initial send"]:::agent + + classDef core fill:#172554,stroke:#60a5fa,color:#eff6ff; + classDef tracker fill:#78350f,stroke:#fbbf24,color:#fffbeb; + classDef file fill:#1e293b,stroke:#64748b,color:#e2e8f0; + classDef workspace fill:#064e3b,stroke:#34d399,color:#ecfdf5; + classDef prompt fill:#312e81,stroke:#a78bfa,color:#f5f3ff; + classDef agent fill:#4c1d95,stroke:#a78bfa,color:#faf5ff; + classDef runtime fill:#164e63,stroke:#22d3ee,color:#ecfeff;+
| Operation | What it does | Source |
|---|---|---|
| Resolve plugins | Locates project-scoped runtime/agent/workspace/tracker/scm/notifier/terminal instances through the registry. | packages/core/src/session-manager.ts:902-918 |
| Spawn | Reserves IDs, creates workspace, builds prompt, configures agent, creates runtime with AO environment, writes metadata, then does post-launch setup. Rollback is guarded by a cleanup stack. | packages/core/src/session-manager.ts:1212-1598 |
| Orchestrator spawn | Uses the same primitives but launches an AO orchestrator session with generated prompt and permissionless settings. | packages/core/src/session-manager.ts:1619-2070 packages/core/src/session-manager.ts:1871-1888 |
| List / get | Reads metadata across projects, enriches runtime state, and computes display status. get fetches one enriched record. | packages/core/src/session-manager.ts:2235-2368 packages/core/src/session-manager.ts:2386-2443 |
| Kill / cleanup | Destroys runtime and workspace, handles idempotency, and writes canonical terminated lifecycle. | packages/core/src/session-manager.ts:2445-2607 packages/core/src/session-manager.ts:2609-2801 |
| Send / restore | Prepares or restores runtime as needed, sends input, and can rebuild workspace/runtime for restorable sessions. | packages/core/src/session-manager.ts:2803-3119 packages/core/src/session-manager.ts:3271-3618 |
| Claim PR | Attaches an existing pull request to a session record. | packages/core/src/session-manager.ts:3121-3244 |
Important invariant: list can detect stale runtimes, but not terminate them
+sessionManager.list() may persist a detecting state with runtime_lost when a tmux/process runtime disappears, but comments make the lifecycle manager the “single authority on terminal decisions” packages/core/src/session-manager.ts:2303-2306 packages/core/src/session-manager.ts:2324-2348.
Flat files, no database
+AO persists durable state in YAML and JSON files under a project-oriented layout. Current V2 paths are under ~/.agent-orchestrator/projects/{projectId}; older hash-based helpers remain marked deprecated/legacy in paths.ts packages/core/src/metadata.ts:1-11 packages/core/src/paths.ts:1-15 packages/core/src/paths.ts:152-200.
flowchart TB
+ LocalConfig["agent-orchestrator.yaml\nnearest config found from cwd"]:::yaml
+ GlobalConfig["~/.agent-orchestrator/config.yaml\nregistered projects + identity"]:::yaml
+ Home["~/.agent-orchestrator"]:::dir
+ Home --> GlobalConfig
+ Home --> Running["running.json\ncurrent ao start PID / port / projects"]:::json
+ Home --> LastStop["last-stop.json\nsessions stopped by ao stop / Ctrl+C"]:::json
+ Home --> Projects["projects/{projectId}/"]:::dir
+ Projects --> Sessions["sessions/{sessionId}.json\nsession metadata + lifecycle"]:::json
+ Projects --> Orchestrator["orchestrator.json\norchestrator session metadata"]:::json
+ Projects --> Worktrees["worktrees/{sessionId}/\nagent workspaces"]:::dir
+ Projects --> Feedback["feedback / code-reviews"]:::dir
+ LocalConfig --> Loader["loadConfig()\nYAML + Zod + defaults"]:::core
+ GlobalConfig --> Loader
+
+ classDef yaml fill:#312e81,stroke:#a78bfa,color:#f5f3ff;
+ classDef json fill:#1e293b,stroke:#94a3b8,color:#e2e8f0;
+ classDef dir fill:#064e3b,stroke:#34d399,color:#ecfdf5;
+ classDef core fill:#172554,stroke:#60a5fa,color:#eff6ff;
+ Config
+config.ts loads agent-orchestrator.yaml, validates with Zod, applies defaults, and expands paths packages/core/src/config.ts:1-11 packages/core/src/config.ts:247-381 packages/core/src/config.ts:927-959 packages/core/src/config.ts:996-1018. Resolution checks AO_CONFIG_PATH, cwd parents, global config, and legacy locations packages/core/src/config.ts:747-822.
Global project registry
+The global config path can come from AO_GLOBAL_CONFIG, XDG, or ~/.agent-orchestrator/config.yaml packages/core/src/global-config.ts:75-99. It is loaded/saved atomically and merges global identity with local project behavior packages/core/src/global-config.ts:341-384 packages/core/src/global-config.ts:857-886.
Metadata
+metadataPath, readMetadata, atomic writes, locked mutation, list, and O_EXCL ID reservation provide file-backed session state packages/core/src/metadata.ts:137-146 packages/core/src/metadata.ts:280-325 packages/core/src/metadata.ts:331-459 packages/core/src/metadata.ts:508-546.
CLI process state
+running.json tracks the active ao start process and dashboard port; last-stop.json stores sessions killed by stop/Ctrl+C so startup can offer restore packages/cli/src/lib/running-state.ts:16-29 packages/cli/src/lib/running-state.ts:171-205 packages/cli/src/lib/running-state.ts:311-353.
Why flat files?
+AO is a local developer tool that needs transparent, inspectable, crash-resilient state rather than a server database. Atomic JSON writes and file locks keep session metadata durable while keeping setup to “install packages and run ao start.”
+From ao start to dashboard render
+ The user-facing flow begins in the CLI, starts the dashboard and project supervisors, ensures an orchestrator session, then uses core services and web API routes to render session state.
+sequenceDiagram + autonumber + actor Human + participant CLI as ao CLI + participant Config as Config loader + participant Registry as Plugin registry + participant SM as SessionManager + participant LM as LifecycleManager + participant Store as Flat files + participant API as Next.js API + participant Mux as mux WebSocket + participant UI as React dashboard + + Human->>CLI: ao start + CLI->>Config: load agent-orchestrator.yaml + global config + Config->>Registry: create registry + load/register plugins + CLI->>SM: createSessionManager(config, registry) + CLI->>API: start dashboard server + CLI->>SM: ensure orchestrator session + SM->>Store: write orchestrator/session metadata + CLI->>LM: start per-project lifecycle worker + LM->>SM: list sessions + SM->>Store: read metadata + enrich runtime state + LM->>Registry: query SCM / agent / runtime plugins + LM->>Store: persist lifecycle transitions + events + UI->>API: GET /api/sessions initial snapshot + API->>SM: list/listCached sessions + API->>Store: read supplemental metadata + API-->>UI: sessions + stats + orchestrators + UI->>Mux: open WebSocket + Mux->>API: poll /api/sessions/patches every 3s + Mux-->>UI: session snapshot / terminal messages + UI-->>Human: Kanban, detail, xterm terminal+
CLI startup
runStartup starts the dashboard and resolves the port, ensures an orchestrator session, starts the project supervisor, restores last-stop sessions if requested, then registers running.json with PID, config path, port, and projects packages/cli/src/commands/start.ts:900-949 packages/cli/src/commands/start.ts:953-1019 packages/cli/src/commands/start.ts:1021-1160 packages/cli/src/commands/start.ts:1746-1757.
Lifecycle workers
The project supervisor reconciles lifecycle workers based on non-terminal sessions and runs a 60-second supervisor loop. Each worker creates a lifecycle manager and starts it with the default 30-second interval packages/cli/src/lib/project-supervisor.ts:118-240 packages/cli/src/lib/lifecycle-service.ts:30-75.
Web services
Web uses a singleton service module to load dashboard config, create a registry, register static plugins, and create session/lifecycle managers. Comments clarify that CLI polling is authoritative and web lifecycle is mainly for webhook checks packages/web/src/lib/services.ts:1-13 packages/web/src/lib/services.ts:104-133.
API and live transport
/api/sessions lists, filters, serializes, and enriches sessions packages/web/src/app/api/sessions/route.ts:72-201. /api/sessions/patches returns lightweight dashboard patches from cached sessions packages/web/src/app/api/sessions/patches/route.ts:8-44.
Verified current-source note about SSE
+Older high-level docs mention an SSE stream. In the current source inspected for this document, live dashboard updates are implemented through the mux WebSocket: the server polls /api/sessions/patches every 3 seconds and broadcasts snapshots, while the client merges mux updates and uses full refreshes for membership/staleness. No current text/event-stream/EventSource route was found in the web package during this audit packages/web/server/mux-websocket.ts:1-7 packages/web/server/mux-websocket.ts:77-198 packages/web/src/hooks/useSessionEvents.ts:148-340.
Dashboard, mux WebSocket, and xterm.js
+The dashboard is a Next.js 15 app with React 19 and xterm.js. It reads session snapshots from API routes, subscribes to mux WebSocket messages, and renders project-scoped Kanban lanes while the sidebar can include all projects.
+Session data
+Dashboard.tsx calls useSessionEvents and intentionally does not filter session events by project at the hook boundary; it filters the Kanban client-side and groups sessions by attention level packages/web/src/components/Dashboard.tsx:146-179 packages/web/src/components/Dashboard.tsx:287-300.
Mux provider
+MuxProvider builds the WebSocket URL, reconnects, re-opens terminal streams, subscribes to session and notification channels, and handles terminal/session/notification messages packages/web/src/providers/MuxProvider.tsx:84-237.
Terminal setup
+DirectTerminal delegates xterm setup to useXtermTerminal, which dynamically imports xterm/addons, opens a mux terminal, sends input/resizes, and re-sends dimensions after reconnect packages/web/src/components/DirectTerminal.tsx:35-72 packages/web/src/components/terminal/useXtermTerminal.ts:80-115 packages/web/src/components/terminal/useXtermTerminal.ts:278-390.
HTTP refresh path
+The session API routes remain the canonical refresh/snapshot path. The patch endpoint is intentionally lightweight and is polled by the mux broadcaster rather than every browser tab polling independently packages/web/src/app/api/sessions/route.ts:72-201 packages/web/server/mux-websocket.ts:170-198.
+Prompt assembly and orchestrator prompt generation
+Agent prompts are intentionally layered so AO can enforce operational behavior while still letting project configuration and repository rules shape local coding style.
+flowchart LR + Base["Layer 1: Base AO agent prompt\nmanaged session lifecycle + PR workflow"]:::base + Config["Layer 2: Config-derived context\nproject name, repo, agent behavior, defaults"]:::config + Rules["Layer 3: User rules\nagentRules + agentRulesFile"]:::rules + Task["Task / issue prompt"]:::task + Agent["Coding agent launch config"]:::agent + + Base --> Config --> Rules --> Task --> Agent + + OrchTemplate["orchestrator.md template"]:::base --> OrchData["project + dashboard + reactions + rules"]:::config --> OrchPrompt["Orchestrator session prompt"]:::agent + + classDef base fill:#172554,stroke:#60a5fa,color:#eff6ff; + classDef config fill:#064e3b,stroke:#34d399,color:#ecfdf5; + classDef rules fill:#78350f,stroke:#fbbf24,color:#fffbeb; + classDef task fill:#312e81,stroke:#a78bfa,color:#f5f3ff; + classDef agent fill:#4c1d95,stroke:#a78bfa,color:#faf5ff;+
Agent prompt builder
+prompt-builder.ts documents the three layers, defines the base agent prompt, gathers config-derived context, reads user rules from config/files, then builds the final prompt with optional orchestrator back-channel instructions and task content packages/core/src/prompt-builder.ts:1-11 packages/core/src/prompt-builder.ts:21-57 packages/core/src/prompt-builder.ts:111-182 packages/core/src/prompt-builder.ts:188-237.
Orchestrator prompt
+orchestrator-prompt.ts renders the bundled orchestrator.md template with project path/name/repo/default branch, session prefix, dashboard port, reaction descriptions, and orchestrator rules packages/core/src/orchestrator-prompt.ts:1-8 packages/core/src/orchestrator-prompt.ts:33-68 packages/core/src/orchestrator-prompt.ts:144-199.
Why layers?
+Base behavior protects AO invariants such as PR creation, status reporting, and lifecycle signals. Config and repo rules then adapt that behavior to a project without forking agent plugins or the session manager.
+Cross-platform abstractions
+AO treats macOS, Linux, and Windows as first-class. The project’s golden rule is to avoid ad-hoc process.platform === "win32" checks and centralize branching in helpers from @aoagents/ao-core docs/CROSS_PLATFORM.md:9-19.
Central helper module
+platform.ts defines isWindows, isMac, isLinux, chooses default runtime (process on Windows, tmux elsewhere), resolves shells, kills process trees, finds PIDs by port, and normalizes env defaults packages/core/src/platform.ts:8-29 packages/core/src/platform.ts:44-62 packages/core/src/platform.ts:88-143 packages/core/src/platform.ts:154-245.
Deep compatibility guide
+docs/CROSS_PLATFORM.md inventories helpers and calls out Windows gotchas such as PowerShell vs bash, PATH wrappers, process probing, localhost binding, and agent-plugin specifics docs/CROSS_PLATFORM.md:43-68 docs/CROSS_PLATFORM.md:172-181.
Why centralize OS branching?
+Platform checks are otherwise hard to mock and easy to regress. A single helper layer lets tests simulate Windows/macOS/Linux behavior and keeps plugin/runtime code from silently drifting into POSIX-only assumptions.
+Key files for future readers
+If you are new to AO, read these files in order. The list is intentionally biased toward source of truth over high-level docs.
+Orientation: CLAUDE.md:3-33 skills/agent-orchestrator/SKILL.md:7-27
+Workspace/package shape: pnpm-workspace.yaml:1-3 package.json:7-27 packages/cli/package.json:35-61 packages/web/package.json:48-70
+Core interfaces and plugins: packages/core/src/types.ts:3-18 packages/core/src/types.ts:1718-1753 packages/core/src/plugin-registry.ts:38-72 packages/core/src/plugin-registry.ts:420-623
+Lifecycle: packages/core/src/lifecycle-state.ts:45-127 packages/core/src/lifecycle-state.ts:432-505 packages/core/src/lifecycle-manager.ts:900-1397 packages/core/src/lifecycle-manager.ts:2438-3043
+Session orchestration: packages/core/src/session-manager.ts:1212-1598 packages/core/src/session-manager.ts:2235-2368 packages/core/src/session-manager.ts:2445-2801 packages/core/src/session-manager.ts:3271-3618
+Storage/config: packages/core/src/metadata.ts:1-11 packages/core/src/paths.ts:112-145 packages/core/src/config.ts:247-381 packages/core/src/global-config.ts:75-99 packages/cli/src/lib/running-state.ts:16-29
+CLI + web flow: packages/cli/src/commands/start.ts:900-1019 packages/cli/src/lib/project-supervisor.ts:118-240 packages/web/src/lib/services.ts:104-133 packages/web/server/mux-websocket.ts:77-198 packages/web/src/hooks/useSessionEvents.ts:148-340
+Prompt/platform: packages/core/src/prompt-builder.ts:1-237 packages/core/src/orchestrator-prompt.ts:1-199 packages/core/src/platform.ts:8-245 docs/CROSS_PLATFORM.md:9-68
+