fix(opencode): connection drops when background subagent dispatched - #1647
Open
zmlgit wants to merge 9 commits into
Open
fix(opencode): connection drops when background subagent dispatched#1647zmlgit wants to merge 9 commits into
zmlgit wants to merge 9 commits into
Conversation
added 8 commits
August 7, 2026 18:45
/message is non-idempotent and the previous 60s timeout killed legit long-running turns. Move /message and GET /event onto a separate no-timeout client; keep the 60s client for short API calls.
Two related changes that together align cc-connect with opencode's official streaming pattern (packages/opencode/src/cli/cmd/run/stream. transport.ts): one long-lived SSE subscription + fire-and-forget prompts + session.status idle as turn-completion signal. 1. POST /message (blocking, holds conn for whole turn) -> POST / prompt_async (204 fire-and-forget). The whole turn output now flows via the /event SSE subscription, so a long turn (incl. background subagent dispatch) can no longer break the POST and vice versa. 2. De-fatalize /event stream EOF. Previously any stream end (server restart, network blip, keepalive expiry) marked the session dead and emitted EventError, tearing down the in-flight turn. The new readHTTPEventsLoop reconnects with exponential backoff (500ms -> 30s cap) and only exits when the session ctx is cancelled. Verified end-to-end against opencode-server v1.18.14: prompt_async returns in ~3ms (vs ~38s for the old blocking /message on the same turn), SSE delivers all turn output, and the SSE reconnects after a forced EOF without losing the session. Adds regression test TestOpencodeHTTPMode_SSEReconnectsAfterEOF and updates the existing /message-based tests for the new prompt_async semantics.
…henhg5#1557 Upstream PR chenhg5#1557 added a messageID parameter to core.AgentSession.Send (prompt, messageID, images, files) so attachments are scoped per-message. Rebasing opencode-resume-v2 onto the new main exposed the stale 3-arg calls in the opencode tests, failing CI lint with "not enough arguments in call to session.Send". Thread an empty messageID through the four opencode test call sites (no platform message id in test context); SaveFilesToDisk falls back to its flat layout when messageID is empty.
zmlgit
force-pushed
the
opencode-resume-v2
branch
from
August 7, 2026 10:45
165743b to
3f05967
Compare
CI surfaced five lint violations on the rebased branch: - live_resume_test.go: unchecked resp.Body.Close / w.Write (errcheck) and numeric 502/500 literals instead of http.Status* constants (staticcheck ST1013) - session_http.go: doHTTPRequest and httpStatusError became dead code after the streamClient split and were flagged as unused. Thread ignored-return markers (`_, _ =`), use http.Status* constants, and remove the two unused helpers. Validated locally with golangci-lint v2.11.4 matching CI both with and without the no_web build tag.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When
cc-connecttalks to officialopencode serveand the LLM dispatches a background subagent (task(background=true)), the connection drops — the user sees no response to the background completion.Symptom reported: "现在官方的cc-connect和官方的opencode配合使用时,一旦派发后台任务就断了".
Root cause
agent/opencode/session_http.gohad two load-bearing failure modes:1. Blocking
POST /session/{id}/messageThe POST held the connection open for the entire agent turn. When the LLM dispatched a background subagent, the parent turn ended immediately (the tool returns "Background task started" → parent loop goes idle), so the blocking POST returned. cc-connect then treated the turn as done — but the actual work (background completion, parent-session injection) was still pending.
2. Fatal
GET /eventSSE EOFAny
/eventSSE stream termination (server restart, network blip, keepalive expiry) marked the session dead:The engine received the error → tore down the session → cancelled
s.ctx→ killed the in-flight turn. No reconnect logic existed.Fix — align with opencode's official streaming pattern
The official TUI client (
packages/opencode/src/cli/cmd/run/stream.transport.ts) uses four rules. This PR adopts the two cc-connect was missing:readHTTPEventsLoopreconnects with exponential backoff (500ms → 30s cap) on stream EOF; only exits when session ctx is cancelledsendHTTPswitched fromPOST /message(blocking) toPOST /prompt_async(204 No Content)session.status: idleevents as turn-completion signalTestOpencodeHTTPMode_StreamsBeforePromptReturnsAndKeepsBackgroundEvents). Child session events are intermediate noise the user doesn't need.Server compatibility
Verified against
opencode servev1.18.14 (current release):POST /session/{id}/prompt_async✓ exists, returns 204 fire-and-forgetGET /event✓ exists, real SSE global stream/api/session/{id}/event?after=✗ dev-branch only, not on this version (so no cursor/replay; reconnect is blind but sufficient because the SSE stays open across background runs)Verification
Unit tests
Live end-to-end against
opencode servev1.18.14Test changes
TestOpencodeHTTPMode_SSEReconnectsAfterEOF— regression for the central fixTestLivePromptAsync,TestLiveSSEReconnectAfterServerRestart— env-gated live testsTestOpencodeHTTPMode_StreamsBeforePromptReturnsAndKeepsBackgroundEvents— adapted for prompt_async (Send returns immediately; no morereleasePromptsynchronization)TestOpencodeHTTPMode_SendReturnsSSEErrorWhenMessageEndpointFails→...PromptAsyncFails— endpoint follow renameCommit-by-commit
23036102— fix opencode background streaming over HTTP (baseline)5145e134— fix opencode HTTP agent error reporting (baseline)9787dab0— fix opencode HTTP lint errors (baseline)7204f7d0— surface opencode HTTP retry limit errors (baseline)5e14fe5e— reset agent session after idle timeout (baseline)3f081e26— split HTTP client (60s API) from streamClient (no-timeout SSE)165743b9— core fix: prompt_async + SSE auto-reconnectOut of scope (YAGNI)
?after=<seq>cursor replay — server v1.18.14 doesn't expose itPre-merge checklist
go build ./...go test ./agent/opencode/ -racego test ./core/ -run TestCUJTestOpencodeHTTPMode_SSEReconnectsAfterEOF