Agent Orchestrator
Developer-mode bundled orchestration module for coding jobs, interactive sessions, and GitHub/GitLab repository automations.
Regression tasks whose description contains an exact session.*.failed production signature are not marked erledigt immediately after an implementation run. Symphony moves them to warten and persistently waits for the linked pull request to be merged. The production observation window starts at that merge timestamp, lasts 24 hours by default (SYMPHONY_REGRESSION_OBSERVATION_MS), and survives orchestrator restarts through the durable job record. Jobs with pending observations are protected from the 12-hour job TTL cleanup. Symphony only completes the task when the signature stays absent for the full post-merge window. A recurrence or an unmerged PR closure returns the task to offen.
What it does
Agent Orchestrator (agent-orchestrator) runs coding agents against Git repositories and monitors them from one place. It starts detached coding-agent jobs (codex, claude, clapilot-code), hosts interactive agent sessions backed by the native ClapilotAICore broker, and keeps a small compatibility fallback for older job paths. Clapilot-code is Clapilot's own in-process coding loop; Codex and Claude remain external harnesses. GitHub and GitLab repositories share the same repository picker and automation matrix. The runtime reviews pull requests or merge requests, implements issues on a fresh branch, replies to mentions, follows review feedback, and fixes default-branch CI failures.
How to open / enable it
- Agent Orchestrator is a Developer-mode module: the web module and the
Settings -> Agent Orchestratorsurface are hidden while Developer mode is disabled (src/lib/module-store/developer-mode-modules.ts). - With Developer mode enabled, open it from the module menu; the route is
/modules/agent-orchestrator. GitHub automations and runtime settings live underSettings -> Agent Orchestrator(src/app/(app)/(einstellungen)/settings/agent-orchestrator/page.tsx). - Manifest:
bundled-modules/agent-orchestrator/module.json(slugagent-orchestrator, entryindex.html, rendererreact, iconbot). The React UI issrc/components/modules/agent-orchestrator-module.tsx, hosted bysrc/app/(app)/modules/[slug]/page.tsx; bundled frontend assets remain inbundled-modules/agent-orchestrator/but the app renders the module directly in the main React tree. - Auth for the module API: web users authenticate with the
clapilot_sessioncookie; legacy compatibility skills/scripts use a short-lived machine Bearer token fromPOST /api/auth/agent/system-token; remote runners use Hub HMAC auth with the sharedCLAPILOT_HUB_SHARED_SECRET.
Key workflows
Start a detached coding job
Use jobs when the user wants a detached run such as "go implement this", "prepare a PR", or "review this repo".
+ New Agentopens a compact chat-style composer. WithLocalselected it creates an interactive workspace session; its footer contains attachment, repository, icon-prefixed model, optional speech-to-text, and send controls. The model menu combines available Codex, Claude, and Clapilot Code models and infers the harness from the selected model.- When at least one online Codex runner is available, the web composer adds a runner selector between repository and model.
Autolets any online runner claim the work, while choosing a named runner pins it to that machine. A remote selection forces the Codex provider and creates a decoupled coding job instead of an interactive session; the queued job remains pinned even if that runner later goes offline. - The Apple client's
Neuer Agentsurface mirrors the same session-first contract and single-row footer. It hides the generic composer emoji and keyboard-hint controls so repository and model selection remain visible without a second selector row. - Detached and remote jobs remain supported for the web
New Agentcomposer, automations, GitHub observers, remote runners, API/tool calls, existing activity rows, and job follow-ups. - Codex-backed detached jobs keep an internal linked orchestrator session, so the same background run can be resumed later with a follow-up prompt from the web module, the native Apple client, or agent tools. Plain CLI jobs without a linked session, including Claude CLI jobs, can also receive follow-up prompts; those run in the original job workspace and append their output to the same job log. Remote Codex jobs requeue follow-up prompts for the remote runner and reuse the remote job workspace.
Model selection
provider=clapilot-coderuns through Clapilot's in-process coding loop instead of the Codex app-server or Claude CLI. It reuses the nativeembedded_piadapter internally. The web creation UI shows a model picker backed only by usable non-subscription entries in the configured Provider & Modelle catalog; session creation fails clearly when none are available instead of silently falling back.pi,embedded_pi,embedded-pi, andclapilot_coderemain accepted input aliases forclapilot-code. New jobs, session metadata, and automation settings use the canonicalclapilot-codeprovider value.- The web
Neuer Agentpanel shows model pickers forclaudeandcodextoo, backed byGET /coding-models. The picker preselects the model a job would run with by default (runtime config override, then the provider catalog default). Options merge the configuredagent_provider_configscatalog with the current CLI-native model lineup, so models the CLI accepts stay selectable even when the Clapilot catalog only lists a subset. - The current Codex lineup includes
gpt-5.6-sol,gpt-5.6-terra,gpt-5.6-luna,gpt-5.5,gpt-5.4,gpt-5.4-mini, and the subscription-onlygpt-5.3-codex-spark. Spark is a current selectable model and is not normalized through the legacy GPT-5.3 fallback rule. - Detached Claude jobs always resolve to a concrete model: an explicit request wins, then the
Settings -> Agent OrchestratorClaude runtime model, then the configured Anthropic provider catalog default. The current built-in fallback isclaude-opus-5; the resolved ref (for exampleclaude-default/claude-opus-5) is stored on the job and shown on activity cards, matching interactive sessions. - Claude CLI jobs run with
--output-format stream-json --verbose, so the job log streams live assistant text and tool activity while the job runs, and the job'smodelfield is corrected to the model the CLI actually reports in its init event. - The job follow-up composer (web terminal panel and Apple job detail) has a model picker limited to the running job's provider catalog, preselected to the model the job last ran with. The chosen model is sent as
modelonPOST /jobs/{id}/follow-up; detached CLI follow-ups re-run the CLI with that model, linked-session follow-ups forward it as a per-turn model. - The Codex app-server bridge resolves the Codex service tier for each model. Codex-auth OpenAI providers can set a per-model
Codex-Tier(Global,Fast,Flex,Aus) in ClapilotAICore settings; these values are stored as provider metadata undermodelServiceTiers. The model picker reads Codex app-servermodel/listand uses the returned speed-tier metadata for the per-model tier menu. Models left onGlobaluseCLAPILOT_AGENT_CODEX_SERVICE_TIERwhen it is set; otherwise the tier stays unset and Codex chooses. - Agent Orchestrator runtime settings accept
maxandultrareasoning in addition to the older effort values. Codex currently advertisesmaxfor the GPT-5.6 family andultrafor Sol and Terra; unsupported model/effort combinations remain subject to the Codex runtime's own validation.
Run interactive sessions
Use sessions when the user wants to stay in the same thread/workspace and continue iteratively:
- resumable repo conversations
- thread-bound Telegram/Slack/WhatsApp coding sessions
- live turn + terminal streaming
- fork/archive lifecycle
Interactive session adapters remain codex_app_server, acp_agent, and embedded_pi. provider=auto resolves to Codex unless an explicit adapter override is provided, provider=clapilot-code defaults to the internal adapter=embedded_pi, and an explicit provider=claude defaults to adapter=acp_agent. The acp_agent adapter remains scaffolded for local ACP-compatible sidecars/agents and is still experimental. The web + New Agent panel offers interactive execution for the production-ready Codex and Clapilot-code paths.
Monitor work in the module
The React module shows detached jobs, interactive sessions, and native runtime sessions from ClapilotAICore so admins can inspect normal Assistant chats, Clapilot-code coding sessions, and bridge runtime activity without switching to the dedicated Sessions diagnostics page.
The activity rail uses a canonical read-only thread projection over the existing job, orchestrator-session, native runtime-session, and remote-session records. The projection classifies each source as chat, coding, scheduled, system, or remote, preserves its original session/chat/job/external identifiers, and advertises capabilities such as conversation inspection, reply, diagnostics, workspace files, diffs, terminal, approvals, checkpoints, and Git actions. It does not migrate or replace stored sessions.
Normal web, group, and channel chats remain first-class entries in Sessions. Their persisted chatTranscript stays the authoritative conversation, while model runs, tool events, memory/context diagnostics, raw events, and metadata remain available in the detail inspector. Coding-only workspace capabilities are enabled only for repository/workspace-backed projections, so later Changes, Files, Terminal, Preview, and checkpoint panels cannot replace or hide ordinary chat history.
The Apple client applies the same projection on iPhone, iPad, and macOS. Selecting a native runtime chat loads its full persisted transcript instead of stopping at the list summary; supported chats can continue through the existing runtime-session turn endpoint, while fork, close, terminal, diff, checkpoint, and Git actions remain limited to session types that actually support them.
- The left rail is a dense, searchable activity navigator with counted filters for
Coding Agents,Sessions,Alle,Remote Agents, andAnalytics. Each row keeps category, provider, repository/session identity, title, latest output, resolved model, status, latest tool use, and update time scannable without competing with the selected-thread detail pane. - Selected-thread headers lead with the human-readable session/channel title and retain the technical session id as secondary metadata. This keeps normal chat inspection understandable without removing diagnostic identity.
- Detached-job details use the same compact identity hierarchy as session details: task title, repository/model context, provider mark, status dot, and an optional stop action. A segmented detail control separates Conversation, Activity, Terminal, Changes, and Files; changed files and diffs never render inline in chat history. Do not add a Terminal/Prompt switch, a second "Current Terminal" title, explanatory subtitle, status badge, or always-visible job/workspace metadata row above it. The original task remains visible as a user message.
Coding Jobsshows detached coding jobs plus repository-backed interactive and runtime sessions, so coding work stays grouped with repository context.Session Jobsbundles chat- and runtime-backed session work: normal Assistant sessions (runtimePath=native), native Clapilot-code coding sessions (runtimePath=embedded_pi), and native sessions whose active execution harness resolves to theClaude CLI Bridge. Hidden follow-up sessions that belong to detached coding jobs are intentionally excluded; they stay attached to their owningCoding Jobdetail view.Allemixes session-backed work and detached coding jobs in one rail.- Session overview requests are summary-only by default: they carry compact status/model/activity previews but omit per-session event and chat-history arrays. The web module refreshes visible overview lists every 30 seconds, skips hidden-tab refreshes, and suppresses overlapping requests. Full history is loaded only for the selected session.
Remote Agentsswitches the rail to a remote-machine dropdown, then lists that machine's local Codex CLI andclapilot-codesessions from the latest runner heartbeat. Clapilot Code rows carry a harness label. Selecting a remote session opens its metadata and transcript once the runner has answered the detail request in a later heartbeat. A composer at the bottom creates a decoupled resume job pinned to that machine, so replies queue even while the runner is stale or offline and continue through the matching CLI harness when a compatible runner reconnects.Analyticsopens the internalAgent Costs & Yielddashboard for repo-bound Orchestrator runs. The first MVP derives run/yield data from existing jobs and interactive sessions, including repo, issue/PR context when detectable, provider/model, status category, duration, and simple waste hints such as failed runs or completed runs without a detected PR. Cost and token fields are explicitly marked as unavailable until reliable provider usage data exists; this analytics view does not render raw logs.
Detail-pane behavior:
- Session-backed details preserve each data mode without changing the underlying records.
Conversationis the default and shows only chronological user/assistant turns,Activityisolates tool calls, terminal output, model runs, and lifecycle events, andDetailsexposes identifiers, runtime metadata, and diagnostics. Local repository-backed coding sessions additionally expose capability-gatedTerminal,Changes, andFilesmodes. Terminal isolates command cards and raw terminal deltas already emitted by the agent runtime; Changes shows the current branch, Git status, staged diff, and working-tree diff; Files shows a bounded Git-ignore-aware relative inventory. Normal chats never receive these workspace modes. - Workspace inspection is lazy-loaded from
GET /sessions/{id}/workspaceorGET /jobs/{id}/workspace. Each local job records its starting Git commit; Changes compares the current checkout against that baseline, so committed and pushed agent edits remain visible instead of appearing clean. Before an automation checkout is removed, the Orchestrator persists an immutable final workspace snapshot and serves it to both the owning job and linked session. Historical PR jobs created before snapshot support can recover a read-only changed-file view from the GitHub pull-request files API. Ownership and trusted-root checks still match workspace file references, and remote-runner workspaces remain unavailable. The Terminal inspector is read-only: it reuses the selected session's persisted and SSE-streamed command/terminal events and does not expose the unrestricted Developer-mode admin terminal or introduce an arbitrary-shell endpoint. - Local coding sessions expose an
Approvalsmode on web and Apple. Codex app-server runs useapprovalPolicy=on-request,approvalsReviewer=user, andworkspace-write; command and file-change server requests remain pending in the native broker until the owner explicitly approves or declines them throughGET/POST /sessions/{id}/approvals. The same mode can create bounded read-only workspace checkpoints throughGET/POST /sessions/{id}/checkpoints; checkpoint data is stored in Clapilot runtime state outside the repository and never mutates or rolls back the worktree. Preview, checkpoint restore, and mutating Git actions remain unavailable until their separate guarded contracts are implemented. - The
Changesinspector exposes guarded Git actions throughPOST /sessions/{id}/git-actionsand the equivalent job endpoint. Stage/unstage accepts one validated repository-relative path, commit accepts only a bounded message and requires staged changes, and push is fixed to a non-forceoriginpush of the current branch. Every action rechecks the owner/trusted workspace plus the branch and HEAD the user reviewed; stale requests are rejected. Web uses a two-click push confirmation, and Apple mirrors the same controls. Destructive discard/reset, force push, arbitrary remotes/refspecs, and checkpoint restore are intentionally unsupported. - Wrapper preferences persist without changing chat semantics: web retains the activity filter and resizable detail width in local storage, while Apple retains the activity filter in
AppStorage. Selecting any other session still resets the detail mode toConversation, so a previously inspected Terminal/Changes/Approvals pane can never hide a normal chat transcript. Web exposesCmd/Ctrl+Kfor activity search,Cmd/Ctrl+Shift+Nfor a new agent, and Escape to clear search; macOS mirrors the search and new-agent shortcuts. - Runtime conversations render oldest-to-newest across providers. Reply composers are available only in
Conversation, preventing prompts from being entered while inspecting diagnostics; detached jobs retain their existing prompt/terminal workflow. - Interactive Codex sessions render a typed timeline instead of a flat log wall: assistant replies are unboxed markdown prose, user messages are compact trailing bubbles, and Codex tool/command lifecycle items are quiet expandable rows. Tool details expose command/cwd, duration, exit code, output, and raw payload only on demand; unmatched low-level output is collapsed behind a bounded Terminal output disclosure.
- Multi-turn sessions remain one continuous chronological conversation. Turn IDs, event counts, provider avatars, and timestamps are not persistent section headers; timestamps stay secondary, while user prompts and assistant prose establish the turn boundaries naturally.
- Detached Codex jobs run
codex exec --json. The job log preserves the legacysourceandmessagefields and additionally carries typedeventType,itemType,itemId,turnId,itemStatus,item, andusagefields. Web and Apple renderagent_message,command_execution,file_change, MCP/dynamic tool, and web-search items directly instead of classifying CLI stdout heuristically. - Detached-job detail views use the same visual language on web and Apple: task and follow-up prompts appear as user messages, assistant text renders once as prose, the newest two tool actions stay visible, and older tool calls fold behind a
+N previous tool callsdisclosure. Routine runtime initialization/status chatter stays out of the primary transcript. The currently open job/session follows its SSE stream, preserves structured event fields across reconnect replay, and reconnects after transient transport failures. When a job is linked to a canonical session, the linked transcript owns user/assistant prose and job logs contribute only non-conversation activity, preventing duplicate answers. - Repository-backed web timelines show a compact changed-files card with file count and diff totals;
View diffopens the existing guarded Changes inspector. The composer remains anchored below the scroll content as a rounded floating surface, with attachments and the provider-scoped model selector inside its footer. - Bridge runtime tool calls (
tool.start/tool.endrows fromagent_events, e.g. Claude CLI Bridge and Codex bridge runs) are reconstructed into the same expandable tool rows: calls are paired bytoolCallId, labeled with the tool name plus an args-derived summary, and expose the executed command ($ …), result preview, and full raw JSON payload on demand. Older consecutive tool calls collapse while the newest actions remain visible.run.postprocess.*chatter is hidden unless it failed. The Apple client mirrors the same behavior. - Clapilot-code entries use the same oldest-to-newest transcript direction as Codex and Claude detail panes, auto-scroll to the newest turn, and include a footer composer for follow-up prompts into the same runtime session. Raw runtime events and metadata stay hidden behind the provider icon in the pane header.
- Claude bridge entries expose attached model/tool runs plus the persisted Claude harness session id. The module footer can send follow-up prompts back into the same Claude bridge session; if the Claude CLI process is still running, the prompt is steered into the live stream, otherwise a new runtime turn resumes the persisted Claude session.
- The read-only native session detail pane shows readable timestamps on each model run and nests per-run tool usage events inside the corresponding turn/run card. When no classic
chat_nachrichtentranscript row exists for a native runtime session, the pane reconstructs the visible session transcript from the persisted run history instead of showing an empty transcript block. For native runtime runs that used a Mixture-of-Agents (MoA) preset, the run view also lists the reference model outputs (per-reference provider/model and response) alongside the aggregated answer. - Interactive sessions continue directly from the terminal pane footer (no separate lower controls composer). Detached jobs expose a follow-up composer directly in the job detail pane: linked-session jobs continue the same hidden session, while plain CLI jobs continue in the same workspace and append output to the job log. The follow-up composers reuse the floating-chat input styling, minus voice/model controls, and support the same inline image/file attachments for first and follow-up turns. The new-agent composer carries repository and inferred harness/model selection plus optional speech-to-text in one footer row.
- User-owned local coding sessions and local job follow-ups support workspace file references in the composer. Type
@(or use the@action), search the active working directory, and select a relative path. The request carries the selected paths asfileReferences[]; Codex, Claude CLI, and Clapilot-code are instructed to inspect those files relative to their active cwd without uploading or embedding the file contents. New-agent drafts have no checkout yet, legacy ownerless sessions are not claimed implicitly, and remote runner workspaces are not server-local, so the picker is intentionally unavailable in those cases. - The web module keeps the selected interactive or linked-job session attached to
GET /sessions/{id}/stream?replay=0even while the session is ready or waiting for another turn. The initial SSE snapshot supplies recent history once;replay=0prevents the same historical events from being emitted a second time during reconnects. New events then update the open detail pane without overview polling. - Selected Clapilot-code and Claude bridge entries subscribe to the SSE stream of their linked external session and throttle runtime-detail reconciliation while events are arriving. Legacy runtime rows without a linked external stream, or rows whose persisted stream is no longer available after a runtime restart, use a 15-second selected-detail fallback poll instead of rapid SSE reconnects.
- The Apple client subscribes to the same SSE endpoints (
GET /jobs/{id}/streamandGET /sessions/{id}/stream) for the selected activity item. Session list calls receive the new compact summary view by default, while streamed job logs and linked-session events remain the live detail path.
Run jobs on remote machines
- Detached jobs can set
executionTarget: "remote"with providercodexorauto, and optionallyremoteRunnerIdto wait for a specific registered machine; otherwise any online Codex runner can claim the job. The web+ New Agentpanel exposes local execution, automatic remote claiming, and pinned remote runners through its runner dropdown. - Remote execution is pull-based:
scripts/agent-orchestrator-codex-remote-runner.mjsruns on the remote machine, connects outbound to Clapilot withCLAPILOT_HUB_SHARED_SECRET, claims queued Codex work, checks the repository out from a local shared bare cache into a per-job worktree, runscodex, and reports logs/status back to the same job stream. - The runner includes a read-only CLI-session snapshot in its heartbeat. It scans the remote machine's Codex home (
CLAPILOT_REMOTE_RUNNER_CODEX_HOME,CODEX_HOME, or~/.codex) forstate_*.sqlitethread metadata and Codex CLI/Desktop history files (session_index.jsonlplussessions/**/rollout-*.jsonl). It also scans~/.clapilot-code/sessions/*.jsonl, orCLAPILOT_REMOTE_RUNNER_CLAPILOT_CODE_SESSIONSwhen configured. Both harnesses are exposed per machine throughGET /remote-runners/{runnerId}/codex-sessions; selecting a session requests transcript detail, which the runner includes in a later heartbeat, so detail loading is asynchronous and outbound-only. - Replying to a scanned Codex session requires the Node runner
codex-remote-runner/0.2.3or newer and runscodex exec resumewith the explicit Codex home. Replying to a scanned Clapilot Code session requirescodex-remote-runner/0.2.4or newer and runsclapilot-code exec resume. Both paths use the scanned cwd when it still exists, without cloning or creating a repository worktree. The macOS Swift runner does not claim session-resume assignments. - macOS machines can instead run the signed
Clapilot Remote Runnermenu-bar app. Its popup has anOverview/Connection/Logsegmented control:Overviewshows live worker stats, active sessions, and recent finished sessions (succeeded/failed/cancelled) with timing;Connectionholds the Clapilot URL, Hub shared secret, runner id/label, workspace path, polling interval, slot count, and the Save/Test heartbeat/Quit actions; the secret is stored in Keychain. Start/stop and heartbeat status stay in the popup header and status bar. Settings -> Agent Orchestratorlists registered remote machines with online/stale status, slots, capabilities, version, running jobs, and last heartbeat.- The Clapilot server does not need SSH access or an inbound port on the remote machine. Treat connected runners as trusted, because repo jobs receive a GitHub token for cloning private repositories.
GitHub and GitLab automations
Configure named GitHub and GitLab accounts in Settings -> App Verbindungen, then select repositories in Settings -> Agent Orchestrator. GitLab connections accept GitLab.com or a self-managed instance URL. The settings page and the New Agent composer show one combined repository list; each row carries its GitHub or GitLab suffix icon and retains the source connection so same-named repositories do not collide. The picker caches the last loaded union locally, and a refresh button remains available.
The repository automation matrix selects a concrete model independently for PR review, issue implementation, and mention handling on every enabled repository. The picker reuses the same Codex, Claude, and Clapilot Code model catalog as New Agent; users select only the model, and the model's provider determines the coding harness. Main-CI failure fixes and Symphony coding tasks dispatched from that repository's mapped task board use the repository's issue-implementation model. Existing rows without model metadata remain backward-compatible with the former global provider defaults until the configuration is saved, at which point enabled rows persist concrete { provider, model } selections as prReviewModel, issueObserverModel, and mentionObserverModel inside agent_orchestrator_repo_automation_config.
For each repository you can independently enable:
- automatic pull-request or merge-request reviews
- automatic issue observer runs
- automatic mention replies
- default-branch CI failure fixes from GitHub or GitLab webhooks
Trigger mode is configured per repository as polling or webhook. Webhook rows show a repo-specific tokenized URL for the matching GitHub or GitLab project webhook; polling rows run from the interval loop. GitLab merge-request, issue, note, pipeline, and push hooks are normalized to the same scoped automation families as their GitHub equivalents.
Integrations and providers:
- Each repository retains its named GitHub or GitLab connection. Review scans, checkout, review submission, issue assignment, follow-up tracking, mentions, and CI inspection all use that same provider-specific connection.
- GitLab parity covers nested group paths, GitLab.com and self-managed base URLs, merge requests, issues, notes/discussions, approvals, pipeline jobs, emoji reactions, and project webhooks.
- Legacy
github_token/github_pr_review_tokenvalues still exist as compatibility fallbacks, but the intended admin flow is named GitHub integrations. - GitHub automations can select
codex,claude, or localclapilot-code; the OpenClaw compatibility fallback is not used for review, implementation, or mention-reply submission.
PR review: scans non-draft open PRs, deduplicates by repo + PR number + head SHA, and derives a GitHub review decision from the generated findings:
REQUEST_CHANGESwhen any finding is taggedcriticalor the review explicitly asks for changesAPPROVEwhen the review reports no issues or onlymedium/minorfindingsCOMMENTremains the fallback when the review output cannot be classified
Issue observer: assigns the issue to the configured GitHub identity when a coding run starts, comments that implementation has started, and closes the issue immediately after pickup so it cannot be picked up by another observer run. On success it persists the issue-to-PR link and comments the PR link back onto the issue. If another active implementation session already owns the same issue, the duplicate pickup is treated as already in progress and the issue stays closed. If implementation fails and no PR can be recovered, it reopens the issue and records the failure with the normal retry backoff. Codex-backed issue implementations default to interactive sessions so the same session can be resumed for later PR follow-up work. Issue observer settings include an Agent Goal toggle: when enabled and the implementation provider resolves to Codex or Claude, implementation and PR creation runs start with a /goal command derived from the issue task. PR creation prompts require PR bodies to include a closing issue reference plus a ## Testing Instructions section; custom issue-observer prompts still receive this mandatory PR body contract after template expansion.
Main CI failure fix: webhook-only. When enabled for a webhook-mode repository, completed failing workflow_run, check_run, or check_suite events on the repository default branch start a coding run from that default branch. The run must push a branch, open a PR, include testing instructions, and then enters the same tracked-PR follow-up loop as issue-observer PRs. Pull-request branch failures stay on the existing tracked-PR follow-up path.
Tracked PRs: PRs opened by orchestrator-managed coding runs or interactive sessions are tracked durably in the database until they merge or close.
- If an issue observer run fails after a linked PR was already opened, or a later manual/chat run opens a PR that closes the watched issue, the next issue scan recovers that PR into the same durable tracked-PR follow-up state instead of leaving it unmonitored.
- While a tracked PR remains open, the orchestrator watches for merge conflicts, failing GitHub checks, PR review comments, top-level PR review bodies, GitHub Actions CI report comments, and mention activity on the open PR branch. Failing check follow-up is not gated only by the PR thread
updated_attimestamp, so edited GitHub Actions smoke/e2e reports still retrigger the tracked PR worker when the check run fails again. - When a tracked PR still has an owning interactive session, follow-up events are redirected into that same session as another turn so the original agent keeps its context.
- If no usable session exists,
CHANGES_REQUESTEDreviews, merge conflicts, and critical comments trigger an immediate follow-up run, while non-critical comments keep the guarded triage fallback. Triage can choosereplyfor direct questions, status requests, or clarification comments and post a GitHub PR conversation reply without changing code; it choosesfollow_upfor small safe code fixes (which can also post a result-specific PR conversation reply after updating the branch);ignoreis reserved for noise or duplicate comments. - Tracked PR status exposes recent follow-up trigger outcomes and failures for debugging (
lastScanResults, recent handled triggers, recent failures). - Active tracked-PR turns are checkpointed before agent execution. After a runtime restart the original trigger is restored once with the explicit
requeued_after_restartstatus and dispatched with the same composite idempotency key. Review/conversation replies carry a hidden operation marker, so replay after a crash reuses an already posted GitHub comment or GitLab merge-request note instead of posting it twice. - Detached/manual jobs that explicitly implement a GitHub issue share the same issue-level lock as the issue observer: a second job for the same repo issue is rejected while an implementation session is active or once an open PR already closes the issue.
Mention observer: watches configured issue and PR conversations for @<github-login> mentions of the GitHub identity behind the configured token, adds a lightweight reaction, and then either redirects the mention into the tracked PR session or starts a target-specific session/workspace for that PR or issue.
Symphony task-board dispatch
- Symphony's poll loop is controlled by a persisted enable/disable flag in
app_settings.agent_orchestrator_symphony_enabled.POST /orchestrator/startenables and starts the loop,POST /orchestrator/stopdisables it and clears queued retries.GET /statusincludesenabled; disabled loops do not dispatchaufgabencandidates. The UI control lives underSettings -> Agent Orchestrator, not in the user-facing module view. - The Symphony task-board toggle only controls
aufgabencoding task dispatch; GitHub automations continue to run from the per-repository automation matrix even when Symphony is disabled or no task board is selected. - Dispatch observes the configured default task board in
app_settings.agent_orchestrator_symphony_task_board_idplus any repo-specific boards selected in the Settings GitHub automation matrix. If Symphony is enabled without a default board or a repo board mapping, no task dispatch happens. - Tasks inside repo-specific boards inherit the mapped GitHub repository automatically. Tasks inside the default board still need a concrete
repo:owner/namereference in the task description so the agent can safely clone the repository. - Repository coding tasks dispatched from a mapped board resolve the same concrete
issueObserverModelused for issue implementation, including its implied Codex, Claude, or Clapilot Code harness. The job record stores the requested provider/model and the provider-reported effective model remains visible in Agent Activity. Rows without a concrete model retain the legacy global issue-observer provider fallback. - Symphony only appends mandatory branch/commit/PR instructions for explicit repository coding tasks. General
aufgabenrecords such as document or payroll review tasks without a repo stay in the non-repository task path and must not create GitHub PRs. - Symphony-created PR bodies must include both the
Requested-by: Symphony task ...line and aClapilot task: .../aufgaben/{id}link for traceability (using the configured public app URL, not internal container service URLs), plus a## Testing Instructionssection with concrete validation commands or manual checks. The coding job retains the originating task ID. After detecting the created PR, Symphony adds a system comment with its canonical URL, PR number, and open status to that task, localized from the task creator's persisted UI language (de,en, orit; private-board owner and German fallbacks). Separate PRs accumulate as separate comments; a typed UUID advisory lock and URL lookup run as separate statements in one transaction so retries and concurrent completion paths do not add the same canonical PR URL twice. Linking still runs when optional tracked-PR follow-up registration fails. - Symphony tasks and repository automations share one resource-aware dispatch budget. A slot is reserved synchronously before asynchronous workspace preparation, preventing one poll from exceeding the configured limit. The safe default is one local run. The effective limit is also capped by total and currently free memory so coding workers cannot consume the runtime's safety reserve;
GET /statusexposeseffectiveMaxConcurrentAgentsand thecapacitysnapshot. - Container bootstrap does not start Symphony by default. Set
CLAPILOT_AGENT_ORCHESTRATOR_BOOTSTRAP_ENABLED=trueonly when a deployment should force the poll loop on during boot.
How the agent can drive it (tools)
Native ClapilotAICore tool contracts cover repository job orchestration and interactive session orchestration (services/clapilot-agent/src/sessions/index.mjs):
agent_orchestrator_list_reposagent_orchestrator_start_jobagent_orchestrator_list_jobsagent_orchestrator_get_jobagent_orchestrator_follow_up_jobagent_orchestrator_stop_jobagent_orchestrator_start_sessionagent_orchestrator_list_sessionsagent_orchestrator_get_sessionagent_orchestrator_send_turnagent_orchestrator_fork_sessionagent_orchestrator_close_session
Wiring notes:
src/lib/agent-runtime/tool-proxy.tsforwardsagent_orchestrator_*tool calls into this module API on behalf of the linked Clapilot user.- Repository start tools forward the provider, named connection, instance URL, and clone URL returned by
agent_orchestrator_list_repos; the module re-resolves the named connection and derives authenticated remote URLs server-side before using its token. services/clapilot-agent/src/orchestrator-sessions/index.mjsbrokers interactive session lifecycle, Codex app-server integration, and event streaming intoagent_events.- Native/channel agent runs should use these tools for generic repo coding and PR-preparation work instead of legacy gateway execution paths.
- Website Canvas uses the same interactive session broker internally for iterative website edits, but keeps repo sync, preview, commit, and push ownership inside Website Canvas.
Configuration & limits
Repository checkout and cleanup:
- Server-side local automation keeps shared bare repository caches under
.agent-orchestrator/reposand creates per-job/per-session Git worktrees under the existing job roots (manual-job-workspaces,github-automation-workspaces/*, and.agent-orchestrator/sessions). - GitHub and GitLab tokens are supplied to Git fetches through temporary
http.extraheaderarguments instead of being persisted in remote URLs. - Repository-backed Clapilot-code sessions keep the initiating Clapilot user id on initial and resumed turns, so user-scoped Agent Orchestrator tools remain available inside the linked coding session. Older sessions acquire and persist that binding when they are resumed from the signed-in module UI.
- Repository shells receive the selected credential only as ephemeral process environment: GitHub uses
GH_TOKEN, GitLab usesGL_TOKENandGITLAB_HOST, and Git push uses temporary process-local Git config. Named-connection and GitLab sessions use the embedded native runtime so credentials remain scoped to one session. Tokens are never written to the checkout remote URL, job log, or external-session metadata. - Completed local automation worktrees are removed at job completion where possible, and the instance cleanup worker removes stale PR review, issue, main-CI, tracked PR, manual-job, and Symphony workspaces while protecting active work. Native interactive session roots are excluded because the session subsystem owns their resumable lifetime.
- The crash-recovery reaper runs at startup and hourly.
AGENT_ORCHESTRATOR_WORKSPACE_TTL_HOURScontrols the orphan TTL (default24); an atomic PID lock prevents concurrent reapers. Lease state plus the in-memory queued/running inventory protects active workspaces, and terminal jobs with linked interactive sessions stay protected for follow-up turns. Paused or other non-terminal Symphony task states remain active; only configured terminal task states become eligible after the TTL. - Every GC emits structured
agent_orchestrator_workspace_gctelemetry with workspace counts andbytesBefore,bytesAfter, andbytesFreed.AGENT_ORCHESTRATOR_WORKSPACE_HARD_LIMIT_GIBoptionally rejects every new local manual or GitHub-automation worktree and emitsagent_orchestrator_workspace_backpressureonce orchestrator storage reaches the configured limit. - Run-local dependency trees are never copied between worktrees. Removing a worktree recursively removes its
node_modules; package managers should use their host/container-level shared content store (for pnpm, the configured global pnpm store). - Bare repo caches are retained for reuse and are pruned by age only when Git reports no linked worktrees. The default cache TTL is 336 hours.
- Remote runners keep their own sibling
reposcache next to the configuredworkspacesdirectory and remove stale completed job workspaces after 24 hours by default.
Detached Claude auth:
- Detached Claude jobs resolve auth from the configured Anthropic provider in Clapilot's database before falling back to legacy app settings or local CLI state.
- Claude subscription auth uses the stored Claude
setup-tokenand maps it to the Claude CLI internally; it does not rely onANTHROPIC_OAUTH_TOKENbeing injected via Docker.env.
Supervisor:
- An optional supervisor loop periodically reviews orchestrator state (for example stale runs) and can be triggered or toggled via
GET|POST /supervisor/run,POST /supervisor/start, andPOST /supervisor/stop. - Its configuration is persisted in
app_settings(agent_orchestrator_supervisor_enabled,..._provider,..._model,..._interval_ms,..._stale_run_ms); the interval floor is 1 minute and the stale-run floor is 15 minutes.
Optional env overrides:
AGENT_ORCHESTRATOR_CODEX_ARGSCLAPILOT_AGENT_CODEX_SERVICE_TIERAGENT_ORCHESTRATOR_CLAUDE_ARGSOPENCLAW_PRIMARY_MODELCLAPILOT_AGENT_ORCHESTRATOR_BOOTSTRAP_ENABLED(force Symphony on at container boot)SYMPHONY_MAX_CONCURRENT(configured local automation concurrency, default1, maximum8)AGENT_ORCHESTRATOR_MIN_FREE_MEMORY_MIB(memory reserve protected from new local automation runs, default3072)AGENT_ORCHESTRATOR_RUN_MEMORY_BUDGET_MIB(estimated memory required per new local automation run, default4096)
API endpoints exposed by module
Base: /api/modules/agent-orchestrator/api — implementation: bundled-modules/agent-orchestrator/api/handler.mjs; reference: bundled-modules/agent-orchestrator/README.md.
GET /toolsGET /authPOST /authDELETE /authPOST /reposGET /statusGET /pollPOST /configGET /webhook/{token}POST /webhook/{token}POST /orchestrator/startPOST /orchestrator/stopGET|POST /supervisor/run,POST /supervisor/start,POST /supervisor/stopGET /remote-runnersPOST /remote-runners/heartbeatPOST /remote-runners/claimGET /remote-runners/{runnerId}/codex-sessionsGET /remote-runners/{runnerId}/codex-sessions/{sessionId}POST /remote-runners/{runnerId}/codex-sessions/{sessionId}/follow-upPOST /remote-runners/jobs/{id}/eventsGET /coding-models(model options plus resolved default forclaude,codex, andclapilot-code; the Clapilot-code list contains only usable non-subscription catalog models)GET /jobsPOST /jobs(supports an optionalmodelfor all coding providers — Clapilot-code direct provider models plus concrete Claude/Codex CLI models;executionTarget: "remote"plus optionalremoteRunnerIdremains Codex-only, and optionalcodexGoalEnabledenables Codex/Claude first-turn/goalbootstrapping)GET /jobs/{id}GET /jobs/{id}/streamGET /jobs/{id}/workspace-files?q={query}&limit={count}(local jobs only; linked jobs resolve the linked coding session cwd)POST /jobs/{id}/follow-up(supports an optionalmodeloverride and validated relativefileReferences[]for local follow-up runs; remote jobs reject file references)DELETE /jobs/{id}GET /sessionsPOST /sessions(supports canonicalprovider: "clapilot-code"plusmodel; legacypi/embedded_pialiases normalize to Clapilot-code and use the internalembedded_piadapter)GET /sessions/{id}DELETE /sessions/{id}GET /sessions/{id}/workspace-files?q={query}&limit={count}(returns bounded, Git-ignore-aware relative file suggestions for an owned local workspace)POST /sessions/{id}/turns(supports an optional per-turnmodeloverride plus validated relativefileReferences[], forwarded to the native session broker; stale or invalid selected paths reject the request)GET /sessions/{id}/streamPOST /sessions/{id}/forkPOST /sessions/{id}/archive
Troubleshooting
- The module is not visible: Agent Orchestrator is hidden while Developer mode is disabled; enable Developer mode first.
- Symphony is enabled but no tasks dispatch: check that a default task board (
app_settings.agent_orchestrator_symphony_task_board_id) or a repo-specific board mapping is configured, and that default-board tasks contain a concreterepo:owner/namereference. - A second job for the same GitHub issue is rejected: the issue-level lock is intentional — one implementation session or open closing PR owns the issue at a time.
- Remote job never starts: verify the runner heartbeat in
Settings -> Agent Orchestrator(online/stale status) and that the runner shares the correctCLAPILOT_HUB_SHARED_SECRET; remote execution is outbound-only from the runner.
