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
72 changes: 72 additions & 0 deletions .opencode/workflows/change-review.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
title: Change review
config:
name: change-review
max_concurrency: 3
node_defaults:
required: false
report_to_parent: false
worker_config:
timeout_ms: 900000
nodes:
- id: survey
name: survey
worker_type: explore
depends_on: []
required: true
prompt_template:
id: code-explore
input:
target: "the uncommitted changes in this repository (git status, git diff) and the modules they touch"

- id: review-logic
name: review-logic
worker_type: general
depends_on: [survey]
prompt_template: { id: review-logic }

- id: review-arch
name: review-arch
worker_type: general
depends_on: [survey]
prompt_template: { id: review-arch }

- id: verify
name: verify
worker_type: build
depends_on: [survey]
prompt_template:
inline: |
Run the repository's commit gates against the changed packages and report the raw results.
From each affected package directory (never the repo root): `bun typecheck`, then `bun test` for the
test files covering the changed code. Report failures verbatim; do not fix anything.

- id: arbitrate
name: arbitrate
worker_type: general
depends_on: [review-logic, review-arch, verify]
required: true
report_to_parent: true
output_schema:
type: object
required: [verdict, summary, findings]
properties:
verdict:
type: string
enum: [ACCEPT, REVISE, REJECT, BLOCKED]
summary: { type: string }
findings:
type: array
items:
type: object
required: [severity, location, problem]
properties:
severity:
type: string
enum: [blocker, major, minor, nit]
location: { type: string }
problem: { type: string }
prompt_template:
inline: |
Two reviewers and one verification run examined the same change. Submit a single deduplicated
verdict. A failing gate in {{verify}} is a blocker regardless of review opinion. Drop any reviewer
claim you cannot tie to a specific file and line.
112 changes: 107 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@
<a href="./README.zh.md">简体中文</a>
</p>

# OpenCode-GraphAgent
# GraphAgent

> A fork of [opencode](https://github.com/anomalyco/opencode) that adds a DAG workflow engine: the coding agent decomposes a task into a dependency graph of child agents and drives it to completion. State is durable, crashes are recoverable, and the whole thing can be inspected and controlled from the terminal.
> 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.

Built on top of the MIT-licensed [opencode](https://github.com/anomalyco/opencode) terminal AI agent. **Not affiliated with or endorsed by the OpenCode team.**
GraphAgent is the product name for this project; the repository is published as
**OpenCode-GraphAgent**, a fork of the MIT-licensed
[opencode](https://github.com/anomalyco/opencode) terminal AI agent that adds
the DAG workflow engine. **Not affiliated with or endorsed by the OpenCode
team.**

---

Expand All @@ -20,6 +24,91 @@ A single agent loop struggles once a task has staged dependencies, parallelizabl
3. **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.
4. **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.

## Using workflows

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.

### 1. Choose the model tiers — `.opencode/dag.jsonc`

Nodes never name their own model. The graph declares *which nodes are critical*
and configuration decides *what runs them*:

```jsonc
{
// 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"
}
```

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.

### 2. Save the workflows you rerun — `.opencode/workflows/`

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:

```yaml
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.

### 3. Let the agent author it

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.

## DAG workflow engine

The engine lives in [`packages/core/src/dag`](./packages/core/src/dag) (state machine, dependency graph, scheduling, event projection, SQLite read model) and [`packages/opencode/src/dag`](./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.
Expand Down Expand Up @@ -77,9 +166,20 @@ Workflow-level knobs: `max_concurrency` (default 5), `max_node_replan_attempts`
POST /dag/:dagID/control pause/resume/cancel/replan/extend/step/complete
```

### Configuration
### Project configuration files

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) |

`dag.jsonc` (project `.opencode/` overrides global config dir; a commented default is seeded on first use) sets two model tiers: `advanced` for critical nodes (`required: true`, review workers), `standard` for everything else, plus a `thinking_depth` reasoning variant for child sessions. Everything else inherits the main opencode configuration.
Both `dag.jsonc` and the workflow library are read lazily, so an edit applies to
the next workflow start without a restart.

---

Expand Down Expand Up @@ -133,6 +233,8 @@ Exact file boundaries are listed in [`NOTICE`](./NOTICE). The AGPL covers the DA

## Docs

- [Saved workflow authoring guide](./packages/core/src/plugin/skill/create-dag-workflow.md) — the `create-dag-workflow` skill body
- [`.opencode/workflows/change-review.yaml`](./.opencode/workflows/change-review.yaml) — a working saved workflow, startable as `change-review`
- [`docs/harness-dag.md`](./docs/harness-dag.md) — deep-mode admission & review lifecycle
- [`.opencode/dag-prompts`](./.opencode/dag-prompts) — built-in node prompt templates
- [`AGENTS.md`](./AGENTS.md) — contribution & development guide
Expand Down
86 changes: 81 additions & 5 deletions README.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
<a href="./README.zh.md"><b>简体中文</b></a>
</p>

# OpenCode-GraphAgent
# GraphAgent

> [opencode](https://github.com/anomalyco/opencode) 的 fork,加了一个 DAG 工作流引擎:编码智能体把任务拆成一张子智能体依赖图,然后驱动它跑完。状态持久化,崩溃能恢复,在终端里就能看图、控图
> 一个把任务拆成子智能体依赖图、并驱动它跑完的编码智能体。状态持久化,崩溃能恢复,整张图在终端里就能看、能控

基于 MIT 许可的 [opencode](https://github.com/anomalyco/opencode) 终端 AI 智能体构建。**与 OpenCode 团队无任何隶属或背书关系。**
GraphAgent 是本项目对外的产品名;仓库以 **OpenCode-GraphAgent** 发布,是 MIT 许可的
[opencode](https://github.com/anomalyco/opencode) 终端 AI 智能体的 fork,在其之上加了 DAG 工作流引擎。**与 OpenCode 团队无任何隶属或背书关系。**

---

Expand All @@ -20,6 +21,71 @@
3. **门禁结论必须有下文。** 检查点返回 `REVISE` / `REJECT` / `BLOCKED` 时,父智能体要在同一个唤醒回合里处置它:extend、replan、开新工作流,或者说明理由后停下。只复述结论就结束回合,按契约算编排失败。
4. **恢复靠证据,不靠猜。** 所有状态变更都是持久化事件,状态转换要过声明式状态机的守卫,终态不可逆(只有一个写进规范的例外),读模型是 CQRS 投影。崩溃后只依据持久化证据和解现场,不会凭空重放模型调用。

## 工作流怎么用

不配置也能直接试:给它一件有阶段、有可并行部分、或者中间需要一道审查门禁的活,智能体自己会建图并跑起来。想把它变成你自己的一套固定流程,有三件事:

### 1. 选定模型分层 —— `.opencode/dag.jsonc`

节点从不指定自己的模型。图只声明*哪些节点是关键的*,由配置决定*用什么跑*:

```jsonc
{
// 关键节点:required: true,以及审查/仲裁类 worker。
"model": {
"advanced": "anthropic/claude-sonnet-4-5",
"standard": "anthropic/claude-haiku-4-5"
},
// DAG 子会话的推理深度变体,仅当模型定义了同名变体时生效。
"thinking_depth": "medium"
}
```

项目的 `.opencode/dag.jsonc` 优先于 opencode 配置目录下的全局文件;首次使用时会在全局目录生成一份带注释的默认文件。只配一层时,它就是统一默认值。每个节点的解析顺序是:分层 → worker agent 模型 → 父会话模型;三者都给不出模型时,工作流不会被创建,而是回来问你去配一个。

### 2. 把要复用的工作流存起来 —— `.opencode/workflows/`

工作流 spec 就是一个描述图的 YAML 文件。把它放进工作流目录,它就有了**名字**:

| 作用域 | 路径 | 可用范围 |
|---|---|---|
| 项目级 | `.opencode/workflows/<name>.yaml` | 本仓库,随仓库提交 —— 团队拿到的是同一套流程 |
| 全局级 | `<opencode 配置目录>/workflows/<name>.yaml` | 本机所有项目 |

文件名(去掉扩展名)就是名字,同名时项目级遮蔽全局级。一个最小的 spec:

```yaml
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."
```

之后直接说「跑 dependency-audit 工作流」就行 —— 智能体按名字启动,不需要路径。临时 spec 的用法完全不变:看起来像路径的 `spec_path`(含路径分隔符,或带 `.yaml`/`.yml` 扩展名)仍按会话目录相对解析,不走工作流库。

### 3. 让智能体替你写

内置的 **`create-dag-workflow`** skill 覆盖 spec 编写:两个作用域、文件结构、存盘 spec 必须守的规矩(不能钉死模型、`worker_type` 必须存在、模板必需变量必须给全),以及怎么验证。说一句「把这个存成可复用的工作流」,智能体会先跟你确认阶段和门禁,把文件写进你选的作用域,再真跑一次证明它能用。

节点 prompt 来自 `.opencode/dag-prompts/*.md` —— 随仓库附带 12 个,通过 `prompt_template.id` 引用。往那儿加一个 `.md` 就多一个模板;全局工作流建议用 `inline` prompt,否则会依赖某个仓库本地的模板。

## DAG 工作流引擎

引擎位于 [`packages/core/src/dag`](./packages/core/src/dag)(状态机、依赖图、调度、事件投影、SQLite 读模型)和 [`packages/opencode/src/dag`](./packages/opencode/src/dag)(工作流服务、执行循环、节点生成、准入、审查生命周期、崩溃恢复、模板)。智能体通过单个 `workflow` 工具驱动它;人通过 TUI 或 HTTP API 观察和控制它。
Expand Down Expand Up @@ -77,9 +143,17 @@
POST /dag/:dagID/control pause/resume/cancel/replan/extend/step/complete
```

### 配置
### 项目配置文件

DAG 相关的东西都放在 `.opencode/` 下,在 opencode 配置目录(`OPENCODE_CONFIG_DIR`,否则 `~/.config/opencode`)里有对应的全局版本。其余全部继承 opencode 主配置。

| 路径 | 用途 | 全局对应 |
|---|---|---|
| `.opencode/dag.jsonc` | 模型分层(`advanced` / `standard`)与子会话 `thinking_depth` | `<配置目录>/dag.jsonc`,首次使用时生成带注释的默认文件 |
| `.opencode/workflows/*.yaml` | 存盘的工作流 spec,可按名字启动 | `<配置目录>/workflows/*.yaml` |
| `.opencode/dag-prompts/*.md` | 由 `prompt_template.id` 引用的节点 prompt 模板 | ——(仅项目级) |

`dag.jsonc`(项目 `.opencode/` 优先于全局配置目录,首次使用时自动生成带注释的默认文件)里设置两个模型层:`advanced` 给关键节点(`required: true` 和审查类 worker),`standard` 给其余节点,另外还有子会话的 `thinking_depth` 推理深度。其余全部继承 opencode 主配置
`dag.jsonc` 和工作流库都是惰性读取,改完下一次启动工作流就生效,不用重启

---

Expand Down Expand Up @@ -133,6 +207,8 @@ bun dev serve # headless API 服务(端口 4096)

## 文档

- [存盘工作流编写指南](./packages/core/src/plugin/skill/create-dag-workflow.md) —— `create-dag-workflow` skill 正文
- [`.opencode/workflows/change-review.yaml`](./.opencode/workflows/change-review.yaml) —— 一个能用的存盘工作流,按 `change-review` 启动
- [`docs/harness-dag.md`](./docs/harness-dag.md) —— deep 模式准入与审查生命周期
- [`.opencode/dag-prompts`](./.opencode/dag-prompts) —— 内置节点 prompt 模板
- [`AGENTS.md`](./AGENTS.md) —— 贡献与开发指南
Expand Down
29 changes: 27 additions & 2 deletions packages/core/src/plugin/command/workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,26 @@ config:
nodes: []
```

## Saved workflows

A `spec_path` with no path separator and no `.yaml`/`.yml` extension is a
**name** resolved against the workflow library instead of the filesystem:

1. `.opencode/workflows/<name>.yaml` — project scope, committed with the repo
2. `<opencode config dir>/workflows/<name>.yaml` — global scope, available in every project

The project scope wins when both hold the name. `workflow(action: "list")`
reports the saved names with their scope, title, and node count; a name that
resolves nowhere fails with the directories that were searched.

Prefer a saved workflow when the user names a recurring procedure ("run the
code review workflow"): starting it is one call, and its graph has already been
reviewed. Compose a fresh spec file when the task is one-off or when the saved
graph does not fit — a path-shaped `spec_path` keeps the original
session-relative behavior. To turn a working one-off spec into a saved
workflow, move the file into one of the two directories under a descriptive
name.

## Orchestration Lifecycle

Heavy tasks follow a meta-workflow: multiple workflows chained together, each producing a decision that shapes the next. The lifecycle is the two accuracy axes applied in sequence — breadth to cover the surface, depth to earn the verdict:
Expand Down Expand Up @@ -453,10 +473,15 @@ All nodes share the same workspace. Write conflicts are an orchestration concern

**start** — Create a workflow from a YAML spec with `config` and optional
`title`, `mode`, and admission input at the file root. Write the file first,
then call `{ action: "start", spec_path: ".opencode/workflows/name.yaml" }`.
then call `{ action: "start", spec_path: ".opencode/workflows/name.yaml" }`, or
pass a saved workflow name (`{ action: "start", spec_path: "code-review" }`).
Returns the workflow ID. Nodes declare `depends_on` (node IDs); layers and
execution order are computed automatically.

**list** — Show the saved workflow specs in the library (project and global
scope) with their names, titles, and node counts. This lists reusable specs,
not running workflows; use `status` for a workflow's live state.

**extend** — Add nodes to a running workflow. Existing nodes are unaffected;
new nodes are immediately eligible for scheduling if their dependencies are
met. It also accepts a genuinely additive wave after a reporting leaf
Expand Down Expand Up @@ -496,5 +521,5 @@ file-root `nodes` array, then call
### What NOT to expect

- No `node_complete` action — completion is automatic
- No `list` / `history` actions — inspect a known workflow with `status`; broader browsing remains TUI-only
- No `history` action — inspect a known workflow with `status`; browsing running workflows remains TUI-only (`list` shows saved specs, not running workflows)
- No topology templates — templates are prompt fragments only; you design the graph
Loading
Loading