When the ACP client persists the session id to support resuming, it has
no way to pass it back to the agent to resume an existing session as the
current implementation always creates a new id that is now shared with
Claude Code.
My client used to pass the value in the `--resume` within Claude Code
option, but `--resume` is not compatible with the new `--session-id`:
```
--session-id cannot be used with --continue or --resume.
```
Change:
When `--resume` is used, extract its value and use it as the session id.
---------
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
There have been some issues for folks because the session id we returned
for ACP wasn't the same as the session id for Claude Code.
Now, we pass it through to the `--session-id` flag of the Claude Code
CLI, which means both ACP and Claude Code are always referring to the
same UUID. Yay!
Currently, if we get an unexpected message, we crash. While this is fine
in the world where we vendor CC (which we do by default) we also allow
users to override the executable for cases where their work needs a
wrapper.
This makes it so that we just log the event that we get, but don't crash
right away in case we can continue on.
The "status" message may say "success" but can actually have an error
which we weren't surfacing.
We should now be returning a more helpful message to the user.
Closes#161 and possibly #146
Add ability to disable built-in tools (Read, Write, Edit, Bash, etc.)
when `_meta.disableBuiltInTools` is set to true. This allows clients to
rely solely on MCP tools without the built-in tool implementations.
---
This is just my take on #157 — not necessarily the one that makes sense
/cc @benbrandt
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
🤖 Co-authored-by: Claude (Anthropic AI Assistant)
## Problem
Tool approval dialogs in ACP clients (like Emacs
[agent-shell](https://github.com/xenodium/agent-shell)) were showing up
empty because the `toolCall` object in `session/request_permission`
requests was missing the `title` field.
This caused dialogs to appear with just buttons and no description of
what tool is being approved, making it impossible for users to make
informed decisions.
## Solution
Add the `title` field to both `requestPermission` calls (lines 553 and
612) by using the existing `toolInfoFromToolUse()` function which
already generates appropriate titles for all tool types.
## Before
```
╭─
⚠ Tool Permission ⚠
Allow (y) Reject (n) Always Allow (!)
╰─
```
## After
```
╭─
⚠ Tool Permission ⚠
Write /path/to/file.txt
Allow (y) Reject (n) Always Allow (!)
╰─
```
## References
- This agent-shell.el issue shows (as has been shown above) what the
expected interaction would look like and what this changeset supports:
https://github.com/xenodium/agent-shell/issues/123
- The ACP protocol already supports the `title` field and agent-shell
displays it correctly when present (confirmed with OpenCode Agent which
does send titles)
## Testing
- **Manual testing**: Confirmed with agent-shell that permission dialogs
now show descriptive titles like "Write /path/to/file.txt"
- **Unit tests**: Added test coverage that verifies the `title` field is
present in permission request structures for multiple tool types (Write,
Bash, Read)
- **Regression testing**: Verified the new test fails when the `title`
field is omitted, confirming it catches the bug this PR fixes
- **Existing tests**: All 61 existing unit tests pass
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
We were mapping tool call rejections to actual "refusals" which isn't
quite right in ACP.
Also filters out more of these status messages that Claude is returning.
Use the `_meta` field for tool calls to forward:
- the original tool name (ie `Bash` etc)
- the structured output of the tool call
The output is received by using the `PostToolUse` hook from the SDK. The
`tool_call_update` for the tool result is now only sent when both the
hook callback and the original tool result (plain text) have been
received.
Claude Code describes [in its documentation
](https://docs.claude.com/en/api/agent-sdk/typescript#tool-output-types)the
expected structure of the output for each tools. Unfortunately those are
not part of the SDK, and [empirically I've found that there was some
differences](58c3100a36/local-server/src/server/endpoints/sendMessage/acp/clients/claudeCode/types.ts (L265)).
Still I've found that having this structured output was useful for my
client to provide a richer UX. Maybe Zed will too.
I was not sure how to add tests for this change, given the current setup
We were relying on unreachable to catch stuff for us, but it wasn't
erroring because we were manually casting a few of the types to never,
leading to a bug when a hook response was encountered.
We'll revisit showing these hook responses to the user once ACP supports
multiple types of status messages.
Closes#98
It turns out we can't always call this right away, as on some machines
the process hasn't initialized yet.
Since available models waits for the process to start, as a workaround
we wait for that first, and then set the permission mode again once we
know that it is ready.
It seems that unless you start the session with bypassPermissions, you
can't change to it later.
So we start with it on, and immediately change it back to default. This
allows people to change it later however.
Closes https://github.com/zed-industries/zed/issues/39392
resolves#90
Tested both the `append` and `override` flows by sending:
```javascript
const resp = await this.connection.newSession({
cwd: basePath,
_meta: {
systemPrompt: "you are an obsidian assistant agent, you should be helpful to the user when working with their notes"
},
mcpServers: []
});
```
```javascript
const resp = await this.connection.newSession({
cwd: basePath,
_meta: {
systemPrompt: {
append: "always speak in spanish"
}
},
mcpServers: []
});
```
and confirmed that in both cases those prompts are what gets sent to the
agent!
---------
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
Currently edit requires a permissions call and write does not. This
means that if clients want to allow users to approve `Write` calls they
need to add handling to `HandleFileWrite` but if they do that then edits
get _two_ permissions checks.
Alternatively we could remove the write tool from approved tools (which
would make more sense to me) but I'm assuming its there for a good
reason, if not happy to change to remove it instead!
---------
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
When `default`, `acceptEdits`, or `plan` modes fail, users see the real
error from the SDK instead of a generic message.
`Bypass permissions` gets special treatment - it shows both the SDK
error AND explains it needs proper configuration. I couldn't test SDK
thats why I added special treatment for bypass permissions. If you can
test it we can talk further.
Related to: https://github.com/zed-industries/zed/pull/39524 @benbrandt
## Description
Closes#84 by allowing users to set `CLAUDE_CODE_EXECUTABLE` to override
the binary that's used by the claude code SDK. This is key in enterprise
environments where claude CLI has been shimmed in some way.