OpenCode 1.14.42+ closes the /event SSE stream cleanly right after
server.connected, breaking the entire turn lifecycle: prompts get
queued and run, but Paseo never sees session.idle / message deltas /
tool calls / questions. Every turn either fails with a generic stream
EOF or hangs waiting for events that never arrive.
Switch the post-EOF recovery to the canonical messages REST endpoint
(via the upgraded SDK) and poll incrementally so tool calls and
clarifying questions surface live during the SSE gap. Bound the wait
with a completion cap and a separate liveness cap so silent rejections
fail fast instead of hanging until the cap. Cap session.abort similarly
so explicit cancels land within seconds.
The recovery path is gated on the for-await loop exiting without a
terminal event, so healthy turns never enter it - if upstream restores
SSE delivery, this code becomes dead and is mechanically removable via
the COMPAT(opencodeEofRecovery) and COMPAT(opencodeSlowAbort) tags.
Also tighten the type of projectSettingsRoute on WorktreeSetupCalloutPolicy
so router.navigate accepts it (was widened to string, broke app
typecheck on main).
Refs: getpaseo/paseo#861, anomalyco/opencode#26697,
anomalyco/opencode#26635
* fix(server): recover completed opencode turns after SSE EOF (#861)
* fix(server): wait for opencode completion after EOF
The original EOF recovery only checked OpenCode storage once after the SSE stream ended. That missed the real failure mode from #861: OpenCode can drop /event while the turn continues behind the scenes, then persist the assistant completion a little later.
Poll the persisted session for the active turn before failing EOF, while still requiring strong completion evidence and ignoring messages that predate the turn. This preserves failure behavior when there is no persisted completion.
Add behavioral coverage for delayed completion after EOF, partial streamed text plus persisted completion without duplication, stale old completions, and the no-evidence failure path.
---------
Co-authored-by: pluto <plutofog@proton.me>
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
On web (browser/Electron), expo-crypto's randomUUID() and getRandomValues()
just forward to globalThis.crypto.* (see expo-crypto/src/ExpoCrypto.web.ts).
The previous polyfill installed `g.crypto.randomUUID = () => ExpoCrypto.randomUUID()`,
which on web reads back through the same `globalThis.crypto.randomUUID` it
just installed, recursing until the stack overflows. The same trap exists
for getRandomValues, but it rarely triggers because the native version is
almost always present.
Capture a bound reference to the native getRandomValues *before* installing
the polyfill, and generate UUID v4 in JS from 16 random bytes (RFC 4122
version + variant bits). The fallback path stays on ExpoCrypto.getRandomValues
(non-recursive on native), and ExpoCrypto.randomUUID is no longer used.
* test(codex): regression test for server-initiated JSON-RPC approval round-trip
Verified this test fails against the pre-#866 classifier shape from #758.
* test(codex): cover all server-initiated request methods in transport
* Patch production dependency advisories
* ci: harden dependency installs with npm ci --ignore-scripts
- Replace npm install with npm ci --ignore-scripts across all workflows
to enforce lockfile parity and block dependency postinstall scripts.
- Run npm run postinstall explicitly after install so our root patch
script still applies (the only legitimate postinstall use in this
repo); dep lifecycle scripts stay blocked.
- Add lockfile-lint and npm audit signatures to the lint job to catch
registry-host tampering and verify cryptographic signatures.
- Regenerate package-lock.json to match bumped package.json versions.
* ci: drop --ignore-scripts; rely on npm ci + lockfile-lint + audit signatures
Lavamoat allow-scripts only traverses root deps; it can't see workspace
deps like electron, esbuild, sharp. Without proper monorepo support, an
allowlist would be incomplete and electron's binary download breaks.
Keep the rest of the security stack:
- npm ci (strict lockfile parity)
- lockfile-lint (resolved-host check)
- npm audit signatures (cryptographic verification)
Real script-blocking would need pnpm 10+ migration. Tracking separately.
* ci(nix-build): use PR head SHA so fork checkouts work
The previous `ref: ${{ github.head_ref || github.ref }}` made
actions/checkout fetch a branch name that only exists on the fork,
not origin. Use the PR head SHA instead — origin mirrors PR commits
via refs/pull/N/head, so this works for fork PRs without changing
the push-to-main auto-commit behavior.
---------
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>