Skip to content

feat: pool the OpenCode server runtime - #184

Open
Waishnav wants to merge 4 commits into
codex/subagent-runtime-codexfrom
codex/subagent-runtime-opencode
Open

feat: pool the OpenCode server runtime#184
Waishnav wants to merge 4 commits into
codex/subagent-runtime-codexfrom
codex/subagent-runtime-opencode

Conversation

@Waishnav

@Waishnav Waishnav commented Aug 11, 2026

Copy link
Copy Markdown
Owner

OpenCode is naturally split between a server and client, but DevSpace previously recreated that pair for each turn. This stacked change adds the reusable OpenCode runtime that keeps one server alive for multiple logical sessions, preserves provider session ids for cold continuation, and closes the server through the shared runtime lifecycle.

Summary by CodeRabbit

  • New Features
    • Added support for using OpenCode as a local agent provider.
    • OpenCode sessions now support prompt execution, model and thinking-level overrides, and write-mode-specific agent and permission settings.
    • Results include the final assistant response and structured run status.
  • Reliability Improvements
    • Improved handling of session validation, health checks, connection failures, runtime recovery, and clean shutdown.
    • Reuses active OpenCode servers where possible for more efficient session management.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds OpenCode local-agent support with shared runtime management, session execution, model and permission mappings, response parsing, health handling, provider wiring, and integration tests.

Changes

OpenCode integration

Layer / File(s) Summary
Runtime contracts and provider wiring
src/local-agent-opencode.ts, src/local-agent-adapters.ts
Defines OpenCode runtime and driver types, creates shared server runtimes, maps write modes, and selects the OpenCode driver.
Session execution and response handling
src/local-agent-opencode.ts
Adds health checks, session creation or resumption, model and thinking updates, prompt execution, message parsing, final-response extraction, and transport-failure handling.
Runtime lifecycle and session validation
src/local-agent-opencode.test.ts
Tests runtime reuse, session behavior, mappings, health-failure replacement, callbacks, and idempotent closure.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant LocalAgentDrivers
  participant OpencodeLocalAgentDriver
  participant OpencodeRuntime
  participant OpenCodeServer
  participant OpenCodeSession
  LocalAgentDrivers->>OpencodeLocalAgentDriver: select "opencode"
  OpencodeLocalAgentDriver->>OpencodeRuntime: obtain shared runtime
  OpencodeRuntime->>OpenCodeServer: check health
  OpencodeRuntime->>OpenCodeSession: create or resume session
  OpencodeRuntime->>OpenCodeSession: submit prompt and wait
  OpencodeRuntime->>OpenCodeSession: read messages
  OpencodeRuntime-->>OpencodeLocalAgentDriver: return response and session ID
Loading

Possibly related PRs

Poem

A rabbit hops through OpenCode’s gate,
Sharing one runtime while sessions wait.
Models switch and prompts take flight,
Health checks guard the tunnel tight.
Responses bloom, then servers close—
A tidy burrow for every prose.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: pooling the reusable OpenCode server runtime.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/subagent-runtime-opencode

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Waishnav
Waishnav changed the base branch from codex/subagent-runtime-owner to codex/subagent-runtime-codex August 11, 2026 14:13
@greptile-apps

greptile-apps Bot commented Aug 11, 2026

Copy link
Copy Markdown

Greptile Summary

The PR introduces a pooled OpenCode runtime that shares one server across logical sessions while preserving durable provider session IDs and integrating runtime health and shutdown behavior.

  • Registers OpenCode as a native pooled local-agent driver.
  • Creates, resumes, configures, prompts, waits for, and reads OpenCode sessions.
  • Applies model, thinking-level, agent, and write-mode permission settings before each prompt.
  • Adds health invalidation, runtime recreation, and idempotent server shutdown coverage.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
src/local-agent-opencode.ts Implements the pooled OpenCode runtime, durable-session continuation, per-turn authority settings, response extraction, health invalidation, and idempotent shutdown; the previously reported cold-continuation defect is addressed.
src/local-agent-opencode.test.ts Covers server reuse, session creation and continuation, thinking overrides, permission mapping, health-failure eviction, recreation, and single shutdown.
src/local-agent-adapters.ts Registers OpenCode with the pooled driver path while leaving other providers on their existing implementations.

Sequence Diagram

sequenceDiagram
    participant M as Local Agent Manager
    participant P as Runtime Pool
    participant R as OpenCode Runtime
    participant S as OpenCode Server
    M->>P: run(context, input)
    P->>R: reuse or create runtime
    R->>S: health.get()
    alt New session
        R->>S: session.create(model, agent, workspace)
    else Durable continuation
        R->>S: session.get(sessionId)
        R->>S: switchModel(model + thinking variant)
    end
    R->>S: switchAgent(write-mode agent)
    R->>S: prompt(sessionId, prompt)
    R->>S: wait(sessionId)
    R->>S: messages(sessionId)
    R-->>M: session ID and final response
    P->>R: close on lifecycle expiry
    R->>S: close server
Loading

Reviews (2): Last reviewed commit: "fix: enforce OpenCode authority and heal..." | Re-trigger Greptile

Comment thread src/local-agent-opencode.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3ccc712d54

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}
}

export class OpencodeLocalAgentDriver implements LocalAgentDriver {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Wire the pooled drivers into the manager

Repo-wide search only finds OpencodeLocalAgentDriver and CodexLocalAgentDriver in these new files and their tests, while the server constructs subagents through createLocalAgentDrivers() from local-agent-adapters; as a result this pooled OpenCode runtime is unreachable in production, so OpenCode runs still won't share a server runtime. Please register the new driver(s) on the production driver factory path.

Useful? React with 👍 / 👎.

Comment thread src/local-agent-opencode.ts Outdated
Comment on lines +135 to +138
return client.session.prompt({
sessionID: sessionId,
prompt: { text: input.prompt },
}, { throwOnError: true });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Send OpenCode prompts in the v2 shape

When this runtime is used with the real @opencode-ai/sdk/v2, prompts are sent with a nested prompt object, but OpenCode's own v2 client call sends top-level sessionID, model/variant, and parts for session.prompt; with this payload the user text/model override is not part of the request the SDK expects, so OpenCode turns can be rejected or run without the requested prompt. Use the v2 parts: [{ type: "text", text: input.prompt }]/top-level fields shape here instead.

Useful? React with 👍 / 👎.

@Waishnav
Waishnav force-pushed the codex/subagent-runtime-opencode branch from 3ccc712 to 2cdb266 Compare August 11, 2026 18:28
@Waishnav
Waishnav force-pushed the codex/subagent-runtime-opencode branch from 2cdb266 to 962f356 Compare August 11, 2026 19:02
@Waishnav
Waishnav force-pushed the codex/subagent-runtime-opencode branch from 962f356 to b6d06a0 Compare August 12, 2026 03:40
@Waishnav
Waishnav force-pushed the codex/subagent-runtime-opencode branch from 2353b53 to 30dd494 Compare August 12, 2026 04:20
@Waishnav
Waishnav force-pushed the codex/subagent-runtime-opencode branch from 30dd494 to 0929a57 Compare August 12, 2026 04:24
@Waishnav
Waishnav force-pushed the codex/subagent-runtime-opencode branch from 0929a57 to 6ecc540 Compare August 12, 2026 04:29
@Waishnav
Waishnav force-pushed the codex/subagent-runtime-opencode branch from 6ecc540 to 47713ab Compare August 12, 2026 04:44
@Waishnav
Waishnav force-pushed the codex/subagent-runtime-opencode branch 2 times, most recently from 738779f to b2dd430 Compare August 12, 2026 05:19
@Waishnav
Waishnav force-pushed the codex/subagent-runtime-opencode branch from b2dd430 to b0c15bf Compare August 12, 2026 05:25
@Waishnav
Waishnav force-pushed the codex/subagent-runtime-opencode branch 2 times, most recently from 526c3d2 to 8e0d52d Compare August 12, 2026 05:27
@Waishnav
Waishnav force-pushed the codex/subagent-runtime-opencode branch from 8e0d52d to d1e388a Compare August 12, 2026 05:32

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/local-agent-opencode.ts`:
- Around line 108-114: Update the three agent configurations in createOpencode
for devspace_read_only, devspace_allowed, and devspace_full_access to explicitly
set mode to "primary" and task to "deny" alongside their existing permission
values. Add tests covering these fields for all three agent configurations.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: da3b45ec-e4af-415a-b163-be4e62bbd3be

📥 Commits

Reviewing files that changed from the base of the PR and between cb9e8f1 and d1e388a.

📒 Files selected for processing (3)
  • src/local-agent-adapters.ts
  • src/local-agent-opencode.test.ts
  • src/local-agent-opencode.ts

Comment on lines +108 to +114
return createOpencode({ config: {
agent: {
devspace_read_only: { permission: opencodePermissionFor("read_only") },
devspace_allowed: { permission: opencodePermissionFor("allowed") },
devspace_full_access: { permission: opencodePermissionFor("full_access") },
},
} });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

For @opencode-ai/sdkversion 1.17.13, which agent configuration fields supportmode: "primary"andpermission.task: "deny"?

💡 Result:

In @opencode-ai/sdk version 1.17.13, the agent configuration fields mode: primary and permission.task: deny are defined within the agent's configuration object [1][2]. The mode: primary setting is a property of an agent's configuration that designates it as a primary agent, allowing it to interact directly with the user and perform main conversation tasks [1][3][4]. This is configured under the agent's key in the configuration: { "agent": { "your-agent-name": { "mode": "primary",... } } } The permission.task: deny setting is used within the permission configuration of an agent to restrict its ability to invoke subagents via the Task tool [1][3][5]. When set to deny, the specified subagent (or all subagents if using a wildcard) is removed from the Task tool's description, preventing the model from attempting to invoke it [1][4][6]. It is typically structured as follows: { "agent": { "your-agent-name": { "permission": { "task": { "": "deny", "specific-subagent-name": "allow" } } } } } These settings are part of the broader agent configuration schema defined in the SDK's types [2]. Note that OpenCode has migrated to a V2 configuration format, where agent permissions are handled via a more explicit permissions array (e.g., { "action": "task", "resource": "", "effect": "deny" }), though the SDK maintains compatibility with legacy frontmatter and configuration structures [7].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- target file ---'
cat -n src/local-agent-opencode.ts | sed -n '1,190p'

printf '%s\n' '--- dependency declarations and lock entries ---'
rg -n -C 3 '`@opencode-ai/sdk`|opencode' package.json package-lock.json pnpm-lock.yaml yarn.lock bun.lockb 2>/dev/null || true

printf '%s\n' '--- related tests and call sites ---'
rg -n -C 4 'devspace_read_only|devspace_allowed|devspace_full_access|opencodePermissionFor|defaultOpencodeFactory|permission.*task|mode:.*primary' . \
  -g '!node_modules' -g '!dist' -g '!build' || true

printf '%s\n' '--- tracked files near the target ---'
git ls-files | rg '(^|/)(test|tests|spec|__tests__)/|opencode|package.json|lock' | head -200

Repository: Waishnav/devspace

Length of output: 15258


🏁 Script executed:

#!/bin/bash
set -eu

tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT

curl -fsSL 'https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.17.13.tgz' -o "$tmpdir/sdk.tgz"
tar -xzf "$tmpdir/sdk.tgz" -C "$tmpdir"

printf '%s\n' '--- SDK package files relevant to V2 config ---'
find "$tmpdir/package" -type f | rg 'types|config|gen|index' | head -100

printf '%s\n' '--- V2 agent/config/permission declarations ---'
rg -n -C 5 'PermissionConfig|AgentConfig|permission|mode|task' "$tmpdir/package" \
  -g '*.d.ts' -g '*.ts' -g '*.js' | head -300

printf '%s\n' '--- local tests ---'
cat -n src/local-agent-opencode.test.ts | sed -n '1,155p'

printf '%s\n' '--- runtime input contract ---'
rg -n -C 5 'interface LocalAgentRunInput|type LocalAgentRunInput|workspace|writeMode|task|profile|result' src/local-agent-runtime.ts src -g '*.ts' | head -300

Repository: Waishnav/devspace

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -eu

tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
curl -fsSL 'https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.17.13.tgz' -o "$tmpdir/sdk.tgz"
tar -xzf "$tmpdir/sdk.tgz" -C "$tmpdir"

printf '%s\n' '--- legacy AgentConfig permission fields ---'
sed -n '835,880p' "$tmpdir/package/dist/gen/types.gen.d.ts"

printf '%s\n' '--- V2 AgentConfig and Config declarations ---'
rg -n 'export type (AgentConfig|Config|PermissionConfig)|interface (AgentConfig|Config|PermissionConfig)|agent\?:|permission\?:|task\?:|mode\?:' \
  "$tmpdir/package/dist/v2" -g '*.d.ts' -g '*.ts'
sed -n '1,220p' "$tmpdir/package/dist/v2/gen/types.gen.d.ts" | rg -n -C 8 'AgentConfig|Config|PermissionConfig|task|mode|permission'

printf '%s\n' '--- createOpencode config handling ---'
rg -n -C 8 'function createOpencode|createOpencode|config' "$tmpdir/package/dist/v2" -g '*.js' -g '*.d.ts' | head -180

Repository: Waishnav/devspace

Length of output: 23268


🏁 Script executed:

#!/bin/bash
set -eu

tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
curl -fsSL 'https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.17.13.tgz' -o "$tmpdir/sdk.tgz"
tar -xzf "$tmpdir/sdk.tgz" -C "$tmpdir"
types="$tmpdir/package/dist/v2/gen/types.gen.d.ts"

printf '%s\n' '--- exact V2 permission and agent types ---'
sed -n '1315,1380p' "$types"
sed -n '1505,1630p' "$types"

printf '%s\n' '--- task/mode references in the SDK package ---'
rg -n -C 4 '"task"|task\?:|mode\?:|PermissionConfig|PermissionRuleConfig' "$tmpdir/package" \
  -g '*.d.ts' -g '*.js' -g '*.md' | head -260

printf '%s\n' '--- repository configuration usage and OpenCode docs ---'
rg -n -C 4 'OPENCODE_CONFIG|PermissionConfig|permission|mode|task|subagent|primary' src examples README.md package.json \
  -g '*.ts' -g '*.md' -g '*.json' | head -300

Repository: Waishnav/devspace

Length of output: 37339


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- local-agent ownership and subagent execution path ---'
rg -n -C 6 'subagents|LocalAgentRunInput|LocalAgentRuntimeContext|profile|prompt|workspace|agentId' \
  src/local-agent-*.ts src/local-agent-*.test.ts \
  -g '*.ts' | head -500

printf '%s\n' '--- OpenCode agent/task documentation and implementation references ---'
for url in \
  'https://raw.githubusercontent.com/anomalyco/opencode/v1.17.13/packages/web/src/content/docs/agents.mdx' \
  'https://raw.githubusercontent.com/anomalyco/opencode/dev/packages/web/src/content/docs/agents.mdx' \
  'https://api.github.com/repos/anomalyco/opencode/contents/packages' \
  'https://api.github.com/repos/anomalyco/opencode/commits?path=packages/opencode/src/tool/task.ts&per_page=5'; do
  printf '\nURL: %s\n' "$url"
  curl -L --max-time 15 -fsS "$url" 2>/dev/null | rg -n -C 4 'mode|task|subagent|permission|default' | head -160 || true
done

Repository: Waishnav/devspace

Length of output: 43817


🏁 Script executed:

#!/bin/bash
set -eu

tree="$(mktemp)"
trap 'rm -f "$tree"' EXIT
curl -fsSL --max-time 20 \
  'https://api.github.com/repos/anomalyco/opencode/git/trees/v1.17.13?recursive=1' > "$tree"

printf '%s\n' '--- source paths for agent and permission defaults ---'
python3 - "$tree" <<'PY'
import json, sys
data=json.load(open(sys.argv[1]))
for item in data.get("tree", []):
    p=item.get("path","")
    if any(term in p.lower() for term in ("agent", "permission", "task")) and p.endswith((".ts",".tsx",".mdx")):
        print(p)
PY

printf '%s\n' '--- selected source references ---'
for path in \
  'packages/opencode/src/agent/agent.ts' \
  'packages/opencode/src/permission/permission.ts' \
  'packages/opencode/src/tool/task.ts' \
  'packages/opencode/src/config/config.ts'; do
  printf '\nPATH: %s\n' "$path"
  curl -L --max-time 15 -fsS \
    "https://raw.githubusercontent.com/anomalyco/opencode/v1.17.13/$path" 2>/dev/null \
    | rg -n -C 6 'mode|task|permission|primary|subagent|all|default' | head -220 || true
done

Repository: Waishnav/devspace

Length of output: 206


Make the OpenCode agent policy explicit.

Set each devspace_* agent to mode: "primary" and add task: "deny" in opencodePermissionFor. Without these fields, OpenCode controls the agent mode and child-agent delegation. Add a test for all three agent configurations.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/local-agent-opencode.ts` around lines 108 - 114, Update the three agent
configurations in createOpencode for devspace_read_only, devspace_allowed, and
devspace_full_access to explicitly set mode to "primary" and task to "deny"
alongside their existing permission values. Add tests covering these fields for
all three agent configurations.

Source: Coding guidelines

@Waishnav
Waishnav force-pushed the codex/subagent-runtime-opencode branch from d1e388a to 5a7d15a Compare August 13, 2026 07:56
@Waishnav Waishnav closed this Aug 13, 2026
@Waishnav Waishnav reopened this Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant