The shared opencode http.Client had a hardcoded 30s Timeout used for every
request including POST /session/{id}/message. Long agent turns (e.g. a web
research report) take longer than 30s, so the client aborted mid-flight with
"context deadline exceeded (Client.Timeout exceeded while awaiting headers)",
even though the per-turn budget (DWS_AGENT_TIMEOUT_MS, default 300s) was far
larger. Retrying hit the same wall.
Drop the client-level deadline (rely on the per-request ctx that doJSON already
wires via http.NewRequestWithContext) and bound only the /global/health probe
with a short 10s timeout so startup detection stays snappy.
Tests: pin Timeout==0 on the default client, and assert a slow reply succeeds
within the turn budget but is cut when the turn ctx is shorter than the reply.
opencode persists its own sessions but, unlike the Claude family, will not let
the caller mint the session id up front: `opencode run` creates a session on the
first turn and only reports its id in the `--format json` event stream, then
continues it with `--session <id>`. So the connector now CAPTURES the id from
the output (like codex's thread id) instead of minting a UUID:
- connect_opencode.go: a dedicated opencodeForwarder that runs
`opencode run --pure --format json`, accumulates reply text from `text`
events, captures the top-level sessionID, and on the first turn persists the
convID->sessionID map to <config>/connect/<clientId>/opencode-sessions.json
(best-effort, restored on startup). The next message — and a connector
restart — replays it with `--session <id>`. Implements streamingForwarder and
sessionResetter, so /new and /clear work and streaming cards stream.
- forwarderForChannel routes the opencode channel to it.
- connectAgentOptionsPayload reports opencode memory as
"per-conversation-captured"; --agent-memory help now lists codex/opencode too.
Verified end to end against the real opencode CLI: turn 1 captures the session
id, turn 2 with --session recalls a fact from turn 1. Unit tests cover capture +
resume (stub bin), store persistence across restart, and reset.
gemini also exposes --session-id/--resume but could not be verified end to end
here (no GEMINI_API_KEY) and its --resume id semantics are unconfirmed, so it is
left for a follow-up rather than wired blind. qoder's qodercli still has only
--resume (no addressable id) and cannot do per-conversation memory.