|
| 1 | +# 132.10 — Verification: WebSocket Correctness |
| 2 | + |
| 3 | +## Scope |
| 4 | + |
| 5 | +Phase 132 hardens the Codex-facing Responses WebSocket path and changes WebSocket advertisement |
| 6 | +from default-on to explicit opt-in. |
| 7 | + |
| 8 | +## Implementation Evidence |
| 9 | + |
| 10 | +Commits: |
| 11 | + |
| 12 | +- `334f7c2 fix: gate websocket transport by explicit opt-in` |
| 13 | +- `e27f44c fix: harden responses websocket protocol` |
| 14 | +- `2e4733d fix: preserve websocket turn cancellation hooks` |
| 15 | + |
| 16 | +Modified: |
| 17 | + |
| 18 | +- `/Users/jun/Developer/new/700_projects/opencodex/.gitignore` |
| 19 | +- `/Users/jun/Developer/new/700_projects/opencodex/src/config.ts` |
| 20 | +- `/Users/jun/Developer/new/700_projects/opencodex/src/types.ts` |
| 21 | +- `/Users/jun/Developer/new/700_projects/opencodex/src/codex-inject.ts` |
| 22 | +- `/Users/jun/Developer/new/700_projects/opencodex/src/codex-catalog.ts` |
| 23 | +- `/Users/jun/Developer/new/700_projects/opencodex/src/bridge.ts` |
| 24 | +- `/Users/jun/Developer/new/700_projects/opencodex/src/responses/parser.ts` |
| 25 | +- `/Users/jun/Developer/new/700_projects/opencodex/src/server.ts` |
| 26 | +- `/Users/jun/Developer/new/700_projects/opencodex/src/ws-bridge.ts` |
| 27 | +- `/Users/jun/Developer/new/700_projects/opencodex/tests/codex-inject.test.ts` |
| 28 | +- `/Users/jun/Developer/new/700_projects/opencodex/tests/codex-catalog.test.ts` |
| 29 | +- `/Users/jun/Developer/new/700_projects/opencodex/tests/ws-endpoint.test.ts` |
| 30 | + |
| 31 | +## Closed Findings |
| 32 | + |
| 33 | +- Routed continuation safety: routed bridged WS responses now emit empty response ids, so Codex |
| 34 | + falls back to a full next request instead of sending an unresolved incremental suffix. |
| 35 | +- Body type handling: successful WS bodies are classified as SSE, mislabelled SSE, JSON, or |
| 36 | + protocol error. |
| 37 | +- Terminal enforcement: the WS pump stops at the first terminal, cancels the reader, and reports EOF |
| 38 | + before terminal as standalone `type:error`. |
| 39 | +- Header/error fidelity: non-2xx responses use standalone `type:error` with HTTP status and safe |
| 40 | + headers; inbound WS data stores only forwarded allowlist headers. |
| 41 | +- Cancellation: socket close still cancels upstream, and same-socket replacement turns cancel the |
| 42 | + previous in-flight reader and suppress stale frames. |
| 43 | +- Framing/backpressure: CRLF, multiline data, split chunks, unterminated final event, dropped send, |
| 44 | + `-1` backpressure, and bounded sniff replay are covered by tests. |
| 45 | +- Advertisement: absent `websockets` is now false; only explicit `websockets: true` advertises |
| 46 | + provider/catalog `supports_websockets`. |
| 47 | + |
| 48 | +## Automated Verification |
| 49 | + |
| 50 | +- `bun test tests/codex-inject.test.ts tests/codex-catalog.test.ts` |
| 51 | + - 16 pass, 0 fail, 104 assertions. |
| 52 | +- `bun test tests/ws-endpoint.test.ts` |
| 53 | + - 18 pass, 0 fail, 37 assertions. |
| 54 | +- `bun test tests` |
| 55 | + - 77 pass, 0 fail, 255 assertions. |
| 56 | +- `bun x tsc --noEmit` |
| 57 | + - passed with exit 0. |
| 58 | + |
| 59 | +## Live `ocx` Smoke |
| 60 | + |
| 61 | +Initial state: |
| 62 | + |
| 63 | +- `ocx status` returned `Proxy not running`. |
| 64 | +- `~/.opencodex/config.json` had `websockets` absent, with providers `openai`, `opencode-go`, |
| 65 | + `anthropic`. |
| 66 | + |
| 67 | +Default advertisement smoke: |
| 68 | + |
| 69 | +- `ocx start` started the proxy on `http://localhost:10100`. |
| 70 | +- `GET /healthz` returned HTTP 200. |
| 71 | +- `GET /v1/models?client_version=0.141.0` returned catalog rows with `supports_websockets` absent |
| 72 | + for: |
| 73 | + - `gpt-5.5` |
| 74 | + - `opencode-go/kimi-k2.7-code` |
| 75 | + - `opencode-go/minimax-m3` |
| 76 | + |
| 77 | +Direct WebSocket smoke: |
| 78 | + |
| 79 | +- `ws://localhost:10100/v1/responses` with `generate:false` returned |
| 80 | + `response.created -> response.completed` and empty response id `""`. |
| 81 | +- Direct routed WebSocket one-shot with `opencode-go/kimi-k2.6` returned: |
| 82 | + - `result completed` |
| 83 | + - 8 frames |
| 84 | + - first frame `response.created` |
| 85 | + - last frame `response.completed` |
| 86 | + - text `OK` |
| 87 | + |
| 88 | +Shutdown: |
| 89 | + |
| 90 | +- `ocx stop` stopped PID 23557. |
| 91 | +- Final `ocx status` must remain `Proxy not running` before release. |
| 92 | + |
| 93 | +## Residual Note |
| 94 | + |
| 95 | +Codex RS exposes no standalone `response.cancel` client frame in the checked source. Phase 132 |
| 96 | +therefore implements the server-side safe behavior available to opencodex: socket close cancellation |
| 97 | +and same-socket replacement-turn cancellation. A human-visible TUI Ctrl-C transcript remains useful |
| 98 | +release evidence if automation can drive it reliably, but it is no longer the only proof of stale |
| 99 | +frame isolation. |
0 commit comments