From e957222883f1b8c904705a03a529b6acfd531a42 Mon Sep 17 00:00:00 2001 From: GuestAUser Date: Sat, 5 Sep 2026 13:39:49 -0300 Subject: [PATCH] fix(opencode): preserve Astra priority service tier - Update the bundled OpenAI SDK to 3.0.88 in opencode and core so GPT-6 Priority requests retain service_tier. - Add request-path regression coverage for Priority and Default while preserving max effort, explicitly detailed summaries, canonical model identity, encrypted stateless replay, tool schemas, and API-key authentication. - Keep provider identity and transport code unchanged, with lockfile changes limited to the affected SDK and provider-utils resolutions. --- bun.lock | 12 +- packages/core/package.json | 2 +- packages/opencode/package.json | 2 +- packages/opencode/test/session/llm.test.ts | 150 ++++++++++++++++++++- 4 files changed, 157 insertions(+), 9 deletions(-) diff --git a/bun.lock b/bun.lock index 12e2a52f1b13..f1b82a7a66f9 100644 --- a/bun.lock +++ b/bun.lock @@ -306,7 +306,7 @@ "@ai-sdk/google-vertex": "4.0.181", "@ai-sdk/groq": "3.0.31", "@ai-sdk/mistral": "3.0.51", - "@ai-sdk/openai": "3.0.84", + "@ai-sdk/openai": "3.0.88", "@ai-sdk/openai-compatible": "2.0.41", "@ai-sdk/perplexity": "3.0.26", "@ai-sdk/provider": "3.0.8", @@ -581,7 +581,7 @@ "@ai-sdk/google-vertex": "4.0.181", "@ai-sdk/groq": "3.0.31", "@ai-sdk/mistral": "3.0.51", - "@ai-sdk/openai": "3.0.84", + "@ai-sdk/openai": "3.0.88", "@ai-sdk/openai-compatible": "2.0.41", "@ai-sdk/perplexity": "3.0.26", "@ai-sdk/provider": "3.0.8", @@ -6019,7 +6019,7 @@ "@opencode-ai/app/@opencode-ai/client": ["@opencode-ai/client@vendor/opencode-ai-client-1.17.13-v2.tgz", {}, "sha512-332kgNifvpQOF9e3UA+pIa5xPrMhLaQkUiNiO+meS0Ba9HjSE6hfsWnEojMkD0DPSLqPP6rCF1dDoF7U0Y0OCQ=="], - "@opencode-ai/core/@ai-sdk/openai": ["@ai-sdk/openai@3.0.84", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@ai-sdk/provider-utils": "4.0.38" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cmgbeJL0bbY0yTJH4/AdmP5E7MjWRL9G8UdhIi0JlV/So03o82ORJofW8OzwCZPTORVQblFbpZXYGDcUd9NdUQ=="], + "@opencode-ai/core/@ai-sdk/openai": ["@ai-sdk/openai@3.0.88", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@ai-sdk/provider-utils": "4.0.40" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-6M4+bxK/UMijDYK2ia1hCKYma1iajFYxHDlhbinFUdhH2WbFsjoZRMpSjEMlTOBptqmh2J1JclKnprGulF8WwQ=="], "@opencode-ai/core/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.41", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kNAGINk71AlOXx10Dq/PXw4t/9XjdK8uxfpVElRwtSFMdeSiLVt58p9TPx4/FJD+hxZuVhvxYj9r42osxWq79g=="], @@ -6395,7 +6395,7 @@ "opencode/@ai-sdk/cerebras": ["@ai-sdk/cerebras@2.0.60", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.54", "@ai-sdk/provider": "3.0.12", "@ai-sdk/provider-utils": "4.0.33" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Rnok3cThg6awBwaDSyiZpgRpbV7pqxGYrA89LODCo5cuEHeP2h0AM0lLHP7zIkclAdXfOm4wldKi/S2T/DGCOw=="], - "opencode/@ai-sdk/openai": ["@ai-sdk/openai@3.0.84", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@ai-sdk/provider-utils": "4.0.38" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cmgbeJL0bbY0yTJH4/AdmP5E7MjWRL9G8UdhIi0JlV/So03o82ORJofW8OzwCZPTORVQblFbpZXYGDcUd9NdUQ=="], + "opencode/@ai-sdk/openai": ["@ai-sdk/openai@3.0.88", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@ai-sdk/provider-utils": "4.0.40" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-6M4+bxK/UMijDYK2ia1hCKYma1iajFYxHDlhbinFUdhH2WbFsjoZRMpSjEMlTOBptqmh2J1JclKnprGulF8WwQ=="], "opencode/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.41", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kNAGINk71AlOXx10Dq/PXw4t/9XjdK8uxfpVElRwtSFMdeSiLVt58p9TPx4/FJD+hxZuVhvxYj9r42osxWq79g=="], @@ -6915,7 +6915,7 @@ "@opencode-ai/core/@ai-sdk/openai/@ai-sdk/provider": ["@ai-sdk/provider@3.0.14", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-5X1k57JBJ4H7H1QjX7CnJYAB1I19r/trVZTMcSms7/kLNZ8RaU4Nt2agcwZzv82Hfx6Q7/TOLU7agAKeFfc8cA=="], - "@opencode-ai/core/@ai-sdk/openai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.38", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-/HHGmtKllqjg1OLc023v9w9kK3laW7Z6TzfZukYQWCsGBbzB9p60zTvvpXFVcs44NZBVXL3viOa1HRKUbeee8g=="], + "@opencode-ai/core/@ai-sdk/openai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.40", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-OL5IrpUm9Y8Dwy+w/vvFwPotS6m52O9W0op2oXgXdCROMJIBalBI0oro6OIBYkPxvm5Xg02GSkoQN25RlR0bnw=="], "@opencode-ai/desktop/@actions/artifact/@actions/http-client": ["@actions/http-client@2.2.3", "", { "dependencies": { "tunnel": "^0.0.6", "undici": "^5.25.4" } }, "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA=="], @@ -7163,7 +7163,7 @@ "opencode/@ai-sdk/openai/@ai-sdk/provider": ["@ai-sdk/provider@3.0.14", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-5X1k57JBJ4H7H1QjX7CnJYAB1I19r/trVZTMcSms7/kLNZ8RaU4Nt2agcwZzv82Hfx6Q7/TOLU7agAKeFfc8cA=="], - "opencode/@ai-sdk/openai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.38", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-/HHGmtKllqjg1OLc023v9w9kK3laW7Z6TzfZukYQWCsGBbzB9p60zTvvpXFVcs44NZBVXL3viOa1HRKUbeee8g=="], + "opencode/@ai-sdk/openai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.40", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-OL5IrpUm9Y8Dwy+w/vvFwPotS6m52O9W0op2oXgXdCROMJIBalBI0oro6OIBYkPxvm5Xg02GSkoQN25RlR0bnw=="], "p-locate/p-limit/yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], diff --git a/packages/core/package.json b/packages/core/package.json index 276b0fe8f76d..4b0bb3b95eb6 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -73,7 +73,7 @@ "@ai-sdk/google-vertex": "4.0.181", "@ai-sdk/groq": "3.0.31", "@ai-sdk/mistral": "3.0.51", - "@ai-sdk/openai": "3.0.84", + "@ai-sdk/openai": "3.0.88", "@ai-sdk/openai-compatible": "2.0.41", "@ai-sdk/perplexity": "3.0.26", "@ai-sdk/provider": "3.0.8", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index b5c8e1d3ac8e..45c004b4cc43 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -67,7 +67,7 @@ "@ai-sdk/google-vertex": "4.0.181", "@ai-sdk/groq": "3.0.31", "@ai-sdk/mistral": "3.0.51", - "@ai-sdk/openai": "3.0.84", + "@ai-sdk/openai": "3.0.88", "@ai-sdk/openai-compatible": "2.0.41", "@ai-sdk/perplexity": "3.0.26", "@ai-sdk/provider": "3.0.8", diff --git a/packages/opencode/test/session/llm.test.ts b/packages/opencode/test/session/llm.test.ts index fcb536f46d91..c58e21dc39a7 100644 --- a/packages/opencode/test/session/llm.test.ts +++ b/packages/opencode/test/session/llm.test.ts @@ -3,7 +3,7 @@ import { ConfigV1 } from "@opencode-ai/core/v1/config/config" import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test" import { SessionV1 } from "@opencode-ai/core/v1/session" import path from "path" -import { tool, type ModelMessage } from "ai" +import { jsonSchema, tool, type ModelMessage } from "ai" import { Cause, Effect, Exit, Fiber, Layer, Stream } from "effect" import { InstanceRef } from "../../src/effect/instance-ref" import { HttpClientRequest, HttpClientResponse } from "effect/unstable/http" @@ -1376,6 +1376,154 @@ describe("session.llm.stream", () => { { config: () => openAIConfig(loadFixture("openai", "gpt-5.2").model, `${state.server!.url.origin}/v1`) }, ) + for (const serviceTier of ["priority", "default"]) { + it.instance( + `preserves Astra ${serviceTier} tier with max reasoning and stateless tool replay`, + () => + Effect.gen(function* () { + const request = waitRequest( + "/responses", + createEventResponse([ + { + type: "response.created", + response: { id: "resp-astra", created_at: 0, model: "gpt-6-astra" }, + }, + { + type: "response.completed", + response: { + incomplete_details: null, + usage: { input_tokens: 1, output_tokens: 1 }, + }, + }, + ]), + ) + const resolved = yield* Provider.use.getModel(ProviderV2.ID.openai, ModelV2.ID.make("astra")) + const sessionID = SessionID.make("session-astra") + const agent = { + name: "test", + mode: "primary", + options: {}, + permission: [{ permission: "*", pattern: "*", action: "allow" }], + } satisfies Agent.Info + + yield* drain({ + user: { + id: MessageID.make("msg_user-astra"), + sessionID, + role: "user", + time: { created: Date.now() }, + agent: agent.name, + model: { providerID: ProviderV2.ID.openai, modelID: resolved.id, variant: "max" }, + }, + sessionID, + model: resolved, + agent, + system: ["You are a helpful assistant."], + messages: [ + { role: "user", content: "Look up a value" }, + { + role: "assistant", + content: [ + { + type: "reasoning", + text: "", + providerOptions: { openai: { itemId: "rs_previous", reasoningEncryptedContent: "encrypted-test" } }, + }, + { + type: "tool-call", + toolCallId: "call_lookup", + toolName: "lookup", + input: { query: "value" }, + providerOptions: { openai: { itemId: "fc_previous" } }, + }, + ], + }, + { + role: "tool", + content: [ + { + type: "tool-result", + toolCallId: "call_lookup", + toolName: "lookup", + output: { type: "text", value: "Found" }, + }, + ], + }, + { role: "user", content: "Continue" }, + ], + tools: { + lookup: tool({ + description: "Look up a value", + strict: true, + inputSchema: jsonSchema( + ProviderTransform.schema(resolved, { + type: "object", + properties: { query: true, limit: { type: "integer", minimum: 1 } }, + required: ["query"], + additionalProperties: false, + }), + ), + }), + }, + }) + + const capture = yield* Effect.promise(() => request) + expect(resolved.api.npm).toBe("@ai-sdk/openai") + expect(capture.url.pathname).toBe("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/v1/responses") + expect(capture.headers.get("Authorization")).toBe("Bearer test-openai-key") + expect(capture.body.model).toBe("gpt-6-astra") + expect(capture.body.stream).toBe(true) + expect(capture.body.reasoning).toEqual({ effort: "max", summary: "detailed" }) + expect(capture.body.store).toBe(false) + expect(capture.body.include).toContain("reasoning.encrypted_content") + const input = capture.body.input as Array> + expect(input).toContainEqual({ type: "reasoning", encrypted_content: "encrypted-test", summary: [] }) + expect(input).toContainEqual({ + type: "function_call", + call_id: "call_lookup", + name: "lookup", + arguments: '{"query":"value"}', + }) + expect(input).toContainEqual({ type: "function_call_output", call_id: "call_lookup", output: "Found" }) + expect(input.some((item) => "id" in item || item.type === "item_reference")).toBe(false) + expect(capture.body.tools).toEqual([ + { + type: "function", + name: "lookup", + description: "Look up a value", + parameters: { + type: "object", + properties: { query: { type: "string" }, limit: { type: "integer" } }, + required: ["query"], + additionalProperties: false, + }, + strict: false, + }, + ]) + expect(capture.body.service_tier).toBe(serviceTier) + }), + { + config: () => ({ + enabled_providers: ["openai"], + provider: { + openai: { + npm: "@ai-sdk/openai", + options: { apiKey: "test-openai-key", baseURL: `${state.server!.url.origin}/v1` }, + models: { + astra: { + id: "gpt-6-astra", + reasoning: true, + options: { serviceTier }, + variants: { max: { reasoningEffort: "max", reasoningSummary: "detailed" } }, + }, + }, + }, + }, + }), + }, + ) + } + it.instance( "keeps supported OpenAI models on AI SDK path when native flag is off", () =>