Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
2ba4a34
refactor(subagents): separate provider instances from drivers
Waishnav Oct 3, 2026
55cd9ae
docs(subagents): document provider instances
Waishnav Oct 3, 2026
b246d4e
fix(deps): pin koffi for package installs
Waishnav Oct 3, 2026
6944735
fix(subagents): keep profile instructions when a profile shares its p…
Waishnav Oct 25, 2026
3a15397
build(opencode): add v2 client
Waishnav Oct 3, 2026
a84c48c
refactor(opencode): add runtime generation foundation
Waishnav Oct 3, 2026
11aff46
feat(opencode): support v2 runtime
Waishnav Oct 3, 2026
33b0f32
docs(opencode): document runtime version routing
Waishnav Oct 3, 2026
be77f88
fix(opencode): probe the executable version on every runtime creation
Waishnav Oct 25, 2026
28679a7
fix(opencode): report a failed v2 turn instead of a missing assistant…
Waishnav Oct 25, 2026
3df63b0
feat(subagents): add capabilities and turn cancellation
Waishnav Oct 3, 2026
b0b2d3a
feat(subagents): expose agent stop command
Waishnav Oct 3, 2026
2889608
fix(subagents): make stop bounded and never start work after a stop
Waishnav Oct 25, 2026
bafce8b
refactor(subagents): make driver runtime policy declarative
Waishnav Oct 3, 2026
801d0ae
test(subagents): assert runtime independence without counting runtimes
Waishnav Oct 25, 2026
32392d3
feat(pi): add rpc transport
Waishnav Oct 3, 2026
9e75976
refactor(subagents): normalize ACP provider identity
Waishnav Oct 3, 2026
5bc8262
refactor(pi): run agent through native rpc cli
Waishnav Oct 3, 2026
143eb3a
refactor(acp): support generic provider instances
Waishnav Oct 3, 2026
8ae91bd
docs(pi): describe native rpc sessions
Waishnav Oct 3, 2026
eb7dadf
docs(acp): document generic driver configuration
Waishnav Oct 3, 2026
dc8be67
fix(pi): isolate cancelled turns and harden the RPC process
Waishnav Oct 25, 2026
7e558b9
fix(acp): keep flavor for named ACP instances and align preflight wit…
Waishnav Oct 25, 2026
254fa3d
build(pi): require a Pi release that emits agent_settled
Waishnav Oct 25, 2026
175fce5
test(acp): describe cancel tests with flavor options
Waishnav Oct 25, 2026
d9fd25b
fix(pi): check availability against the executable the runtime launches
Waishnav Oct 25, 2026
c7ce8e2
feat(subagents): record normalized turn events with app-owned turn ids
Waishnav Oct 25, 2026
0176abd
feat(subagents): expose turn events through the daemon and CLI
Waishnav Oct 25, 2026
d3ad695
feat(subagents): stream Claude and Pi tool and message activity
Waishnav Oct 25, 2026
768cc3d
feat(subagents): stream Codex and ACP tool and message activity
Waishnav Oct 25, 2026
c949935
test: expect the turn-events migration
Waishnav Oct 25, 2026
d3734ee
fix(subagents): address event review findings
Waishnav Oct 25, 2026
f5a3fa8
test(subagents): expect turn ids on wait results
Waishnav Oct 25, 2026
9da5e0c
feat: add programmable JavaScript workflows for subagents
Bhavik-ag Sep 16, 2026
460ded6
fix: install and validate QuickJS workflow runtime
Bhavik-ag Sep 16, 2026
f758f51
fix: address workflow review feedback
Bhavik-ag Sep 16, 2026
700515e
refactor(workflows): route agent cancellation through the subagent ma…
Waishnav Oct 25, 2026
018a799
feat(workflows): hide workflows behind DEVSPACE_EXPERIMENTAL_WORKFLOWS
Waishnav Oct 25, 2026
9e24113
fix(workflows): canonicalize managed worktree paths with native realpath
Waishnav Oct 25, 2026
ac515e7
fix(workflows): address workflow calls by the public turn id
Waishnav Oct 25, 2026
7ad314e
fix(workflows): linear meta prefix regex and allow union schema types
Waishnav Oct 25, 2026
09ccb7c
fix(daemon): share one shutdown budget across close phases
Waishnav Oct 25, 2026
6f4037a
fix(store): redraw generated agent ids that collide instead of reusin…
Waishnav Oct 25, 2026
9e46fe3
fix(workflows): report workspace authorization failures as workflow e…
Waishnav Oct 25, 2026
d515275
fix(workflows): keep the result when the runner child exits after sen…
Waishnav Oct 25, 2026
01e5af0
fix(cli): resolve managed workflow worktrees in agents stop and events
Waishnav Oct 25, 2026
7ce7794
fix(workflows): hash only Git-visible files for the resume context
Waishnav Oct 25, 2026
2eb50b7
fix(subagents): apply a continuation profile only for its provider in…
Waishnav Oct 26, 2026
3991199
Merge branch 'refactor/subagent-provider-instances' into feat/opencod…
Waishnav Oct 26, 2026
c12954f
Merge branch 'feat/opencode-v1-v2' into refactor/subagent-driver-regi…
Waishnav Oct 26, 2026
30dd94a
Merge branch 'refactor/subagent-driver-registry' into feat/subagent-c…
Waishnav Oct 26, 2026
c2cce4f
Merge branch 'feat/subagent-capabilities-cancel' into refactor/generi…
Waishnav Oct 26, 2026
18cd1c9
Merge branch 'refactor/generic-acp' into refactor/pi-rpc
Waishnav Oct 26, 2026
17825f0
Merge branch 'refactor/pi-rpc' into feat/subagent-events
Waishnav Oct 26, 2026
b5ea40d
Merge branch 'feat/subagent-events' into feat/workflows-gated
Waishnav Oct 26, 2026
a6b66ff
fix(opencode): ignore stale assistant messages and drain probe output
Waishnav Oct 26, 2026
a2af679
Merge remote-tracking branch 'origin/feat/opencode-v1-v2' into feat/s…
Waishnav Oct 26, 2026
ddbcad7
fix(acp): scope ACP runtimes to one agent
Waishnav Oct 26, 2026
a0767cd
fix(opencode): report stopped only after OpenCode confirms the interrupt
Waishnav Oct 26, 2026
b9c9736
Merge remote-tracking branch 'origin/feat/opencode-v1-v2' into refact…
Waishnav Oct 26, 2026
6d901c2
Merge remote-tracking branch 'origin/refactor/subagent-driver-registr…
Waishnav Oct 26, 2026
9e21569
Merge remote-tracking branch 'origin/feat/subagent-capabilities-cance…
Waishnav Oct 26, 2026
06faf8f
fix(acp): reject config.args for built-in ACP flavors
Waishnav Oct 26, 2026
a4770bd
fix(acp): run generic ACP providers only with full_access
Waishnav Oct 26, 2026
f7bcefd
Merge remote-tracking branch 'origin/refactor/generic-acp' into refac…
Waishnav Oct 26, 2026
2dfb313
Merge remote-tracking branch 'origin/refactor/pi-rpc' into feat/subag…
Waishnav Oct 26, 2026
bda555e
Merge remote-tracking branch 'origin/feat/subagent-events' into feat/…
Waishnav Oct 26, 2026
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
73 changes: 51 additions & 22 deletions docs/agent-profile-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ model can choose the right profile.

### `provider`

Required built-in provider id:
Required provider instance id. The default instance ids are:

```yaml
provider: codex
Expand All @@ -76,22 +76,29 @@ provider: copilot
provider: grok
```

Unsupported or custom providers are rejected. DevSpace maps providers to their
native integration:
Profiles may also reference named provider instances configured under
`subagents.providers`, such as `provider: codex-work`. Provider instances map
to one of these built-in driver integrations:

- `codex`: the host-installed `codex app-server` command
- `claude`: Claude Code SDK
- `opencode`: OpenCode SDK
- `pi`: the installed Pi coding-agent SDK, one in-process session per DevSpace agent
- `cursor`: ACP
- `copilot`: ACP
- `grok`: Grok Build ACP (`grok agent stdio`)

Codex is resolved from the user's environment rather than bundled with
DevSpace. Run `codex login` normally before using it; set `CODEX_COMMAND` when
the executable is not on the normal PATH. OpenCode, Cursor, and Copilot
runtimes are started and reused by the daemon internally, while Pi is embedded
through its Node SDK.
- `opencode`: the host-installed OpenCode CLI; DevSpace probes its major version
and uses the matching 1.x or 2.x protocol internally
- `pi`: Pi's normal `--mode rpc` CLI process. DevSpace preserves Pi's native
auth, settings, extensions, skills, context files, and session store.
- `acp`: generic Agent Client Protocol integration. Cursor, Copilot, and Grok
are default provider instances using this driver; other ACP agents can be
configured without adding a DevSpace driver.

Codex and OpenCode are resolved from the user's environment rather than bundled
as executables. Run their normal login/setup flows before using them; set
`CODEX_COMMAND` when Codex is not on the normal PATH. OpenCode is discovered as
`opencode`, and DevSpace automatically routes OpenCode 1.x and 2.x to their
matching runtime implementations. OpenCode, Cursor, and Copilot runtimes are
started and reused by the daemon internally. Pi is launched in RPC mode and
uses its native persisted session file as DevSpace's continuation identity;
`PI_COMMAND` selects another Pi executable (otherwise `pi` on `PATH`, then the
bundled package). Pi 0.80.5 or newer is required for the `agent_settled` event.

### `model`

Expand All @@ -118,10 +125,10 @@ DevSpace passes this through to providers that expose a matching control:

- `claude`: SDK effort with adaptive thinking.
- `codex`: app-server model reasoning effort.
- `pi`: the AgentSession thinking-level control.
- `pi`: RPC `set_thinking_level`.
- `opencode`: model variant.
- `cursor` and `copilot`: ACP thought-level config when supported.
- `grok`: `--reasoning-effort` on startup and xAI's ACP model metadata for resumed sessions.
- ACP: standard model/thought-level config when advertised by the agent. The
Grok flavor also understands xAI's typed model metadata and startup effort.

### `disabled`

Expand Down Expand Up @@ -154,6 +161,7 @@ devspace agents run <profile-or-provider> "<prompt>"
devspace agents continue <id> "<prompt>"
devspace agents show <id>
devspace agents wait <id>...
devspace agents events <id> [--follow]
```

The commands do not add a document-level wrapper. `targets`, `ls`, and `wait`
Expand All @@ -163,9 +171,9 @@ model-facing result small:
```xml
<provider name="codex" model="gpt-5.4" effort="high"/>
<profile name="reviewer" provider="codex">Read-only code review.</profile>
<agent id="agt_123" status="running"/>
<agent id="agt_123" status="completed">Review complete.</agent>
<agent id="agt_456" status="failed" code="PROVIDER_EXECUTION_ERROR" retryable="true">Provider disconnected.</agent>
<agent id="agt_123" status="running" turn="trn_9f2c1a7b"/>
<agent id="agt_123" status="completed" turn="trn_9f2c1a7b">Review complete.</agent>
<agent id="agt_456" status="failed" turn="trn_0d41e6c2" code="PROVIDER_EXECUTION_ERROR" retryable="false">Claude agent turn failed.<cause>Claude returned an error result: usage limit reached</cause></agent>
<error code="AGENT_NOT_FOUND" retryable="false" agent-id="agt_missing">Subagent not found.</error>
```

Expand All @@ -175,9 +183,30 @@ agents finish. With `--timeout <seconds>`, it returns each unique agent in
first-seen order and marks unfinished work with `status="running"
wait="timeout"`.

Every turn has a DevSpace-owned `turn` id. A failed turn includes the
provider's own explanation in `<cause>`, redacted and truncated.

`events` prints the current turn's activity as normalized fragments, and
`--follow` keeps printing until the turn ends. `--turn <turn-id>` selects an
earlier turn:

```xml
<event seq="1" type="turn_started" turn="trn_9f2c1a7b" at="2026-10-26T10:00:00.000Z"/>
<event seq="2" type="tool_started" turn="trn_9f2c1a7b" tool="Bash">git status --short</event>
<event seq="3" type="tool_completed" turn="trn_9f2c1a7b" tool="Bash" status="completed">git status --short</event>
<event seq="4" type="message" turn="trn_9f2c1a7b">The tree is clean.</event>
<event seq="5" type="turn_completed" turn="trn_9f2c1a7b" status="completed" at="2026-10-26T10:00:09.000Z"/>
```

Providers that cannot stream (currently OpenCode) report only `turn_started`
and `turn_completed`. The daemon keeps at most 500 events and 256 KiB per turn,
keeps events for the 20 most recent turns of each agent, truncates messages and
tool summaries, and redacts common credential shapes. Tool summaries carry the
identifying argument (a command, path, or query), never the full input or output.

`--json` remains available for scripts that need it, but the bundled skill does
not request it. Internal turn records, prompts, provider session IDs, workspace
paths, and timestamps are absent from both output formats.
not request it. Prompts, provider session IDs, and workspace paths are absent
from both output formats.

`open_workspace` exposes compact profile metadata:

Expand Down
60 changes: 54 additions & 6 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,27 @@ Subagent providers are explicit. Omitted providers are disabled:
}
```

`id` is the provider instance id used by profiles and `devspace agents run`.
For the default built-in instance ids, `driver` is optional and defaults to the
same value. Named instances declare the driver explicitly, which allows
multiple independently configured instances of one integration:

```jsonc
{
"id": "codex-work",
"driver": "codex",
"enabled": true,
"env": {
"CODEX_HOME": "/home/alice/.codex-work",
},
}
```

Profiles can then select that instance with `provider: codex-work`. The driver
is the implementation (`codex`, `claude`, `opencode`, `pi`, or `acp`);
provider-native session ids remain internal to DevSpace. Cursor, Copilot, and
Grok are default ACP provider instances rather than separate drivers.

`subagents.instructions` controls when ChatGPT receives the managed workflow:

| Value | Behavior |
Expand All @@ -171,15 +192,42 @@ does not expand `$NAME` references in these values.
All subagent providers accept `env`. The daemon inherits its startup
environment, then overlays the provider's `env` without mutating the daemon's
process environment. OpenCode receives that environment on its managed server
process; embedded Pi scopes it to its provider requests and command execution.
process; Pi receives it on its managed RPC process.

The `opencode` driver runs `opencode --version` before creating its first
runtime and automatically selects the OpenCode 1.x or 2.x protocol. Both major
versions use the same provider instance and profile configuration; `opencode2`
is not a separate driver.

Codex, Claude, Pi, and ACP instances also accept `command`. OpenCode does not
expose a command override. Cursor, Copilot, and Grok retain their built-in ACP
commands when `command` is omitted. An arbitrary ACP instance declares its
executable and may provide fixed startup arguments under `config.args`:

```jsonc
{
"id": "kiro",
"driver": "acp",
"enabled": true,
"command": "kiro",
"config": { "args": ["acp"] }
}
```

`config.flavor` is reserved for the small built-in protocol quirks currently
needed by `cursor`, `copilot`, and `grok`; generic ACP agents normally omit it.
Those built-in flavors derive their sandbox arguments from the write mode, so
they reject `config.args`. DevSpace cannot enforce `read_only` or `allowed` on an
arbitrary ACP agent, so generic ACP instances run only with `full_access`.

Codex, Claude, Cursor, Copilot, and Grok also accept `command`. OpenCode and Pi
do not expose a command override. For providers that support it, an explicit
`command` wins over both the inherited command override and a command override
placed in `env`.
Pi runs as the normal CLI in `--mode rpc`, so user-installed extensions,
skills, context files, auth, models, and settings load normally. Restricted
DevSpace modes inject a sandbox extension and pin Pi's active built-in tools to
the matching read-only/workspace-safe set; `full_access` leaves Pi's normal
tool configuration untouched.

Existing process-level overrides remain supported: `CODEX_COMMAND`,
`CODEX_HOME`, `CLAUDE_COMMAND`, `CURSOR_COMMAND`, `COPILOT_COMMAND`,
`CODEX_HOME`, `CLAUDE_COMMAND`, `PI_COMMAND`, `CURSOR_COMMAND`, `COPILOT_COMMAND`,
`GROK_COMMAND`, and `GROK_AGENT_PROFILE`. Provider configuration takes
precedence where the same value is set in both places.

Expand Down
5 changes: 5 additions & 0 deletions docs/local-agent-daemon.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ functionality, but `devspace serve` is not required for local-agent execution.
The daemon is scoped to one DevSpace `stateDir`, so one SQLite store and one
runtime owner serve all clients using that configuration.

Experimental persisted workflows (`DEVSPACE_EXPERIMENTAL_WORKFLOWS=1`) use the same daemon and local agent manager. Active
workflows keep it alive after the submitting CLI exits. Startup reconciles
agent turns before workflow records; shutdown closes workflow supervisors
before the agent manager.

Communication uses a private Unix domain socket on Linux/macOS or a named pipe
on Windows. The endpoint is not exposed through the public MCP HTTP port.
Provider session identifiers and logical agent records are durable; live
Expand Down
64 changes: 64 additions & 0 deletions docs/workflows.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Workflows

> **Experimental and hidden.** Workflows ship disabled in v1.1. Set `DEVSPACE_EXPERIMENTAL_WORKFLOWS=1` in the environment of both the CLI and the daemon (run `devspace agents daemon stop` first if a daemon is already running) to enable the `devspace workflow` commands and the managed `workflows` skill. There is no config-file option and no MCP tool for workflows yet.

DevSpace workflows run persisted multi-agent plans through the same on-demand daemon as local subagents. A CLI process submits a run and exits immediately; the daemon remains alive until active workflows finish.

Place reusable workflow scripts in `.devspace/workflows/` and run them by name, or provide a file directly:

```js
export const meta = { name: "review", concurrency: 2 };

const findings = await agent("Review the current changes", {
target: "reviewer",
writeMode: "read_only",
});
return { findings };
```

The script body receives `args` plus the `agent`, `workflow`, `parallel`, `pipeline`, `phase`, and `log` helpers. It must return a JSON value. `agent(prompt, options)` requires a configured profile or provider in `options.target`; `workflow(name, args)` may call one named workflow, with nesting limited to one level.

`parallel(items, worker, concurrency)` returns one outcome per input instead of failing the whole batch when one worker throws. `pipeline(items, ...stages)` passes each raw item through every stage in order; each stage receives `(currentValue, originalItem, index)`. `phase(name, run)` records durable start, completion, and failure events and gives `run` an agent helper that labels its calls with that phase.

Run the script by name or provide a file directly:

```bash
devspace workflow run --name review --args '{"base":"main"}'
devspace workflow run --file ./review.workflow.js --args-file ./inputs.json
```

Exactly one of `--file`, `--name`, or `--resume` is required. `--file` reads the source before contacting the daemon. Named workflows are resolved inside the current workspace. Runs default to `read_only`; `--write-mode allowed` permits workflow calls configured for writes.

`run` returns an asynchronous receipt. Use the returned workflow ID with:

```bash
devspace workflow status <id>
devspace workflow wait <id> --timeout 60
devspace workflow calls <id>
devspace workflow call <id> <index>
devspace workflow events <id> --after <last-sequence> --json
devspace workflow cancel <id>
devspace workflow ls
```

`wait` accepts a timeout from 0 to 60 seconds and returns the latest durable run state on timeout. `calls` returns compact call summaries without prompts, results, or fingerprints. `call` returns the full stored record for one zero-based call index. `events` returns up to 100 persisted log and phase entries after the supplied sequence. All commands accept `--json` for scripts.

Workflow records, calls, and provider continuation state survive CLI exits and daemon restarts. A daemon restart marks unfinished workflows interrupted for explicit inspection or resume; it does not dispatch calls automatically. At startup the daemon first reconciles local agent turns, then workflow runs. During shutdown it closes workflows before the local agent manager so supervisors can stop their child turns cleanly. Both phases share one shutdown budget, and workflows get at most half of it so the agent manager always gets time to close.

Named workflows and their arguments are workspace scoped. IDs from another checkout, workspace, or managed worktree are rejected even when the caller can read the shared DevSpace state directory.

## Bounds

Workflow metadata may set `concurrency` from 1 to 16; the default is 4. The daemon admits at most eight active workflows and eight aggregate workflow agent turns, with at most sixteen script runners including nested scripts. One run may make at most 128 agent turns, including schema-correction turns, and may invoke at most eight nested workflows. Nesting is limited to one level and shares the parent run's admission, cancellation, and call budgets.

Scripts run in QuickJS with a 32 MiB heap, 512 KiB stack, and 15-minute wall clock limit. Source is limited to 64 KiB, arguments to 128 KiB, and workflow or agent results to 256 KiB. Guest `log()` and `phase()` events share a 64 KiB limit per run; bounded internal events such as agent turns and nested workflow source snapshots are additional. These are hard product boundaries rather than tuning controls.

An agent call may include an inline JSON Schema in `options.schema`. DevSpace compiles strict draft-07 structural schemas with at most 16 KiB, 512 nodes, and 16 levels. It rejects `$ref`, `$dynamicRef`, `$recursiveRef`, `$async`, `pattern`, `patternProperties`, and `format`. Invalid output gets one read-only correction in the same agent session; the correction counts toward the 128-turn budget.

## Resume and worktrees

Resume reuses only a matching prefix of completed read-only calls. Any call with write authority, isolation, or a named workflow workspace disables replay for the run. DevSpace also hashes the workspace context before the original and resumed runs: `HEAD`, the staged diff, and every file Git tracks or would add (`git ls-files --cached --others --exclude-standard`). Ignored files such as `node_modules` or build output are not part of the context, so changing them does not block resume. More than 20,000 entries, more than 64 MiB of files, any symlink, special file, unreadable path, or other hash failure produces `RECOVERY_CONTEXT_CHANGED`.

An isolated call creates a managed worktree and records it as a workspace. DevSpace retains those worktrees after the run for inspection. Normal stale-worktree cleanup preserves recovery snapshots for commits and dirty tracked changes and skips worktrees with untracked files.

Profiles are resolved in each call’s execution workspace. For isolated worktrees, use global profiles or commit workspace profiles so they exist in the new worktree. A new worktree starts at the source’s committed `HEAD`; uncommitted source changes are not copied.
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,22 +52,25 @@
"@anthropic-ai/claude-agent-sdk": "0.3.200",
"@anthropic-ai/sandbox-runtime": "0.0.71",
"@clack/prompts": "^1.5.1",
"@earendil-works/pi-coding-agent": "^0.80.3",
"@earendil-works/pi-coding-agent": "^0.80.10",
"@modelcontextprotocol/ext-apps": "^1.7.2",
"@modelcontextprotocol/node": "^2.0.0",
"@modelcontextprotocol/sdk": "^1.29.0",
"@modelcontextprotocol/server": "^2.0.0",
"@opencode-ai/sdk": "1.17.13",
"@opencode/client": "2.0.18",
"@pierre/diffs": "^1.3.6",
"ajv": "^8.20.0",
"better-result": "^2.10.0",
"better-sqlite3": "^12.10.0",
"cross-spawn": "^7.0.6",
"diff": "^8.0.3",
"drizzle-orm": "^0.45.2",
"express": "^5.2.1",
"jsonc-parser": "^3.3.1",
"koffi": "^3.1.2",
"koffi": "3.2.1",
"lucide": "^1.24.0",
"quickjs-emscripten": "^0.32.0",
"react": "^19.2.6",
"react-dom": "^19.2.6",
"semver": "^7.8.4",
Expand Down
Loading
Loading