Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ edition = "2024"
license = "MIT"
repository = "https://github.com/kumanday/OpenSymphony"
rust-version = "1.97.1"
version = "2.11.3"
version = "3.0.0"

[workspace.lints.rust]
rust_2018_idioms = { level = "warn", priority = -1 }
Expand Down
39 changes: 32 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# OpenSymphony

OpenSymphony is a Rust implementation of the [OpenAI Symphony](https://github.com/openai/symphony) specification for orchestrating AI coding agents. It connects to [Linear](https://linear.app) for issue tracking and can run issues through either the managed [OpenHands](https://github.com/OpenHands/OpenHands) agent-server or the local Codex app-server harness.
OpenSymphony is a Rust implementation of the [OpenAI Symphony](https://github.com/openai/symphony) specification for orchestrating AI coding agents. It connects to [Linear](https://linear.app) for issue tracking and runs issues through the managed [OpenHands](https://github.com/OpenHands/OpenHands) agent-server, the local Codex app-server, or a local ACP v1 agent.

![OpenSymphony desktop task graph showing Completed, Current, and Backlog panes with selected-task critical path](docs/images/os-tg-ancestry-critical-path.png)

Expand All @@ -10,25 +10,31 @@ OpenSymphony automates software development workflows by:

1. **Polling Linear** for issues in active states (Todo, In Progress, etc.)
2. **Creating isolated workspaces** for each issue with lifecycle hooks
3. **Dispatching AI agents** via OpenHands or Codex to work on issues autonomously
3. **Dispatching AI agents** through OpenHands, Codex, or a configured ACP profile
4. **Managing retries, reconciliation, and cleanup** based on issue state changes
5. **Providing a desktop operator dashboard** for task graph, run detail, and memory navigation

### Key Features

- **Hierarchy-aware scheduling**: Parent issues wait for sub-issues to complete
- **WebSocket-first runtime**: Real-time agent updates with REST reconciliation
- **Multi-repository projects**: Repository-bound sub-issues run in their own checkouts; an unlabeled parent checks their merged work
- **Live runtime updates**: OpenHands uses WebSocket with REST reconciliation; Codex and ACP use local stdio
- **Per-issue workspaces**: Deterministic, isolated directories with lifecycle hooks
- **GraphQL-only Linear integration**: Agent-side Linear reads and writes through checked-in helper/query assets
- **GraphQL-only Linear integration**: The scheduler polls Linear; agent-side writes use checked-in helper/query assets
- **Dependency-aware Task Graph**: Shows dispatchable work, roadmap backlog, and selected-task critical paths
- **Harness selection**: Default OpenHands agent-server execution, plus local Codex app-server support for ChatGPT subscription-backed runs
- **Harness selection**: OpenHands, local Codex app-server, or a configured ACP v1 stdio agent
- **Code Graph**: Tree-sitter-backed symbols, diagnostics, and source-cited structural context for agents
- **Operational Knowledge Graph**: Builds agent-queriable and human-navigable memory from completed work

OpenSymphony `1.0.0` is the compatibility boundary for the GraphQL-only Linear
rewrite. See [Migration Guide](docs/migration-1.0.0.md) if you are upgrading an
older setup.

Version 3.0.0 adds explicit multi-repository project routing and production
ACP worker profiles. Existing single-repository workflows remain available.
See the [3.0 upgrade guide](docs/migration-3.0.0.md) before selecting a new
routing mode or harness.

Packaging note: crates.io exposes a single public package, `opensymphony`.
Internally, the repo still keeps clear subsystem boundaries under
`crates/opensymphony-*`, but those directories are now internal module trees,
Expand Down Expand Up @@ -216,6 +222,15 @@ cd /path/to/target-repo
opensymphony run
```

For a central configuration in another location, pass
`--config /absolute/path/to/config.yaml` to `opensymphony run`.
The default single-repository workflow needs no multi-repository labels.
For a project spanning several repositories, follow the
[multi-repository guide](docs/multi-repository.md). To run a local ACP agent,
follow the [ACP harness guide](docs/acp.md). Repository routing and harness
selection are independent: an ACP profile can run a child bound to one
repository in a multi-repository project.

For real-time monitoring while the orchestrator is running, run the TUI in a separate terminal window:
```bash
opensymphony tui
Expand Down Expand Up @@ -335,10 +350,11 @@ flowchart TB
orchestrator["Orchestrator Scheduler"]
workspace["Workspace Manager"]
control["Gateway + Control API<br/>GET /healthz, /api/v1/snapshot, /api/v1/capabilities"]
runtime["Harness Runtime Client<br/>OpenHands REST/WebSocket or Codex stdio"]
runtime["Harness Runtime Client"]
linear_read["Linear Read Adapter"]
routing["Repository binding and parent integration"]

orchestrator --> workspace
orchestrator --> routing --> workspace
orchestrator --> runtime
orchestrator --> linear_read
orchestrator --> control
Expand All @@ -357,13 +373,19 @@ flowchart TB
linear["Linear"]
openhands["OpenHands Agent-Server"]
codex["Codex App-Server"]
acp["Local ACP v1 agent"]
config["Central project set and harness profiles"]

operator --> control
config --> routing
config --> runtime
workspace --> issue_ws
runtime --> openhands
runtime --> codex
runtime --> acp
openhands --> agent
codex --> agent
acp --> agent
linear_read -->|read issues| linear
graphql -->|agent-side writes| linear
```
Expand Down Expand Up @@ -517,11 +539,14 @@ OPENSYMPHONY_LIVE_OPENHANDS=1 ./scripts/live_e2e.sh

- [Architecture](docs/architecture.md) - High-level design and component interactions
- [Configuration](docs/configuration.md) - Target repo bootstrap and runtime config
- [Multi-repository projects](docs/multi-repository.md) - Repository labels, parent integration, and rollout
- [ACP harness](docs/acp.md) - Local agent profiles, operator requests, and recovery
- [Deployment Modes](docs/deployment-modes.md) - Local vs hosted deployment
- [Installer and Distribution Strategy](docs/installer-and-distribution.md) - Future signed installer shape and DuckDB packaging boundaries
- [Operations](docs/operations.md) - Doctor, rehydration, diagnostics, and local ops
- [Testing](docs/testing-and-operations.md) - Test strategy and validation layers
- [Migration Guide](docs/migration-1.0.0.md) - Breaking changes and upgrade steps for 1.0.0
- [3.0 upgrade guide](docs/migration-3.0.0.md) - Moving from single-repository workflows and selecting ACP
- [AGENTS.md](AGENTS.md) - Repository guidelines for coding agents
- [Development Guide](docs/DEVELOPMENT.md) - Contributing and development details

Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@opensymphony/desktop",
"version": "2.11.3",
"version": "3.0.0",
"description": "OpenSymphony Tauri desktop app shell",
"type": "module",
"private": true,
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion apps/desktop/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "opensymphony-desktop"
version = "2.11.3"
version = "3.0.0"
description = "OpenSymphony Tauri desktop application shell"
license = "MIT"
homepage = "https://opensymphony.dev"
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"productName": "OpenSymphony",
"mainBinaryName": "OpenSymphony",
"version": "2.11.3",
"version": "3.0.0",
"identifier": "dev.opensymphony.app",
"build": {
"beforeDevCommand": "npm run dev",
Expand Down
89 changes: 88 additions & 1 deletion docs/acp.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,94 @@ visibility: public
last_memory_sync: 2026-09-22T11:51:20.924956+00:00
---

# Acp
# ACP harness

ACP is a way to run an agent that speaks Agent Client Protocol v1 over a local
stdio process. OpenSymphony still owns issue scheduling, workspaces, retries,
and operator decisions. The agent process works inside the issue's checkout.
ACP is an execution choice: it can run a single-repository issue or a child in
a [multi-repository project](multi-repository.md).

```mermaid
sequenceDiagram
participant S as OpenSymphony scheduler
participant H as ACP session host
participant A as Local ACP agent
participant O as Operator client
S->>H: Start turn in the issue checkout
H->>A: initialize, session/new, session/prompt
A-->>H: Updates and optional permission request
H-->>S: Bound run events and pending request
S-->>O: Run detail and request options
O->>S: Answer for the current run
S->>H: Deliver bound answer
H->>A: ACP callback response
A-->>H: Prompt result
H-->>S: Outcome and retained session state
```

## Configure a local agent

Install the agent's CLI and complete its own login first. Then add a named
profile to the selected central config. This excerpt assumes the rest of the
instance and tracker configuration already exists:

```yaml
routing:
harness: acp
harness_profile: cursor
acp:
profiles:
cursor:
command: /absolute/path/to/agent
args: [acp]
transport: stdio
protocol_versions: [1]
permissions:
mode: operator
```

`routing.harness_profile` chooses a profile ID; the ID is not a vendor switch
in the scheduler. Use an absolute executable path and literal arguments. If
the agent needs environment credentials, map target names to source variable
names under `env_refs`; do not put values in the file or arguments. Omit
`auth` when the CLI's existing login is sufficient. An explicit `auth.method_id`
must be advertised by the agent.

With `permissions.mode: operator`, a live TUI, web, or desktop client must be
available to answer permission requests. `deny` selects an offered one-time
rejection; `allow_once` selects an offered one-time approval for a trusted
unattended profile. Unsupported options are cancelled rather than guessed.
See [ACP profile configuration](configuration.md#acp-stdio-profiles) for session
model/mode/options, host services, MCP attachments, deadlines, and extension
registration.

Run `opensymphony run --config <path>` after selecting the profile. A profile
preflight checks the executable and configuration; the live session negotiates
authentication, persistence, modes, options, and optional methods. Inspect
`/api/v1/capabilities` and the active run detail before offering an operation
to an operator. A profile entry alone does not mean that an optional method is
available.

## Sessions, recovery, and vendor support

The session host retains an idle agent process for compatible later turns.
Recovery loads or resumes a known session only when the peer advertised the
method. If a prompt might have been submitted before a disconnect, the run is
fenced for reconciliation; OpenSymphony does not resend that prompt blindly.
Cancel acknowledgement follows a matching stopped prompt result. The
[operations guide](operations.md#acp-client-validation-and-limits) describes
limits and recovery diagnostics.

The [live qualification report](acp-live-qualification.md) records tracked
issue runs through pinned Cursor and Devin CLIs. Both advertised
`session/load`; neither advertised `session/resume` in that qualification.
Cursor's registered plan and todo methods are limited to its observed pinned
version. Generic ACP v1 profiles do not inherit those vendor methods.

ACP agents are local processes with trusted host access. Choose the executable,
credentials, and permission policy accordingly. The process runs with its
current directory set to the issue workspace.

<!-- BEGIN OPENSYMPHONY MANAGED MEMORY SYNC -->

Expand Down
33 changes: 23 additions & 10 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@

## 1. Objective

Implement the Symphony orchestration model in Rust while using OpenHands as the
execution substrate and FrankenTUI as an optional operator client.
Implement the Symphony orchestration model in Rust. The scheduler can use the
OpenHands agent-server, the local Codex app-server, or a configured local ACP
v1 agent for execution. FrankenTUI is an optional operator client.

The system must preserve these boundaries:

- the orchestrator is the source of truth for scheduling state
- the tracker is polled and reconciled by the orchestrator
- each issue executes in its own workspace
- each repository-bound work issue executes in its own checkout
- a multi-repository parent uses a separate integration workspace after its
children merge
- `WORKFLOW.md` remains the repo-owned policy and prompt contract
- UI is optional and must not affect correctness

Expand Down Expand Up @@ -43,7 +46,7 @@ OpenSymphony is split into five layers:
2. Configuration layer
- typed workflow/config loader
- env and path resolution
- OpenHands extension config
- project sets, repository inventory, and harness profiles
3. Coordination layer
- orchestrator actor
- retry queue
Expand All @@ -54,6 +57,7 @@ OpenSymphony is split into five layers:
- OpenHands REST client
- OpenHands WebSocket runtime stream
- local Codex app-server stdio adapter
- local ACP v1 stdio session host
- issue session runner
5. Observability layer
- structured logs
Expand All @@ -67,6 +71,10 @@ Packaging distinction:
- the `crates/opensymphony-*` directories are internal module trees compiled
into that one package

Start with the [multi-repository guide](multi-repository.md) for issue binding
and parent integration, or the [ACP guide](acp.md) for local agent setup and
operator requests. The two choices are independent.

## 3. Main decisions

### 3.1 Rust owns orchestration
Expand All @@ -92,7 +100,7 @@ including when a subtree no longer requires merge evidence. Reopening or
recovering nonterminal work invalidates its durable success receipt before
batched launch preparation.

Repository routing is also orchestrator-owned: terminal child metadata carries
Repository routing is also orchestrator-owned: repository-bound child metadata carries
one alias, the central inventory resolves it to a canonical provider identity,
and the scheduler persists that identity plus its config and inventory
generations before claiming work. Project associations are validation scope,
Expand Down Expand Up @@ -247,7 +255,7 @@ The completed cleanup intent remains in orchestrator state after the parent root
is removed and seeds automatic-capture completion after daemon restart, so a
terminal parent is not routed and captured again without first reopening.

### 3.2 OpenHands is the execution adapter
### 3.2 OpenHands adapter

OpenHands provides:

Expand All @@ -260,7 +268,7 @@ OpenHands provides:

OpenSymphony does not reimplement an agent loop.

### 3.3 WebSocket-first, not WebSocket-only
### 3.3 OpenHands uses WebSocket and REST

REST is still required for:

Expand All @@ -271,7 +279,7 @@ REST is still required for:
- reconnect reconciliation
- restart recovery

### 3.4 One local server, many workspaces
### 3.4 One local OpenHands server, many workspaces

The local supervised topology runs one OpenHands server for the daemon while
passing a distinct `working_dir` per issue.
Expand Down Expand Up @@ -370,7 +378,7 @@ relations, attachments, project content/status updates, and introspection.

## 5. Process model

Local MVP process graph:
Local process graph when an OpenHands route is selected:

```text
opensymphony run
Expand All @@ -380,16 +388,21 @@ opensymphony run
├─ openhands REST client
├─ openhands WebSocket client
├─ optional Codex app-server stdio worker
├─ optional ACP v1 stdio session host
├─ gateway API
├─ control-plane compatibility API
└─ local server supervisor
└─ python -m openhands.agent_server
```

An ACP-only or Codex-only run does not launch the OpenHands server.

The scheduler attaches a `HarnessRouteDecision` to each worker start request.
The default route remains `openhands_agent_server`. Workflow `routing.harness`
or the `OPENSYMPHONY_HARNESS` environment override can select the local
`codex_app_server` route when that harness is available and can start runs.
`codex_app_server` or `acp` route when configured and available. ACP also
requires a named `routing.harness_profile`; the scheduler binds the selected
profile to each prepared run.
Route decisions are emitted as `routing.decision` runtime audit events so dry-run
previews and real dispatches show the selected harness, model, and model
profile.
Expand Down
5 changes: 5 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
This document covers target-repo bootstrap, generated files, and the runtime
configuration that `opensymphony run` expects.

For a first multi-repository setup, read [multi-repository projects](multi-repository.md)
before the field reference below. For a local ACP agent, start with the
[ACP harness guide](acp.md). Repository routing and harness selection are
independent choices in the same selected config.

Operator projections expose project and repository identity through safe
aliases, canonical IDs, and safe remote fingerprints. Credential-bearing
remote URLs and raw credential values are excluded.
Expand Down
Loading
Loading