diff --git a/CHANGELOG.md b/CHANGELOG.md index e97b22c..5df60e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,8 @@ ## 0.6.2 -- Document Claude Sonnet 5.5 and add it to cache validation. No model behavior changes. +- Add GPT-6.1 Sol (`gpt-6.1-sol`) to the OpenAI profiles, with `low` through `max` efforts (`none` is not supported). +- Document Claude Sonnet 5.5 and add it to cache validation. ## 0.6.1 diff --git a/README.md b/README.md index 8d958e5..3f6eec1 100644 --- a/README.md +++ b/README.md @@ -145,6 +145,7 @@ cost, but have no manual effort variants. | `jev-router/gpt-6-astra` | `low`, `medium`, `high`, `xhigh`, `max` | | `jev-router/gpt-6-luna` | `none`, `low`, `medium`, `high`, `xhigh`, `max` | | `jev-router/gpt-6-sol` | `none`, `low`, `medium`, `high`, `xhigh`, `max` | +| `jev-router/gpt-6.1-sol` | `low`, `medium`, `high`, `xhigh`, `max` | | `jev-router/claude-fable-5-1` | `low`, `medium`, `high`, `xhigh`, `max` | | `jev-router/claude-mythos-5-1` | `low`, `medium`, `high`, `xhigh`, `max` | | `jev-router/claude-opus-5-5` | `low`, `medium`, `high`, `xhigh`, `max` | @@ -152,8 +153,9 @@ cost, but have no manual effort variants. | `jev-router/claude-sonnet-5-5` | `low`, `medium`, `high`, `xhigh`, `max` | See OpenAI's [Astra](https://developers.openai.com/api/docs/models/gpt-6-astra), -[Luna](https://developers.openai.com/api/docs/models/gpt-6-luna), and -[Sol](https://developers.openai.com/api/docs/models/gpt-6-sol) pages to choose +[Luna](https://developers.openai.com/api/docs/models/gpt-6-luna), +[Sol](https://developers.openai.com/api/docs/models/gpt-6-sol), and +[6.1 Sol](https://developers.openai.com/api/docs/models/gpt-6.1-sol) pages to choose between them. Your endpoint must grant access to the model you select. For GPT-6, only standard, single-agent mode is supported: pro models, other diff --git a/docs/behavior.md b/docs/behavior.md index 79f7d9e..fe3d9b7 100644 --- a/docs/behavior.md +++ b/docs/behavior.md @@ -31,7 +31,7 @@ Reference for the wire behavior shared by the OpenCode plugin and the standalone drop injected history). Conflicting caller updates at an insertion boundary receive a local `400`. No item fields are silently stripped to make these combinations work. -- Exact registered OpenAI IDs are `gpt-6-astra`, `gpt-6-luna`, and `gpt-6-sol`; +- Exact registered OpenAI IDs are `gpt-6-astra`, `gpt-6-luna`, `gpt-6-sol`, and `gpt-6.1-sol`; Anthropic `/v1/messages` accepts `claude-fable-5-1`, `claude-mythos-5-1`, `claude-opus-5-5`, `claude-opus-5`, and `claude-sonnet-5-5`. Model/route mismatches are local `400`s. Missing, malformed, unknown, and pro IDs fail locally before classification. diff --git a/src/models.ts b/src/models.ts index e0ae295..ccd7253 100644 --- a/src/models.ts +++ b/src/models.ts @@ -20,6 +20,7 @@ export const MODELS: readonly ModelProfile[] = Object.freeze([ profile("gpt-6-astra", "GPT-6 Astra", ["low", "medium", "high", "xhigh", "max"]), profile("gpt-6-luna", "GPT-6 Luna", ["none", "low", "medium", "high", "xhigh", "max"]), profile("gpt-6-sol", "GPT-6 Sol", ["none", "low", "medium", "high", "xhigh", "max"]), + profile("gpt-6.1-sol", "GPT-6.1 Sol", ["low", "medium", "high", "xhigh", "max"]), profile("claude-fable-5-1", "Claude Fable 5.1", ["low", "medium", "high", "xhigh", "max"], "anthropic", "high"), profile("claude-mythos-5-1", "Claude Mythos 5.1", ["low", "medium", "high", "xhigh", "max"], "anthropic", "high"), profile("claude-opus-5-5", "Claude Opus 5.5", ["low", "medium", "high", "xhigh", "max"], "anthropic"), diff --git a/test/models.test.ts b/test/models.test.ts index 6dc70b9..a9a8383 100644 --- a/test/models.test.ts +++ b/test/models.test.ts @@ -21,7 +21,7 @@ describe("registered model isolation", () => { } }); it("registers only supported Anthropic profiles with model-specific base efforts", () => { - expect(modelsFor("openai").map((model) => model.id)).toEqual(["gpt-6-astra", "gpt-6-luna", "gpt-6-sol"]); + expect(modelsFor("openai").map((model) => model.id)).toEqual(["gpt-6-astra", "gpt-6-luna", "gpt-6-sol", "gpt-6.1-sol"]); expect(modelsFor("anthropic").map((model) => [model.id, model.defaultBaseEffort])).toEqual([ ["claude-fable-5-1", "high"], ["claude-mythos-5-1", "high"], ["claude-opus-5-5", "medium"], ["claude-opus-5", "high"], ["claude-sonnet-5-5", "medium"], ]); diff --git a/test/plugin-v2.test.ts b/test/plugin-v2.test.ts index 8c2dd69..8cf2ea8 100644 --- a/test/plugin-v2.test.ts +++ b/test/plugin-v2.test.ts @@ -9,6 +9,7 @@ import type { ModelEditor } from "@opencode/plugin/promise/model"; import type { SessionHttpRequest, SessionHttpResponse } from "@opencode/plugin/promise/session"; import plugin from "../src/plugin.js"; +import { MODELS } from "../src/models.js"; const originalFetch = globalThis.fetch; const OPENAI = "@opencode/ai/providers/openai/responses"; @@ -22,7 +23,7 @@ const sources = () => new Map([ ]); async function host(options: Record = base, source = sources(), credentials: Record = {}) { - const registrations: unknown[] = []; + const registrations: Parameters[0][] = []; const aliases = new Map>(); const hooks = new Map Promise | void>(); let providerTransform!: (editor: ProviderEditor) => void; @@ -37,7 +38,7 @@ async function host(options: Record = base, source = sources(), const cleanup = await plugin.setup(ctx); const providerPass = () => { aliases.clear(); - providerTransform({ add(registration: any) { registrations.push(registration); for (const model of registration.models) aliases.set(model.id, { ...model }); } } as ProviderEditor); + providerTransform({ add(registration) { registrations.push(registration); for (const model of registration.models) aliases.set(model.id, { ...model }); } } as ProviderEditor); }; const modelPass = () => { modelTransform({ @@ -89,8 +90,8 @@ describe("V2 wrap aliases", () => { it("registers placeholders, copies late source metadata, removes unused aliases and survives reload", async () => { const h = await host(); - expect((h.registrations[0] as any).info).toMatchObject({ id: "jev-router", settings: { transport: "http" } }); - expect((h.registrations[0] as any).models).toHaveLength(8); + expect(h.registrations[0]?.info).toMatchObject({ id: "jev-router", settings: { transport: "http" } }); + expect(h.registrations[0]?.models.map((model) => model.id)).toEqual(MODELS.map((model) => model.id)); expect([...h.aliases.keys()].sort()).toEqual(["claude-opus-5-5", "gpt-6-astra"]); expect(h.aliases.get("gpt-6-astra")).toMatchObject({ name: "GPT-6 Astra", modelID: "gpt-6-astra", limit: { context: 123 }, cost: [1], variants: [], settings: { baseURL: "https://a.test/v1", apiKey: "config-key", extra: 1 }, headers: { "x-source": "provider", "x-model": "yes" }, body: { top: true, model: true } }); expect(h.aliases.get("gpt-6-astra")).toMatchObject({ transport: "http" });