fix(opencode): port 8 isolated bug fixes from upstream opencode - #199
Conversation
wrapSSE used `void reader.cancel(err)` on chunk timeout, which does not attach a rejection handler — if the underlying stream rejects the cancel, it surfaces as an unhandled promise rejection. Chain `.catch` instead so the timeout path can never crash the process. Ported from upstream commit 69c172e ("fix(provider): handle SSE reader cancel rejections (anomalyco#44944)"). No behavior change outside the timeout/cancel path; does not touch Batuta/Breniac/desktop code. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01AGh7o4NA8jUjwakDzZo12e
This migration rebuilds `workspace` to add the `name` column, but its INSERT...SELECT unconditionally read `name` from the old table. On a database that predates this column, the migration itself would fail with "no such column: name". Check `PRAGMA table_info` first and fall back to an empty string when the column isn't there yet. Ported from upstream commit db86d03 ("fix(core): tolerate missing workspace names"). Pure migration-safety fix; no schema or behavior change for databases that already have the column, and unrelated to Batuta/Breniac/desktop. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01AGh7o4NA8jUjwakDzZo12e
resolveModelID() was double-prefixing two cases: - Full Bedrock ARNs (`arn:...`) got a region prefix appended, which Bedrock rejects. - Any model id containing "deepseek" (not just the R1 models that need cross-region inference) got prefixed with the region. Add an early return for ARN ids and narrow the DeepSeek match to "deepseek.r1". Ported from upstream commit ac1758c ("fix: preserve Bedrock DeepSeek model ids (anomalyco#34441)"). Isolated to Bedrock model id resolution; no effect on other providers or on Batuta/Breniac/desktop. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01AGh7o4NA8jUjwakDzZo12e
`headerTimeout` already accepted `false` to disable its timeout, but `chunkTimeout`'s schema only accepted a positive integer even though the runtime (aisdk.ts wrapSSE / provider.ts fetch wrapper) already treats a non-number chunkTimeout as "no timeout". Users setting `chunkTimeout: false` in provider config hit a schema validation error instead of getting the already-supported disable behavior. Widen the schema to `PositiveInt | Literal(false)`, matching headerTimeout. Ported from the schema portion of upstream commit 4eb29a6 ("fix(opencode): default chunk timeout to five minutes (anomalyco#46890)"). This fork's fetch-wrapping code in packages/opencode/src/provider/ provider.ts already diverged from upstream's default-to-300000ms change, so only the schema fix (which matches existing runtime behavior) was ported — not the default-value wiring, to avoid touching code this fork customized. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01AGh7o4NA8jUjwakDzZo12e
The `x-parent-session-id` header was only attached to the non-opencode provider branch, so subagent/child sessions running on opencode-hosted models never told the backend which session spawned them. Move the header outside the provider branch so it's always sent when present. Ported from upstream commit 611cc73 ("fix(opencode): send parent session header (anomalyco#44752)"). Header-only change; does not affect Batuta/Breniac/desktop request handling. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01AGh7o4NA8jUjwakDzZo12e
Anthropic reports, via providerMetadata.anthropic.inputTransformations, when it removed thinking blocks before the model saw the prompt. A non-empty list here means opencode changed conversation history behind a signed thinking block. Log a warning with the session/message/model context so this churn is traceable instead of silently ignored. Ported from upstream commit 3f39a32 ("feat(opencode): tolerate Anthropic thinking block binding (anomalyco#46653)"). Purely additive logging on step-finish; no change to request/response handling, and unrelated to Batuta/Breniac/desktop. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01AGh7o4NA8jUjwakDzZo12e
When a tool part transitions back through the "running" resolve path (e.g. re-emitted deltas for a call already in progress), it reset `time.start` to `Date.now()` every time, corrupting the reported tool duration. Keep the original start time when the existing state is already "running". Ported from upstream commit 765ae64 ("fix(core): Fix for incorrect time.start reset in tool call logging (anomalyco#32574) (anomalyco#32596)"). Isolated to tool-part timing; no change to tool execution or permissions, and unrelated to Batuta/Breniac/desktop. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01AGh7o4NA8jUjwakDzZo12e
The per-change metadata always included `movePath`, even when the change wasn't a rename, sending `movePath: undefined` explicitly instead of omitting the key. Spread it in conditionally so the key is absent for non-move changes. Ported from upstream commit f7da00f ("fix(opencode): omit empty apply patch move path (anomalyco#45329)"). Isolated to the patch-change summary object; no change to patch application logic, and unrelated to Batuta/Breniac/desktop. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01AGh7o4NA8jUjwakDzZo12e
…imits fork-map.md claimed the upstream remote is always pre-configured and upstream-sync.md's compare step assumed dev and upstream/dev share a common ancestor. Neither holds anymore: fresh sandbox clones don't have the upstream remote, and origin/dev..upstream/dev has no merge-base (upstream rewrote its history at some point), so commit-range diffing silently lists their entire history instead of what's actually new. Documents the tree-diff/byte-diff workaround used to port 8 real fixes in this session, plus the sandbox proxy blocking pkg.pr.new/GitHub tarballs that had nothing to do with those fixes but looked like a regression at first. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01AGh7o4NA8jUjwakDzZo12e
|
Hey! Your PR title Please update it to start with one of:
Where See CONTRIBUTING.md for details. |
|
Thanks for updating your PR! It now meets our contributing guidelines. 👍 |
….spec.ts
The e2e (linux) job on this PR's CI failed with a strict-mode violation:
getByRole('button', { name: 'Close Tab' }) resolves to two elements
whenever a tab is open and showing the not-found fallback, because
Playwright's getByRole name matching is case-insensitive by default and
two different i18n keys produce near-identical accessible names
(common.closeTab -> "Close tab", session.error.notFound.closeTab ->
"Close Tab"). Confirmed this is not caused by this PR's diff (which
doesn't touch packages/app at all): the same failure reproduces
identically on dev HEAD (9ef686e) and on unrelated PRs #190/#191.
A fix already exists as open PR #196 (closes tracking issue #195),
verified there by reproducing the failure and the fix locally. Ported
the same one-line change (adding exact: true) into this PR so CI goes
green here without waiting on #196 to merge first.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AGh7o4NA8jUjwakDzZo12e
|
Couldn't re-run the Playwright test locally in this sandbox to double-confirm (pinned Chromium Generated by Claude Code |
|
Generated by Claude Code |
…thy-nx2ksv # Conflicts: # packages/app/e2e/regression/subagent-child-navigation.spec.ts
Issue for this PR
Closes #200
Type of change
What does this PR do?
Compares this fork against upstream
anomalyco/opencodeand ports 8 small, isolated core/harness bug fixes that were missing here. None of them touch Batuta, Breniac, or the desktop app — all customizations of this fork are untouched.Note on process:
origin/devandupstream/devno longer share a common git ancestor (git merge-basereturns nothing — upstream appears to have rewritten its history at some point after the fork was created), so a normalgit log dev..upstream/devis not a reliable diff (it lists their entire history, not what's new). Each fix below was instead located via tree/content diffing and upstream's own commit log, then verified byte-for-byte against the real upstream commit before porting.Fixes included:
packages/core/src/aisdk.ts— swallow an SSE reader-cancel rejection on chunk timeout that could surface as an unhandled promise rejection (upstream69c172e8a7).packages/core/src/database/migration/20260410174513_workspace-name.ts— tolerate a database that predates theworkspace.namecolumn instead of failing the migration itself (upstreamdb86d03b47).packages/core/src/plugin/provider/amazon-bedrock.ts— stop double-prefixing full Bedrock ARNs and non-R1 DeepSeek model ids (upstreamac1758c0e6).packages/core/src/v1/config/provider.ts— allowchunkTimeout: falsein provider config, matching the already-supported runtime behavior andheaderTimeout's schema (schema portion of upstream4eb29a64f0; the default-value wiring inpackages/opencode/src/provider/provider.tswas intentionally not ported since this fork already has its own diverged fetch-wrapping/timeout code there).packages/opencode/src/session/llm/request.ts— send thex-parent-session-idheader for opencode-hosted models too, not just other providers (upstream611cc73d84).packages/opencode/src/session/processor.ts— log when Anthropic reports it dropped thinking blocks before the model saw the prompt (upstream3f39a329c3).packages/opencode/src/session/tools.ts— preserve a tool call's original start time when it's re-matched while already running, instead of resetting it (upstream765ae641d7).packages/opencode/src/tool/apply_patch.ts— omitmovePathfrom patch-change metadata instead of sending it asundefinedfor non-move changes (upstreamf7da00f35e).Also updated
.claude/skills/opencode-fork-expert/references/{fork-map,upstream-sync}.mdto document the broken git history between the two remotes and a sandbox-specific network limitation (blockedpkg.pr.new/GitHub tarball fetches) discovered while validating these changes, so future upstream syncs don't rediscover the same dead ends.How did you verify your code works?
git show <upstream-hash> -- <file>vs the local file) rather than a speculative rewrite.bun turbo typecheck --filter=@opencode-ai/core --filter=opencodepasses clean on both affected packages.packages/app/packages/enterprisetypecheck fails in this sandbox due to network-blocked dependencies (ghostty-web,@solidjs/start) unrelated to this change — confirmed viagit stashthat the same failure exists on a cleandevcheckout, so it's a pre-existing environment limitation, not a regression from this PR.packages/app/package.json(removingghostty-web) was found and discarded rather than committed — it looked like a local workaround for the sandbox's network restriction, not a real upstream fix, and would have broken the terminal feature for real users.Screenshots / recordings
N/A — no UI change.
Checklist
🤖 Generated with Claude Code
https://claude.ai/code/session_01AGh7o4NA8jUjwakDzZo12e