A coding agent that decomposes a task into a dependency graph of child agents and drives it to completion. State is durable, crashes are recoverable, and the whole graph can be inspected and controlled from the terminal.
GraphAgent is the product name for this project; the repository is published as OpenCode-GraphAgent, a fork of the MIT-licensed opencode terminal AI agent that adds the DAG workflow engine. Not affiliated with or endorsed by the OpenCode team.
A single agent loop struggles once a task has staged dependencies, parallelizable independent work, or a quality gate in the middle. Four judgments shaped this engine:
- Split decisions from volume. Work that must be correct (decomposition, gates, arbitration, final synthesis) runs on an advanced model tier; volume work (exploration, implementation, per-angle analysis) fans out on a standard tier. The standard tier buys accuracy with redundancy: breadth means independent parallel slices fanning into one arbiter, depth means claims get re-verified against code and tests across waves.
- Ask before building the graph. Complex work (
deepmode) goes through a bounded Q&A pass first (1, 3, or 5 rounds), producing a versioned, fingerprinted Requirement Brief with aREADY/NOT_READY/WAIVEDverdict. If the question budget runs out with blockers still open, the verdict isNOT_READY. There is no silent pass. - Gate verdicts need a follow-up. When a checkpoint returns
REVISE/REJECT/BLOCKED, the parent agent has to dispose of it in the same wake turn: extend, replan, start a new workflow, or stop with stated reasons. Summarizing the verdict and ending the turn counts as an orchestration failure under the contract. - Recover from evidence, not guesses. Every state change is a durable event, transitions go through a declared state machine's guards, terminal states are irreversible (one exception, written into the spec), and the read model is a CQRS projection. After a crash, recovery reconciles from durable evidence and never fabricates provider work.
Nothing has to be configured to try it: ask for work that has stages, parallel parts, or a review gate in the middle, and the agent designs a graph and runs it. Three things turn that into a repeatable setup of your own.
Nodes never name their own model. The graph declares which nodes are critical and configuration decides what runs them:
A project .opencode/dag.jsonc overrides the global one in the opencode config
dir; a commented default is seeded there on first use. With only one tier
configured, it serves as the unified default. Resolution order per node is
tier → worker agent model → parent session model; when none of them yields a
model, the workflow is not created and you are asked to configure one.
A workflow spec is a YAML file describing the graph. Put it in a workflow directory and it gains a name:
| Scope | Path | Availability |
|---|---|---|
| Project | .opencode/workflows/<name>.yaml |
This repo, committed with it — the whole team gets the same procedure |
| Global | <opencode config dir>/workflows/<name>.yaml |
Every project on the machine |
The filename stem is the name, and a project file shadows a global one with the same name. A minimal spec:
title: Dependency audit
config:
name: dependency-audit
nodes:
- id: inventory
name: inventory
worker_type: explore
depends_on: []
required: true
prompt_template:
id: config-explore
input:
target: "package.json files and lockfiles"
- id: report
name: report
worker_type: general
depends_on: [inventory]
report_to_parent: true
prompt_template:
inline: "Flag outdated or duplicated dependencies in {{inventory}} and propose an upgrade order."Then just say "run the dependency-audit workflow" — the agent starts it by
name, no path needed. Ad-hoc specs still work exactly as before: a spec_path
that looks like a path (has a separator or a .yaml/.yml extension) is read
relative to the session directory instead of the library.
The built-in create-dag-workflow skill covers spec authoring: the two
scopes, the file shape, the rules a saved spec must respect (no pinned models,
worker_type must exist, required template variables must be supplied), and
how to verify it. Ask to "save this as a reusable workflow" and the agent
establishes the phases and gates with you, writes the file into the scope you
pick, and proves it by starting it once.
Node prompts come from .opencode/dag-prompts/*.md — 12 templates ship
in-repo, referenced by prompt_template.id. Add your own .md file there to
make a new template available; a global workflow should prefer inline prompts
so it does not depend on a repo-local template.
The engine lives in packages/core/src/dag (state machine, dependency graph, scheduling, event projection, SQLite read model) and packages/opencode/src/dag (workflow service, execution loop, node spawn, admission, review lifecycle, crash recovery, templates). Agents drive it through a single workflow tool; humans watch and control it through the TUI or HTTP API.
Each node declares:
| Field | Purpose |
|---|---|
depends_on |
Dependency edges; cycle detection and dangling-reference validation at creation |
worker_type |
Which agent runs the node (explore, build, general, or any configured agent) |
prompt_template |
Prompt by id (from .opencode/dag-prompts/, 12 templates ship in-repo) or inline, with {{var}} interpolation |
input_mapping |
Map upstream node outputs into template variables ("count": "node-b.output.count") |
condition |
Expression over upstream outputs; false → node skipped, pure descendants cascade-skip |
output_schema |
JSON Schema; the child agent must call submit_result with a matching structured payload |
required |
Failure of a required node fails the workflow |
report_to_parent |
Wake the parent agent when this node reaches a terminal state |
review |
design or diff review phase with an implementation-fingerprint contract (below) |
Workflow-level knobs: max_concurrency (default 5), max_node_replan_attempts (5), max_total_nodes (100), per-node timeout_ms (default 10 min, queue wait counts toward the deadline).
- Nodes spawn as real child sessions through the same code path as the
tasktool, wave by wave in dependency order, bounded by a concurrency semaphore. A node is durablyqueuedat admission and the child session only materializes inside the permit, so a 100-node fan-out never creates 100 sessions at once. - Dynamic replanning, pause-first:
pausefreezes scheduling instantly,replanmerges a fragment (add / replace / cancel / restart nodes) atomically against the live graph,resumecontinues. Terminal nodes are immutable; retrying a failed node means adding a replacement under a new id.extendappends nodes, and may reopen a naturally-completed workflow (the single sanctioned exception to terminal irreversibility). - Step mode runs one node at a time for debugging.
- The parent does not poll. Synthetic messages wake it when a
report_to_parentnode or the workflow terminalizes. Checkpoint nodes emit a normalized verdict (ACCEPT/REVISE/REJECT/BLOCKED), and the disposal contract governs what happens next. Iteration is a bounded, verdict-driven replan wave; the graph never contains a cyclic edge.
- Declared transition tables for workflow and node status; every mutation goes through a guard, invalid transitions and terminal violations are typed errors (HTTP 409, not 500).
- All changes are published as durable
dag.*events; a projector writes the SQLite read model inside the publish transaction. History is event replay, not a log table. A drift test fails whenever the projector's guards and the declared transition tables are edited out of sync. - Crash recovery is lazy, per-workflow, and evidence-based: nodes left
runningare reconciled against their child session's durable state. Sessions that finished back-fill their captured output; when execution ownership was genuinely lost, the workflow pauses and the parent decides disposition (replan / resume / cancel). Recovery never adopts or restarts provider work on its own.
- Admission Q&A covers six dimensions (goal, scope, constraints/assumptions, acceptance criteria, evidence, risks) under a bounded policy:
LIGHT(1 round),STANDARD(3),GRILL(5, adversarial). The resulting Requirement Brief is fingerprinted (SHA-256 over a canonical form); material changes invalidate the fingerprint and return admission to questioning. A consumed record is persisted with the workflow and never replayed. - Review nodes declare their phase honestly:
designreviews pre-implementation artifacts;diffreviews the actual implementation and requires the implementation node, a passing verification node, and a fingerprint echo. Changing the implementation changes the fingerprint, so a staleACCEPTcannot satisfy the gate.
-
TUI DAG inspector (command palette →
dag.open): workflow list, wave-ordered node view with live status, node detail (deps, errors, output preview, deadline countdown), andp/r/s/xfor pause/resume/step/cancel;enterdrops into a node's child session. -
Sidebar panel: per-session workflow progress (completed/running/failed/queued), expandable node list, driven by ephemeral summary events, with a fetch-on-open safety net instead of polling.
-
HTTP API (same code path as the tool surface):
GET /dag list workflows POST /dag start a workflow GET /dag/session/:sessionID workflows for a session GET /dag/session/:sessionID/summary progress summaries GET /dag/:dagID workflow detail GET /dag/:dagID/nodes node list GET /dag/:dagID/nodes/:nodeID node detail POST /dag/:dagID/control pause/resume/cancel/replan/extend/step/complete
Everything DAG-specific lives under .opencode/, with a global counterpart in
the opencode config dir (OPENCODE_CONFIG_DIR, else ~/.config/opencode).
Everything else inherits the main opencode configuration.
| Path | Purpose | Global counterpart |
|---|---|---|
.opencode/dag.jsonc |
Model tiers (advanced / standard) and thinking_depth for child sessions |
<config dir>/dag.jsonc, seeded with comments on first use |
.opencode/workflows/*.yaml |
Saved workflow specs, startable by name | <config dir>/workflows/*.yaml |
.opencode/dag-prompts/*.md |
Node prompt templates referenced by prompt_template.id |
— (project-scoped) |
Both dag.jsonc and the workflow library are read lazily, so an edit applies to
the next workflow start without a restart.
- Hooks API: Claude Code hooks protocol compatibility. 26 hook events (
PreToolUse,PostToolUse,SessionStart,PermissionRequest,WorktreeCreate, …) × 5 execution types (command,mcp,http,prompt,agent), loaded from a global/project/worktreehooks.jsonchain or registered per-session over HTTP, with optional workspace-trust gating. See the hooks reference. - Tool robustness: JSON repair for broken multi-byte Unicode escapes in LLM output, structured validation errors with field-level hints, expanded tool docs, child-process pipe fixes.
- CJK & IME fixes: corrections for Chinese/Japanese/Korean input in the terminal UI (IME composition flushing, full-width text handling), plus a Korean IME fix script under
patches/. - Worktree isolation: per-workflow
git worktreeisolation, with experimental sandbox-worktree HTTP endpoints. - Configuration assistant: a standalone Go TUI under
config_assistantfor locating, validating, and editing opencode configuration (cd config_assistant && go run ./cmd/ocfg). - An earlier "Goal auto-loop" and the
/goal,/subgoal,/workflowslash commands are gone; autonomous execution now goes through theworkflowtool and its wake mechanism.
All upstream capabilities (multi-provider, built-in LSP, client/server architecture, TUI/desktop/web clients) are preserved.
Prebuilt CLI binaries (Linux / macOS / Windows, with SHA256SUMS) are published on the releases page. Builds from main are formal releases; builds from dev are prereleases.
From source (requires Bun 1.3+):
bun install
bun dev # TUI
bun dev serve # headless API server (port 4096)
# standalone binary
./packages/opencode/script/build.ts --singleThis fork is not published to npm/brew/scoop. The upstream
opencode-aipackage installs upstream opencode, not this fork.
- CI: typecheck on every PR; the
maingate additionally runs the Bun/Turbo unit suite and config-assistant Go tests (Linux), Playwright e2e (Linux + Windows), an HTTP API contract exerciser, and generated-SDK freshness checks. - DAG-specific tests: core scheduling unit tests, projector/state-machine drift tests, workflow lifecycle integration tests, and HTTP API exercise scenarios for every DAG route.
Mixed license model:
| Content | License | Text |
|---|---|---|
| Upstream opencode code (the vast majority) | MIT | LICENSE |
| DAG workflow engine (fork-authored) | AGPL-3.0-or-later | packages/core/src/dag/LICENSE, packages/opencode/src/dag/LICENSE |
Exact file boundaries are listed in NOTICE. The AGPL covers the DAG engine and derivatives of it, including network-server deployments. If you don't touch the DAG engine, the rest of the repository is plain MIT.
- Saved workflow authoring guide — the
create-dag-workflowskill body .opencode/workflows/change-review.yaml— a working saved workflow, startable aschange-reviewdocs/harness-dag.md— deep-mode admission & review lifecycle.opencode/dag-prompts— built-in node prompt templatesAGENTS.md— contribution & development guide
{ // Critical nodes: required: true, plus review/arbiter workers. "model": { "advanced": "anthropic/claude-sonnet-4-5", "standard": "anthropic/claude-haiku-4-5" }, // Reasoning variant for DAG child sessions, when the model defines one. "thinking_depth": "medium" }