This document describes the current runtime architecture on main, with the default settings:
CURSOR_ACP_TOOL_LOOP_MODE=opencodeCURSOR_ACP_PROVIDER_BOUNDARY=v1CURSOR_ACP_PROVIDER_BOUNDARY_AUTOFALLBACK=true
- OpenCode sends chat requests to the
cursor-acpprovider (/v1/chat/completions). - The plugin proxy spawns
cursor-agentper request and streams NDJSON (stream-jsonformat). - Assistant text/thinking streams back to OpenCode as SSE.
tool_callevents are intercepted at the provider boundary.- Intercepted calls are normalized and guarded, then returned as OpenAI
tool_calls. - OpenCode executes tools locally and sends results back as
role: "tool"messages in the next turn. - Prompt builder renders tool results into
TOOL_RESULT (call_id: ...)blocks for the nextcursor-agentinvocation.
- OpenCode owns execution of the active tool list.
- In
chat.params, existing OpenCode tool definitions are preserved and passed through. cursor-acpdoes not execute SDK/MCP tools in this mode; it translates tool-call protocol boundaries.
cursor-acpcan inject tool definitions and execute via internal router:- Local tools
- SDK-discovered tools
- MCP-discovered tools
- Used as compatibility path and fallback strategy.
- Tool-loop behavior is disabled.
The boundary abstraction is implemented in src/provider/boundary.ts and runtime handling in src/provider/runtime-interception.ts.
v1(default): shared extraction/interception path for Bun + Node proxy handlers.legacy: previous extraction/runtime behavior.
v1 adds:
- Tool name alias resolution (
shell,runCommand,delegateTask,skillMcp, etc.) - Schema compatibility normalization (
src/provider/tool-schema-compat.ts) - Optional edit-to-write reroute for malformed full-file replacement edits
- Tool-loop guard with fingerprinting (
src/provider/tool-loop-guard.ts) - Guarded fallback to
legacywhen enabled and specific termination/error conditions are met
v1 loop safety is driven by two mechanisms:
- Schema validation guard: tracks repeated schema-invalid calls by tool + schema signature.
- Tool-loop guard: tracks repeated calls using fingerprints with error class awareness.
The guard classifies outcomes (validation, not_found, permission, timeout, tool_error, success, unknown) and terminates repetitive loops before they spin indefinitely.
The OpenCode plugin tool hook (buildToolHookEntries in src/plugin.ts) registers local tools (bash, read, write, edit, grep, ls, glob, mkdir, rm, stat) and compatibility aliases such as shell -> bash.
When tool-hook execution is used, path/cwd defaults are normalized against tool context (worktree / directory) to keep file and shell behavior workspace-aware.
- Proxy reuse is enabled by default (
CURSOR_ACP_REUSE_EXISTING_PROXY); this can reuse an already-running process on port32124. - During debugging or rollouts, disable reuse (
CURSOR_ACP_REUSE_EXISTING_PROXY=false) or restart OpenCode to ensure the active proxy process matches the latest build. - If
~/.config/opencodeis reset/wiped, reinstall or re-run model/config sync to restore provider config and plugin symlink.