Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions crates/acp_thread/src/acp_thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2315,8 +2315,22 @@ impl AcpThread {
Self::flush_streaming_text(&mut self.streaming_text_buffer, cx);
self.mark_pending_tools_as_canceled();

// Wait for the send task to complete
cx.background_spawn(turn.send_task)
// Drop the send_task instead of awaiting it. This cancels the prompt
// future immediately, which drops the oneshot `tx`. The `rx.await` in
// run_turn then returns Err, hitting the existing "tx dropped" handler
// that emits Stopped(Cancelled) (see the `let Ok(response) = response`
// branch in run_turn).
//
// We still call connection.cancel() above as a courtesy notification
// to the agent. But we don't wait for the agent to acknowledge it —
// ACP agents that don't properly handle CancelNotification (see
// claude-agent-acp#442, #423) would block the next turn indefinitely.
//
// The previous approach was:
// cx.background_spawn(turn.send_task)
// which awaited the prompt future to completion before proceeding.
drop(turn.send_task);
Task::ready(())
}

fn mark_pending_tools_as_canceled(&mut self) {
Expand Down
45 changes: 45 additions & 0 deletions crates/external_websocket_sync/e2e-test/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# E2E Test Requirements

## CRITICAL: Always Run Latest Code

Before running the E2E test, you MUST ensure BOTH binaries are current:

### Zed binary (Rust side)
```bash
# 1. Check if zed-build binary matches current zed repo HEAD
cd ~/pm/zed && git log --oneline -1
stat -c '%y' ~/pm/helix/zed-build/zed

# 2. If stale, rebuild:
cd ~/pm/helix && ./stack build-zed dev # ~3min dev, ~12min release

# 3. ALWAYS copy to e2e test dir before running:
cp ~/pm/helix/zed-build/zed ~/pm/zed/crates/external_websocket_sync/e2e-test/zed-binary
```

### Go test server (Helix side)
The `run_docker_e2e.sh` script rebuilds this automatically from the local helix checkout (via the `replace` directive in go.mod). Pass `--no-build` ONLY if you are certain no Go code has changed.

If `go mod tidy` fails, run it manually first:
```bash
cd ~/pm/zed/crates/external_websocket_sync/e2e-test/helix-ws-test-server && go mod tidy
```

### Verification
The test prints binary timestamps and md5 checksums at startup:
```
=== Binary versions ===
zed-binary: 2026-03-24 11:17:23 cc426038391008b2
helix-ws-test-server: 2026-03-24 14:40:21 6f17f887e47e
```
CHECK THESE. If the zed-binary timestamp is older than the latest zed commit, it's stale.

## Running the test
```bash
cd ~/pm/zed/crates/external_websocket_sync/e2e-test
./run_docker_e2e.sh # full rebuild
./run_docker_e2e.sh --no-build # ONLY if Go code hasn't changed
```

## Never trust `--no-build`
When investigating test failures, ALWAYS do a full rebuild. Stale binaries are a common source of confusion.
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ require (
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf // indirect
github.com/bradleyfalzon/ghinstallation/v2 v2.17.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/buger/jsonparser v1.1.2 // indirect
github.com/bwmarrin/discordgo v0.28.1 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
Expand Down Expand Up @@ -136,7 +136,7 @@ require (
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
github.com/hashicorp/go-version v1.8.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/helixml/kodit v1.2.0 // indirect
github.com/helixml/kodit v1.2.1 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/infracloudio/msbotbuilder-go v0.2.5 // indirect
github.com/invopop/jsonschema v0.13.0 // indirect
Expand Down Expand Up @@ -193,7 +193,7 @@ require (
github.com/nats-io/nuid v1.0.1 // indirect
github.com/nikoksr/notify v0.41.0 // indirect
github.com/nikolalohinski/gonja v1.5.3 // indirect
github.com/oapi-codegen/runtime v1.2.0 // indirect
github.com/oapi-codegen/runtime v1.3.0 // indirect
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect
github.com/oklog/ulid/v2 v2.1.0 // indirect
Expand Down Expand Up @@ -286,7 +286,7 @@ require (
golang.org/x/mod v0.33.0 // indirect
golang.org/x/net v0.50.0 // indirect
golang.org/x/oauth2 v0.35.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/sys v0.41.0 // indirect
golang.org/x/term v0.40.0 // indirect
golang.org/x/text v0.34.0 // indirect
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,8 @@ github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/buger/jsonparser v1.1.2 h1:frqHqw7otoVbk5M8LlE/L7HTnIq2v9RX6EJ48i9AxJk=
github.com/buger/jsonparser v1.1.2/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/bugsnag/bugsnag-go v1.4.0/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
github.com/bugsnag/panicwrap v1.2.0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/bwmarrin/discordgo v0.28.1 h1:gXsuo2GBO7NbR6uqmrrBDplPUx2T3nzu775q/Rd1aG4=
Expand Down Expand Up @@ -520,8 +520,8 @@ github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/helixml/kodit v1.2.0 h1:CHbK9cxe1VH/kw2UNwAeSj2qCP5Qea4bHA2anxhyzI0=
github.com/helixml/kodit v1.2.0/go.mod h1:7eAxW0jhqp2feKjQubpC5TB81cUz/+V6nLeXCJoue/k=
github.com/helixml/kodit v1.2.1 h1:m4bwhEuhtYrx8+SoHryushOVR1oF8rKTStwH21vZbtU=
github.com/helixml/kodit v1.2.1/go.mod h1:bwT0baMTiauunMRF1dAG4cNh6Gw5d8bos28WwajJYWA=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
Expand Down Expand Up @@ -706,8 +706,8 @@ github.com/nikolalohinski/gonja v1.5.3 h1:GsA+EEaZDZPGJ8JtpeGN78jidhOlxeJROpqMT9
github.com/nikolalohinski/gonja v1.5.3/go.mod h1:RmjwxNiXAEqcq1HeK5SSMmqFJvKOfTfXhkJv6YBtPa4=
github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=
github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc=
github.com/oapi-codegen/runtime v1.2.0 h1:RvKc1CVS1QeKSNzO97FBQbSMZyQ8s6rZd+LpmzwHMP4=
github.com/oapi-codegen/runtime v1.2.0/go.mod h1:Y7ZhmmlE8ikZOmuHRRndiIm7nf3xcVv+YMweKgG1DT0=
github.com/oapi-codegen/runtime v1.3.0 h1:vyK1zc0gDWWXgk2xoQa4+X4RNNc5SL2RbTpJS/4vMYA=
github.com/oapi-codegen/runtime v1.3.0/go.mod h1:kOdeacKy7t40Rclb1je37ZLFboFxh+YLy0zaPCMibPY=
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY=
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw=
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c=
Expand Down Expand Up @@ -1100,8 +1100,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
package main

import (
"context"
"encoding/json"
"fmt"
"log"
Expand Down Expand Up @@ -138,7 +139,7 @@ func (d *testDriver) syncEventCallback(sessionID string, syncMsg *types.SyncMess
return
}

case "thread_created", "user_created_thread":
case "thread_created":
acpThreadID, _ := syncMsg.Data["acp_thread_id"].(string)
if acpThreadID == "" {
acpThreadID, _ = syncMsg.Data["context_id"].(string)
Expand All @@ -148,14 +149,25 @@ func (d *testDriver) syncEventCallback(sessionID string, syncMsg *types.SyncMess
d.round.phase1ThreadCreated = time.Now()
}
d.round.threadIDs = append(d.round.threadIDs, acpThreadID)
log.Printf("[%s] Thread #%d: %s (event=%s)", d.round.agentName, len(d.round.threadIDs), truncate(acpThreadID, 16), syncMsg.EventType)
log.Printf("[%s] Thread #%d: %s (event=%s)", d.round.agentName, len(d.round.threadIDs), syncMsg.EventType, truncate(acpThreadID, 16))
// Capture the thread created for phase 8 so we can send the interrupt to it.
if d.phase == 8 && d.round.phase8ThreadID == "" {
d.round.phase8ThreadID = acpThreadID
log.Printf("[%s] Phase 8: Captured thread ID: %s", d.round.agentName, truncate(acpThreadID, 16))
}
}

case "user_created_thread":
// Spontaneous threads created by Zed (e.g. on startup). The production
// handler creates a child session for these, but they are NOT used for
// test phase follow-ups — only thread_created from chat_message responses
// go into threadIDs. Phase 10 tests user_created_thread separately via
// ProcessSyncEvent injection.
acpThreadID, _ := syncMsg.Data["acp_thread_id"].(string)
if acpThreadID != "" {
log.Printf("[%s] Spontaneous user_created_thread: %s (not tracked for phases)", d.round.agentName, truncate(acpThreadID, 16))
}

case "message_added":
// Ignore message_added events for threads from previous rounds.
// Check if the thread ID belongs to the current round's tracked threads.
Expand Down Expand Up @@ -1112,17 +1124,21 @@ func (d *testDriver) validateStore() bool {
}
}

// Expect at least 6 completed interactions per round (phases 1-5, 7 at minimum)
expectedCompleted := 6 * len(d.agentRounds)
// Expect at least 5 completed interactions per round:
// - Phase 1: thread_created → new session + interaction
// - Phase 3: thread_created → new session + interaction
// - Phase 5: message_added(role=user) → on-the-fly interaction
// - Phase 8: thread_created → new session + interaction
// - Phase 9: on-the-fly interaction (from user interrupt)
// Follow-up phases (2, 4, 7) update existing interactions rather than creating new ones.
// This matches production behavior where Helix tracks one interaction per turn.
expectedCompleted := 5 * len(d.agentRounds)
if completedInteractions < expectedCompleted {
errors = append(errors, fmt.Sprintf("Expected at least %d completed interactions, got %d", expectedCompleted, completedInteractions))
}

// Expect at least 6 interactions WITH content per round (phases 1, 2, 3, 4, 5, 7 at minimum).
// The server now creates on-the-fly interactions for follow-up assistant turns where
// Zed skips message_added(role=user), so all follow-up phases produce content.
// This catches genuine accumulation failures where content is silently dropped.
expectedWithContent := 6 * len(d.agentRounds)
// Expect at least 5 interactions WITH content per round (same reasoning as above).
expectedWithContent := 5 * len(d.agentRounds)
if completedWithContent < expectedWithContent {
errors = append(errors, fmt.Sprintf("Expected at least %d completed interactions with content, got %d (accumulation may be broken)",
expectedWithContent, completedWithContent))
Expand Down Expand Up @@ -1241,6 +1257,29 @@ func main() {
store := memorystore.New()
ps := pubsub.NewNoop()

// Seed a session matching HELIX_SESSION_ID so the production handler
// can look it up. In production, sessions always exist before Zed connects
// (created by spectask/session creation flow). Without this, handlers like
// handleUserCreatedThread fail with "session not found" because they call
// GetSession(agentSessionID) expecting a real session.
seedSessionID := os.Getenv("HELIX_SESSION_ID")
if seedSessionID == "" {
seedSessionID = "ses_e2e-test-session-001"
}
seedSession := types.Session{
ID: seedSessionID,
Name: "E2E Test Seed Session",
Created: time.Now(),
Updated: time.Now(),
Owner: "e2e-test-user",
Mode: types.SessionModeInference,
Type: types.SessionTypeText,
}
if _, err := store.CreateSession(context.Background(), seedSession); err != nil {
log.Fatalf("[test-server] Failed to create seed session: %v", err)
}
log.Printf("[test-server] Created seed session: %s", seedSessionID)

// Create HelixAPIServer with production handlers + in-memory store
srv := server.NewTestServer(store, ps)

Expand Down
15 changes: 9 additions & 6 deletions crates/external_websocket_sync/e2e-test/run_docker_e2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,16 @@ mkdir -p "$SCREENSHOTS_DIR"
E2E_AGENTS="${E2E_AGENTS:-zed-agent}"
echo "=== Running E2E test (agents: $E2E_AGENTS) ==="

# Mount local claude-agent-acp if available (for testing local changes)
# Mount local claude-agent-acp if available (for testing local changes).
# Disabled by default: let Zed auto-install the latest from npm via npx,
# which ensures we test against the same version users get.
# To test local changes, uncomment the block below.
CLAUDE_ACP_MOUNT=""
CLAUDE_ACP_DIR="$ZED_DIR/../claude-agent-acp"
if [ -d "$CLAUDE_ACP_DIR/dist" ] && echo "$E2E_AGENTS" | grep -q "claude"; then
CLAUDE_ACP_MOUNT="-v $(cd "$CLAUDE_ACP_DIR" && pwd):/opt/claude-agent-acp"
echo "[setup] Mounting local claude-agent-acp from $CLAUDE_ACP_DIR"
fi
# CLAUDE_ACP_DIR="$ZED_DIR/../claude-agent-acp"
# if [ -d "$CLAUDE_ACP_DIR/dist" ] && echo "$E2E_AGENTS" | grep -q "claude"; then
# CLAUDE_ACP_MOUNT="-v $(cd "$CLAUDE_ACP_DIR" && pwd):/opt/claude-agent-acp"
# echo "[setup] Mounting local claude-agent-acp from $CLAUDE_ACP_DIR"
# fi

ANTHROPIC_BASE_URL_ARG=""
if [ -n "${ANTHROPIC_BASE_URL:-}" ]; then
Expand Down
29 changes: 24 additions & 5 deletions crates/external_websocket_sync/e2e-test/run_e2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,19 @@ cleanup() {
[ -n "${XVFB_PID:-}" ] && kill "$XVFB_PID" 2>/dev/null || true
rm -f "$MOCK_PORT_FILE"

# Dump Zed errors/panics (full log available at ZED_LOG_FILE)
if [ -f "${ZED_LOG_FILE:-}" ]; then
ZED_ERRORS=$(grep -ciE "panic|error|fatal" "$ZED_LOG_FILE" 2>/dev/null || echo "0")
if [ "$ZED_ERRORS" -gt 0 ]; then
echo ""
echo "=================================================="
echo " ZED PROCESS ERRORS ($ZED_ERRORS lines)"
echo "=================================================="
grep -iE "panic|error|fatal" "$ZED_LOG_FILE" | tail -50 || true
echo " (full log: $ZED_LOG_FILE)"
fi
fi

# Report screenshots
if [ -d "$SCREENSHOT_DIR" ]; then
SHOT_COUNT=$(ls -1 "$SCREENSHOT_DIR"/*.png 2>/dev/null | wc -l)
Expand Down Expand Up @@ -123,7 +136,7 @@ export ZED_HELIX_URL="127.0.0.1:${MOCK_PORT}"
export ZED_HELIX_TOKEN="test-token"
export ZED_HELIX_TLS=false
export ZED_HELIX_SKIP_TLS_VERIFY=false
export HELIX_SESSION_ID="e2e-test-session-001"
export HELIX_SESSION_ID="ses_e2e-test-session-001"

# ---- Determine which agents to test ----
# E2E_AGENTS controls which agent rounds to run. Default: zed-agent only.
Expand All @@ -148,9 +161,13 @@ if echo "$E2E_AGENTS" | grep -q "claude"; then
CLAUDE_PATH_JSON=""
if [ -f "/opt/claude-agent-acp/dist/index.js" ]; then
CLAUDE_PATH_JSON="\"path\": \"node\", \"args\": [\"/opt/claude-agent-acp/dist/index.js\"],"
echo "[setup] Using LOCAL claude-agent-acp from /opt/claude-agent-acp"
LOCAL_VERSION=$(node -e "console.log(require('/opt/claude-agent-acp/package.json').version)" 2>/dev/null || echo "unknown")
echo "[setup] Using LOCAL claude-agent-acp v$LOCAL_VERSION from /opt/claude-agent-acp"
else
echo "[setup] Using npm-installed claude-agent-acp (auto-install)"
# Log which version npx will install so we can correlate failures
# with claude-agent-acp upgrades. This is a quick check, not an install.
CLAUDE_ACP_VERSION=$(npm view @anthropic-ai/claude-agent-acp version 2>/dev/null || echo "unknown")
echo "[setup] Using npm-installed claude-agent-acp (auto-install, latest=$CLAUDE_ACP_VERSION)"
fi
AGENT_SERVERS_JSON=$(cat << AGENTEOF
"agent_servers": {
Expand Down Expand Up @@ -202,12 +219,14 @@ echo "[zed] ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:+set (${#ANTHROPIC_API_KEY}
echo "[zed] E2E_AGENTS=$E2E_AGENTS"
echo ""

# Start Zed
# Start Zed (capture logs for debugging)
ZED_LOG_FILE="/tmp/zed-e2e.log"
"$ZED_BINARY" \
--allow-multiple-instances \
"$PROJECT_DIR" \
&
> "$ZED_LOG_FILE" 2>&1 &
ZED_PID=$!
echo "[zed] Logs: $ZED_LOG_FILE"

echo "[zed] Started (PID $ZED_PID)"
echo ""
Expand Down
Loading
Loading