diff --git a/.opencode/command/upstream-gap.md b/.opencode/command/upstream-gap.md new file mode 100644 index 00000000000..01672d01696 --- /dev/null +++ b/.opencode/command/upstream-gap.md @@ -0,0 +1,48 @@ +--- +description: "Analyze gap between our fork and upstream opencode" +--- + +## Upstream Gap Analysis + +You are the project manager for the opencode fork at `randomm/opencode`. Analyze the gap between our fork's `dev` branch and the upstream `anomalyco/opencode` `dev` branch. + +### Steps + +1. **Fetch upstream:** + !`git fetch anomalyco 2>&1 || echo "Add remote first: git remote add anomalyco git@github.com:anomalyco/opencode.git"` + +2. **Get the divergence point:** + !`git merge-base dev anomalyco/dev` + +3. **List upstream commits we don't have** (grouped by date): + !`git log --oneline --no-merges anomalyco/dev --not dev -- packages/opencode/ | head -80` + +4. **Count by area:** + !`git log --oneline --no-merges anomalyco/dev --not dev -- packages/opencode/src/provider/ | wc -l` + !`git log --oneline --no-merges anomalyco/dev --not dev -- packages/opencode/src/session/ | wc -l` + !`git log --oneline --no-merges anomalyco/dev --not dev -- packages/opencode/src/tool/ | wc -l` + !`git log --oneline --no-merges anomalyco/dev --not dev -- packages/opencode/src/agent/ | wc -l` + !`git log --oneline --no-merges anomalyco/dev --not dev -- packages/opencode/src/config/ | wc -l` + !`git log --oneline --no-merges anomalyco/dev --not dev -- packages/opencode/src/mcp/ | wc -l` + !`git log --oneline --no-merges anomalyco/dev --not dev -- packages/opencode/src/permission/ | wc -l` + !`git log --oneline --no-merges anomalyco/dev --not dev -- packages/sdk/ | wc -l` + +5. **Our fork's unique changes** (not in upstream): + !`git log --oneline --no-merges dev --not anomalyco/dev -- packages/opencode/ | head -40` + +### Analysis Instructions + +Based on the above data: + +1. **Create a summary table** of upstream changes grouped by area, with commit count and description of what changed +2. **Flag conflicts** - identify areas where BOTH our fork AND upstream made changes (these need careful 3-way merge) +3. **Recommend priority** - rank which upstream changes are most valuable to port: + - CRITICAL: Security fixes, major bug fixes + - HIGH: New features we need (e.g., SDK upgrades, new providers) + - MEDIUM: Improvements and refactors + - LOW: Minor fixes, cosmetic changes +4. **Note already ported** - identify any upstream changes we already manually ported + +Present the results and ask the user which items they want to port. + +$ARGUMENTS diff --git a/packages/opencode/src/plugin/copilot.ts b/packages/opencode/src/plugin/copilot.ts index ef41ee38d37..f70fbe3b7f9 100644 --- a/packages/opencode/src/plugin/copilot.ts +++ b/packages/opencode/src/plugin/copilot.ts @@ -305,7 +305,8 @@ export async function CopilotAuthPlugin(input: PluginInput): Promise { if (!input.model.providerID.includes("github-copilot")) return if (input.model.api.npm === "@ai-sdk/anthropic") { - output.headers["anthropic-beta"] = "interleaved-thinking-2025-05-14" + output.headers["anthropic-beta"] = + "claude-code-20250219,interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14,context-1m-2025-08-07" } const session = await sdk.session diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index ae493eaa0d9..8ffb93ddc18 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -73,7 +73,7 @@ export namespace Provider { options: { headers: { "anthropic-beta": - "claude-code-20250219,interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14,adaptive-thinking-2026-01-28,context-1m-2025-08-07", + "claude-code-20250219,interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14,context-1m-2025-08-07", }, }, } @@ -638,6 +638,12 @@ export namespace Provider { variants: {}, } + // Override context limit for Anthropic models with context-1m header support + // The models.dev API returns 200k for these models, but with our context-1m header they support 1M + if (provider.id === "anthropic" && (m.id.startsWith("claude-sonnet-") || m.id.startsWith("claude-opus-4"))) { + m.limit.context = 1_000_000 + } + m.variants = mapValues(ProviderTransform.variants(m), (v) => v) return m diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index bca9fe17a83..6a77dae109e 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -456,8 +456,15 @@ export namespace ProviderTransform { modelId.includes("claude-opus-4.6") if (isOpus46) { - // Opus 4.6 uses adaptive thinking with effort levels + // Opus 4.6 uses GA 'effort' parameter (no beta header required, officially recommended by Anthropic) + // Note: thinking type: 'adaptive' is separate and requires adaptive-thinking beta header return { + low: { + thinking: { + type: "enabled", + effort: "low", + }, + }, high: { thinking: { type: "enabled", diff --git a/packages/opencode/src/session/compaction.ts b/packages/opencode/src/session/compaction.ts index fb382530291..27906b80bed 100644 --- a/packages/opencode/src/session/compaction.ts +++ b/packages/opencode/src/session/compaction.ts @@ -32,7 +32,7 @@ export namespace SessionCompaction { if (config.compaction?.auto === false) return false const context = input.model.limit.context if (context === 0) return false - const count = input.tokens.input + input.tokens.cache.read + input.tokens.output + const count = input.tokens.input + input.tokens.cache.read const output = Math.min(input.model.limit.output, SessionPrompt.OUTPUT_TOKEN_MAX) || SessionPrompt.OUTPUT_TOKEN_MAX const usable = input.model.limit.input || context - output return count > usable diff --git a/packages/opencode/test/session/compaction.test.ts b/packages/opencode/test/session/compaction.test.ts index 2e9c091870e..5cd8f421d6a 100644 --- a/packages/opencode/test/session/compaction.test.ts +++ b/packages/opencode/test/session/compaction.test.ts @@ -70,7 +70,8 @@ describe("session.compaction.isOverflow", () => { directory: tmp.path, fn: async () => { const model = createModel({ context: 100_000, output: 32_000 }) - const tokens = { input: 50_000, output: 10_000, reasoning: 0, cache: { read: 10_000, write: 0 } } + // Usable = 100k - 32k = 68k. Input + cache.read = 60k + 10k = 70k > 68k + const tokens = { input: 60_000, output: 10_000, reasoning: 0, cache: { read: 10_000, write: 0 } } expect(await SessionCompaction.isOverflow({ tokens, model })).toBe(true) }, })