Skip to content

ACP connectors, the session's model and mode, and a socket for the user's answers - #30

Merged
endrix merged 2 commits into
mainfrom
feat/acp-connectors
Sep 15, 2026
Merged

endrix merged 2 commits into
mainfrom
feat/acp-connectors

Conversation

@endrix

@endrix endrix commented Sep 15, 2026

Copy link
Copy Markdown
Collaborator

Connectors. A connector is a named ACP agent: its command line, what it can do, its defaults. wfpy discovers the known ones on the PATH (opencode acp, Zed's claude-agent-acp for Claude Code, codex-acp, gemini --experimental-acp); the user's ~/.config/wfpy/connectors.toml (XDG) adds or overrides; the workspace's .wfpy/connectors.toml at the project root (the directory with a pyproject.toml or .git above the workflow file) overrides both. wfpy connectors [--json] lists them, available (the command's first word on the PATH) or not.

[connectors.claude]
command = "claude-agent-acp"
model = "sonnet"          # a session config option the agent offers
mode = "acceptEdits"      # a session mode the agent offers

The transport. acp is the generic ACP transport (opencode-acp stays as its older name). An agent names its connector, @agent(transport="acp", connector="claude"); a run's --acp-connector NAME is the default (opencode when nothing names one); an explicit --agent-cli-acp-command wins as before. A connector's or an agent's model and mode are applied to the session through what the agent offers at new_session / load_session: a model config option (Claude Code: default, sonnet, haiku, opus...) matched by value, name or prefix, and the session modes by id; what is not offered is logged and left. OpenCode keeps OPENCODE_CONFIG_CONTENT.

The human port, for an IDE. --elicit-socket PATH: the agents' questions and, over ACP, their permission requests go to whoever listens on a Unix socket, one JSON object a line; the wire format is on SocketElicitationHandler:

wfpy -> client   {"type": "question", "id": 1, "agent": "planner", "model": "opus", "run_id": "...",
                  "question": "Write hello.txt", "context": "...", "choices": ["Always Allow", "Allow", "Reject"],
                  "timeout_ms": 600000}
client -> wfpy   {"id": 1, "answer": "Allow"}   |   {"id": 1, "declined": true, "reason": "..."}

No listener, a closed one, a reply to another question: a declined question (the reject option for a permission), never a failed run.

Verified on the adapter: a session with model=sonnet mode=default applied (applied in the response); a write permission answered Reject over the socket leaves no file, Allow writes it; wfpy connectors on this machine lists opencode and claude available, codex and gemini not. 26 new tests; whole suite 637 passed.

… user's answers

A connector is a named ACP agent: its command line, what it can do, its
defaults. wfpy discovers the known ones on the PATH (opencode acp, Zed's
claude-agent-acp for Claude Code, codex-acp, gemini --experimental-acp),
the user's ~/.config/wfpy/connectors.toml adds or overrides, the
workspace's .wfpy/connectors.toml at the project root overrides both;
`wfpy connectors [--json]` lists them, available or not. The ACP
transport is now `acp` (opencode-acp its older name): an agent names
its connector, @agent(transport="acp", connector="claude"), a run's
--acp-connector is the default, an explicit --agent-cli-acp-command wins
as before. A connector's or an agent's model and mode are set on the
session through what the agent offers at new_session or load_session: a
config option `model` (Claude Code: default, sonnet, haiku, opus...) and
the session modes; what is not offered is logged and left, OpenCode
keeping its OPENCODE_CONFIG_CONTENT.

--elicit-socket PATH puts the agents' questions and permission requests
to whoever listens on a Unix socket, one JSON object a line, the wire
format documented on SocketElicitationHandler: the IDE's side of the
human port. No listener, a closed one, a reply to another question: a
declined question, never a failed run.

Verified on the adapter: a session with model=sonnet mode=default
applied; a write permission answered Reject over the socket leaves no
file, Allow writes it. 26 tests; the whole suite passes.
@endrix
endrix merged commit 6b36a43 into main Sep 15, 2026
3 of 5 checks passed
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