Skip to content

feat(opencode): add optional runtime overlay#3761

Open
Danigm-dev wants to merge 33 commits intosimstudioai:stagingfrom
Danigm-dev:feat/opencode-optional-runtime
Open

feat(opencode): add optional runtime overlay#3761
Danigm-dev wants to merge 33 commits intosimstudioai:stagingfrom
Danigm-dev:feat/opencode-optional-runtime

Conversation

@Danigm-dev
Copy link
Copy Markdown
Contributor

@Danigm-dev Danigm-dev commented Mar 25, 2026

Summary

Adds OpenCode as an optional Sim capability without changing the default Sim deployment path or base UX.

This PR introduces:

  • the OpenCode workflow block, tools, API routes, and lib/opencode client/service wiring
  • async dropdown/combobox support needed by the OpenCode block selectors
  • an optional OpenCode runtime with local/prod Docker Compose overlays
  • minimal docs and env examples for opt-in setup
  • a configurable OPENCODE_REPOSITORY_ROOT contract so Sim can also target compatible external OpenCode deployments

The default Sim experience remains unchanged:

  • docker-compose.local.yml is unchanged
  • docker-compose.prod.yml is unchanged
  • the OpenCode block stays hidden unless NEXT_PUBLIC_OPENCODE_ENABLED=true

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation
  • Other: ___________

Testing

  • timeout 90s bunx vitest run blocks/blocks.test.ts from apps/sim
  • bunx vitest run lib/opencode/service.test.ts app/api/opencode/repos/route.test.ts app/api/tools/opencode/prompt/route.test.ts from apps/sim
  • docker compose -f docker-compose.local.yml -f docker-compose.opencode.local.yml config
  • env OPENCODE_SERVER_PASSWORD=change-me docker compose -f docker-compose.prod.yml -f docker-compose.opencode.yml config
  • Manual validation:
    • Sim only: base UX unchanged and OpenCode hidden by default
    • Sim + OpenCode local overlay: OpenCode service starts, healthcheck passes, repo catalog loads, and prompts execute from the OpenCode block

Reviewer focus:

  • Confirm the default Sim path is unchanged when OpenCode is not enabled
  • Review the OpenCode runtime contract and OPENCODE_REPOSITORY_ROOT handling for external deployments
  • Review shared dropdown/combobox async-loading changes for regressions

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

Screenshots/Videos

Not included. The OpenCode block is hidden by default and only appears when NEXT_PUBLIC_OPENCODE_ENABLED=true. Manual validation covered both states and a successful prompt execution flow.

@cursor
Copy link
Copy Markdown

cursor bot commented Mar 25, 2026

PR Summary

Medium Risk
Adds a new OpenCode integration path (API routes, internal tool endpoints, and Docker runtime overlays) plus changes to shared async option-loading UI components, which could affect workflow execution and editor dropdown behavior. Risk is mitigated by the feature flag NEXT_PUBLIC_OPENCODE_ENABLED keeping the default UX/deploy path unchanged unless explicitly enabled.

Overview
Adds an opt-in OpenCode integration behind NEXT_PUBLIC_OPENCODE_ENABLED, including a new opencode workflow block with async selectors (repo/provider/model/agent) and corresponding tool registrations for prompting, listing repos, and fetching thread messages.

Introduces new server routes for OpenCode metadata (/api/opencode/*) with session/internal auth + workspace access checks, plus internal-only tool endpoints (/api/tools/opencode/*) that manage OpenCode threads, reuse/store per-workflow session IDs in memory, and retry stale sessions when needed.

Bundles a new OpenCode client/service layer (backed by @opencode-ai/sdk) with safer route error mapping, adds NEXT_PUBLIC_OPENCODE_ENABLED to env schema, updates Next/Vitest aliasing for the SDK, and ships optional Docker Compose overlays + Dockerfile/docs for running the OpenCode runtime locally or in production without changing the base compose files.

Separately hardens shared Dropdown/ComboBox async option fetching (optional options, de-duped fetch attempts, in-flight/version tracking, dependency-change resets) to support the new OpenCode selectors and reduce race conditions.

Written by Cursor Bugbot for commit 9bb48d0. This will update automatically on new commits. Configure here.

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 25, 2026

@Danigm-dev is attempting to deploy a commit to the Sim Team on Vercel.

A member of the Team first needs to authorize it.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 25, 2026

Greptile Summary

This PR introduces OpenCode as an optional Sim capability — a new workflow block, a suite of API routes, a library client/service layer, async dropdown/combobox support, and Docker Compose overlays — while leaving the default Sim deployment path completely unchanged.

The implementation is well-structured: the OpenCode block is hidden behind NEXT_PUBLIC_OPENCODE_ENABLED, all new API routes carry proper auth guards (checkSessionOrInternalAuth / checkInternalAuth) and workspace-access checks, the session-persistence layer uses the existing memory table with a verified unique index, and the stale-session retry logic is both correct and thoroughly tested.

Key changes at a glance:

  • lib/opencode/client.ts — OpenCode SDK client with module-level Docker-runtime detection (previously raised caching concern is resolved)
  • lib/opencode/service.ts — full CRUD of OpenCode sessions/repositories/providers/models/agents, plus DB-backed session storage
  • app/api/opencode/* — discovery routes for the block's async dropdowns; app/api/tools/opencode/* — internal tool routes called by the executor
  • blocks/blocks/opencode.ts — block definition with async fetchOptions/fetchOptionById wired to the discovery routes
  • combobox.tsx / dropdown.tsx — extended with fetchOptions, fetchOptionById, dependency-aware refetch, and in-flight deduplication (shared components; regressions reviewed)
  • docker/opencode.Dockerfile + shell helpers — pinned opencode-ai@0.8.0 via ARG, non-root opencode user, git-askpass.sh scoped to GitHub hosts

Minor findings:

  • Port validation in entrypoint.sh accepts out-of-range values
  • fetchOpenCodeOptionById in the block config fetches the full option list to find one entry by ID on hydration

Confidence Score: 5/5

Safe to merge — all remaining findings are P2 style suggestions with no correctness or data-integrity impact.

The PR is well-implemented: auth is enforced at every route, the memory table upsert relies on a verified unique index, stale-session retry logic is correct and covered by tests, previously flagged issues (existsSync caching, unpinned Docker image, GitHub token leakage) are resolved, and the default Sim path is demonstrably unchanged. The two open findings are minor efficiency and validation nits (P2) that do not affect production correctness.

docker/opencode/entrypoint.sh (port range validation) and apps/sim/blocks/blocks/opencode.ts (fetchOptionById efficiency) have minor P2 suggestions, but neither blocks merge.

Important Files Changed

Filename Overview
apps/sim/lib/opencode/service.ts Core service layer: repository discovery, session CRUD, provider/model/agent listing, and DB-backed session persistence via the memory table (unique index confirmed). Logic is sound with correct deduplication and sorting.
apps/sim/lib/opencode/client.ts SDK client with module-level Docker-runtime detection (IS_DOCKER_RUNTIME) and client-instance caching keyed on base URL + auth header. Previously raised concerns are fully resolved.
apps/sim/app/api/tools/opencode/prompt/route.ts Prompt execution route with session reuse, stale-session retry, and soft-error surfacing (success:true + error field); internal-auth-only, well-validated with Zod, and thoroughly tested.
apps/sim/blocks/blocks/opencode.ts Block definition with async fetchOptions/fetchOptionById wired to discovery routes; fetchOptionById fetches the full option list to find a single entry (P2 efficiency concern).
apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/combobox/combobox.tsx Extended with fetchOptions/fetchOptionById, in-flight deduplication, version tracking, and dependency-aware refetch; existing model/permission filtering is preserved.
apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/dropdown/dropdown.tsx Extended with the same async-loading pattern as combobox, including hydration effect, dependency tracking, and in-flight guard; existing dataMode conversion logic is untouched.
docker/opencode.Dockerfile Pinned opencode-ai version via ARG (default 0.8.0), non-root opencode user, minimal debian-slim base; previously raised unpinned-version concern is addressed.
docker/opencode/entrypoint.sh Orchestrates runtime-env file, opencode.json config, cron installation, and initial repo sync; validate_port only checks for numeric input, not the valid 1–65535 range (P2).
docker/opencode/git-askpass.sh GITHUB_TOKEN is now scoped to github.com prompts only; GIT_TOKEN serves other hosts. Previously raised token-leak concern is resolved.
apps/sim/next.config.ts Adds @opencode-ai/sdk alias for both webpack (absolute path) and turbopack (relative path); both constants are intentionally distinct for their respective bundler semantics.

Sequence Diagram

sequenceDiagram
    participant UI as Browser / Block UI
    participant DiscoveryAPI as /api/opencode/*
    participant ToolAPI as /api/tools/opencode/prompt
    participant Service as lib/opencode/service.ts
    participant Client as lib/opencode/client.ts
    participant OC as OpenCode Server
    participant DB as memory table

    Note over UI,OC: Block configuration — async dropdown loading
    UI->>DiscoveryAPI: GET /api/opencode/repos?workspaceId=...
    DiscoveryAPI->>Service: listOpenCodeRepositories()
    Service->>Client: createOpenCodeClient()
    Client->>OC: project.list()
    OC-->>Client: Project[]
    Client-->>Service: projects
    Service-->>DiscoveryAPI: OpenCodeRepositoryOption[]
    DiscoveryAPI-->>UI: {data: [...repos]}

    UI->>DiscoveryAPI: GET /api/opencode/providers?workspaceId=&repository=...
    DiscoveryAPI->>Service: listOpenCodeProviders(repository)
    Service->>OC: config.providers({ directory })
    OC-->>Service: Provider[]
    Service-->>DiscoveryAPI: OpenCodeProviderOption[]
    DiscoveryAPI-->>UI: {data: [...providers]}

    Note over UI,DB: Workflow execution — prompt tool
    UI->>ToolAPI: POST /api/tools/opencode/prompt {repository, prompt, ...}
    ToolAPI->>Service: resolveOpenCodeRepositoryOption(repository)
    Service->>OC: project.list()
    OC-->>Service: repo option
    ToolAPI->>DB: getStoredOpenCodeSession(workspaceId, memoryKey)
    DB-->>ToolAPI: storedSession | null

    alt No stored session or repository mismatch
        ToolAPI->>Service: createOpenCodeSession(repositoryOption, title)
        Service->>OC: session.create({ directory })
        OC-->>Service: {id: sessionId}
    end

    ToolAPI->>Service: promptOpenCodeSession({sessionId, prompt, ...})
    Service->>OC: session.prompt({id, body})
    OC-->>Service: SessionPromptResponse
    Service-->>ToolAPI: OpenCodePromptResult

    alt Stale session error + reusableStoredThread
        ToolAPI->>Service: createOpenCodeSession() [fresh]
        Service->>OC: session.create()
        OC-->>Service: {id: freshSessionId}
        ToolAPI->>Service: promptOpenCodeSession({freshSessionId, ...})
        Service->>OC: session.prompt()
        OC-->>Service: result
    end

    ToolAPI->>DB: storeOpenCodeSession(workspaceId, memoryKey, {sessionId, repository})
    ToolAPI-->>UI: {success: true, output: {content, threadId, cost?}}
Loading

Reviews (2): Last reviewed commit: "Merge branch 'staging' into feat/opencod..." | Re-trigger Greptile

@Danigm-dev
Copy link
Copy Markdown
Contributor Author

Addressed the actionable Greptile findings in the latest branch state.

  • shouldRetryWithFreshOpenCodeSession() was already narrowed in 6160d1c16, so the broad includes("session") case is no longer present.
  • docker/opencode/git-askpass.sh no longer falls back to GITHUB_TOKEN for non-GitHub hosts.
  • docker/opencode.Dockerfile now pins opencode-ai@0.8.0 to keep the runtime aligned with the pinned SDK version.
  • apps/sim/lib/opencode/client.ts now caches Docker runtime detection at module load instead of checking /.dockerenv on every client creation.

Validation run after these changes:

  • bunx vitest run lib/opencode/service.test.ts app/api/opencode/repos/route.test.ts app/api/tools/opencode/prompt/route.test.ts
  • manual shell check for docker/opencode/git-askpass.sh host-specific token behavior

Latest fix commit: 8ec0c5aea.

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

(open: boolean) => {
if (open) {
void fetchOptionsIfNeeded()
void fetchOptionsIfNeeded(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.

Every dropdown open triggers unnecessary API refetch

Medium Severity

handleOpenChange now calls fetchOptionsIfNeeded(true) with force=true every time any async-loading dropdown or combobox is opened. This bypasses the hasAttemptedOptionsFetchRef guard, causing a full API refetch even when options were already successfully loaded. The intent was to enable retry after fetch errors, but the fix also triggers redundant refetches on every open for all async dropdowns app-wide (including the existing Agent block selectors), causing unnecessary network traffic and loading-spinner flicker.

Additional Locations (1)
Fix in Cursor Fix in Web

@waleedlatif1 waleedlatif1 deleted the branch simstudioai:staging April 3, 2026 23:01
@waleedlatif1 waleedlatif1 reopened this Apr 3, 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.

2 participants