fix(helexa-acp): forward Dispatch::Response to its awaiting router
Some checks failed
build-prerelease / Package helexa-neuron-ada RPM (push) Blocked by required conditions
build-prerelease / Package helexa-neuron-ampere RPM (push) Blocked by required conditions
build-prerelease / Package helexa-neuron-blackwell RPM (push) Blocked by required conditions
build-prerelease / Resolve version stamps (push) Successful in 39s
CI / Format (push) Successful in 41s
CI / Clippy (push) Successful in 2m31s
build-prerelease / Build cortex binary (push) Successful in 4m36s
CI / Test (push) Successful in 5m31s
CI / Build cortex SRPM (push) Has been skipped
CI / Publish cortex to COPR (push) Has been skipped
CI / Build neuron SRPM (push) Has been skipped
CI / Publish neuron to COPR (push) Has been skipped
CI / Bump version in source (push) Has been skipped
build-prerelease / Build neuron-blackwell (push) Successful in 5m51s
build-prerelease / Package cortex RPM (push) Successful in 1m29s
build-prerelease / Build neuron-ampere (push) Successful in 7m18s
build-prerelease / Build neuron-ada (push) Successful in 5m6s
build-prerelease / Publish to rpm.lair.cafe (unstable) (push) Has been cancelled

The catch-all on_receive_dispatch handler was applying
respond_with_error to *every* Dispatch variant, including Response.
For Response variants, that call routes the error to the
ResponseRouter for the *outgoing* request — silently overwriting
the real reply from Zed with "Internal error: not implemented yet".

Every ACP roundtrip we issue (fs/read_text_file, fs/write_text_file,
session/request_permission, terminal/*) was therefore returning an
error to the tool runner regardless of what Zed actually responded.
The model saw uniformly-failing tools, gave up, and confabulated
plausible explanations.

Fix: pattern-match the Dispatch. Response → forward to its router
via respond_with_result. Request / Notification → keep the
"not implemented yet" error response as before.

Found via debug logs showing
  WARN helexa_acp::agent: unhandled ACP message method="fs/read_text_file"
right before every tool failure.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-28 12:16:21 +03:00
parent 33652ac651
commit 5a0861d639

View File

@@ -158,11 +158,31 @@ impl Agent {
)
.on_receive_dispatch(
async move |message: Dispatch, cx: ConnectionTo<Client>| {
tracing::warn!(method = ?message.method(), "unhandled ACP message");
message.respond_with_error(
agent_client_protocol::util::internal_error("not implemented yet"),
cx,
)
// `Dispatch` has three variants. For Request and
// Notification we want the "not implemented yet"
// error response. For *Response* we MUST forward
// the result to its awaiting `ResponseRouter` —
// otherwise our own outbound ACP calls
// (`fs/read_text_file`, `session/request_permission`,
// `terminal/*`, …) get their replies silently
// overwritten with whatever error we'd send a
// peer for an unknown method. That's how Stage 3
// tool dispatches were appearing as
// "Internal error: not implemented yet" results
// to the model.
match message {
Dispatch::Response(result, router) => router.respond_with_result(result),
other => {
tracing::warn!(
method = ?other.method(),
"unhandled ACP message"
);
other.respond_with_error(
agent_client_protocol::util::internal_error("not implemented yet"),
cx,
)
}
}
},
agent_client_protocol::on_receive_dispatch!(),
)