diff --git a/docs-site/src/content/docs/reference/adapters.md b/docs-site/src/content/docs/reference/adapters.md index 8f524e39c1..7acc2dc7f7 100644 --- a/docs-site/src/content/docs/reference/adapters.md +++ b/docs-site/src/content/docs/reference/adapters.md @@ -451,6 +451,13 @@ Login opens Auth0 browser sign-in, then exchanges the Firebase ID token via - Experimental unofficial bridge; not shown in the dashboard preset by default. See the [provider guide](/guides/providers/) for login instructions. +For SWE-2, an explicit reasoning effort overrides an effort suffix in the model +id. For example, `swe-2-high` with `medium` selects the native `swe-2-medium` UID; +`xhigh`, `ultra`, and `max` select `swe-2-max`. Values below Medium select Medium +and do not disable SWE-2 reasoning. Without an explicit effort, a suffixed model +id is preserved. This applies to both Devin account providers through their +shared adapter; other model families keep their existing suffix precedence. + ## `devin-cli` **Targets:** Cognition's `exa.api_server_pb.ApiServerService/GetChatMessage`, the same Connect diff --git a/src/adapters/devin.ts b/src/adapters/devin.ts index 12d9ab7ee2..ad34352f31 100644 --- a/src/adapters/devin.ts +++ b/src/adapters/devin.ts @@ -45,13 +45,23 @@ function hasEffortSuffix(modelId: string): boolean { * If the catalog is unavailable (degraded mode): append the effort suffix * for any base id that doesn't already carry one, mirroring the catalog shape. */ -async function resolveWireModelUid( +export async function resolveWireModelUid( rawModelId: string, apiKey: string, host: string, reasoningEffort?: string, ): Promise { - const modelId = normalizeDevinModelId(rawModelId); + let modelId = normalizeDevinModelId(rawModelId); + // SWE-2's native UIDs encode the effort. A suffix on a picker model must + // not silently discard the caller's explicit reasoning selection. + if (/^swe-2(?:-(?:medium|high|max))?$/.test(modelId)) { + const efforts: Record = { + none: "medium", off: "medium", minimal: "medium", low: "medium", + medium: "medium", high: "high", xhigh: "max", ultra: "max", max: "max", + }; + const effort = reasoningEffort ? efforts[reasoningEffort] : undefined; + if (effort) modelId = `swe-2-${effort}`; + } if (hasEffortSuffix(modelId)) return modelId; const catalog = await getCachedCatalog(apiKey, host); if (catalog) { diff --git a/structure/adapters/registry.md b/structure/adapters/registry.md index aba5ec61b9..1d115e81d8 100644 --- a/structure/adapters/registry.md +++ b/structure/adapters/registry.md @@ -73,3 +73,10 @@ see [Combo editor routing quota](../gui-and-management-api.md#combo-editor-routi Claude replay carries [Go conversation affinity](../data-planes/inbound-compat.md#claude-affinity-at-final-go-dispatch) privately to final dispatch; preliminary route selection does not inject Go-only headers. Native Chat applies qualifying effort ceilings independently of model pins; pin selection precedes the cap and only pins or cap rewrites enter wire mapping. The [catalog effort contract](../catalog.md#ultra-reasoning-level) records the V1/compaction exemptions and caller-preservation boundary. + +## SWE-2 model effort selection + +`src/adapters/devin.ts` resolves an explicit SWE-2 reasoning effort to the native +medium/high/max UID before accepting a suffix already present in the model id. +Both Devin provider rows share this resolver. Omitted effort preserves an explicit +variant; unrelated model families retain their existing suffix precedence. diff --git a/structure/data-planes/inbound-compat.md b/structure/data-planes/inbound-compat.md index ee84baebee..e748c42ac5 100644 --- a/structure/data-planes/inbound-compat.md +++ b/structure/data-planes/inbound-compat.md @@ -138,3 +138,5 @@ Instruction notice extraction scans fence ranges once and walks original lines b a decreasing cursor. It accepts exactly one ASCII space inside the token notice, preserves unmatched prefix bytes, and does not repeatedly scan or copy shrinking prompt prefixes. Native Chat applies qualifying effort ceilings independently of model pins; pin selection precedes the cap and only pins or cap rewrites enter wire mapping. The [catalog effort contract](../catalog.md#ultra-reasoning-level) records the V1/compaction exemptions and caller-preservation boundary. + +The shared Devin adapter owns [SWE-2 model effort selection](../adapters/registry.md#swe-2-model-effort-selection); this UID rewrite is not a shared routing rule for other models. diff --git a/structure/providers/chat-compat.md b/structure/providers/chat-compat.md index e0b87add2d..4dd054da7c 100644 --- a/structure/providers/chat-compat.md +++ b/structure/providers/chat-compat.md @@ -264,3 +264,5 @@ fragments are not guessed onto pending ID-only calls. parallel/colliding identities, distinct unsafe raw JSON index literals, the maximum safe-integer boundary, invalid index types, missing/null continuations and UTF-8 byte-limit boundaries. + +The shared Devin adapter owns [SWE-2 model effort selection](../adapters/registry.md#swe-2-model-effort-selection); this UID rewrite is not a shared routing rule for other models. diff --git a/structure/providers/cursor.md b/structure/providers/cursor.md index be42793e0b..49195d77ee 100644 --- a/structure/providers/cursor.md +++ b/structure/providers/cursor.md @@ -82,3 +82,5 @@ constraints cannot widen the canonical shape. Bare shell bridge names are reject on the freeform path. Namespaced tools do not acquire bare-shell behavior. Regression coverage lives in `tests/providers/cursor/cursor-tool-definitions.test.ts`. + +The shared Devin adapter owns [SWE-2 model effort selection](../adapters/registry.md#swe-2-model-effort-selection); this UID rewrite is not a shared routing rule for other models. diff --git a/structure/runtime.md b/structure/runtime.md index 89276a1839..ec8364f42a 100644 --- a/structure/runtime.md +++ b/structure/runtime.md @@ -237,3 +237,5 @@ The lightweight top-level CLI help counts Cline CLI among the fifteen registered Devin CLI credential path composition in `src/oauth/devin-cli.ts` follows the selected platform: Windows uses Win32 APPDATA paths, other platforms use POSIX XDG-data paths. The explicit absolute override remains verbatim; credential parsing and login behavior are unchanged. Native Chat applies qualifying effort ceilings independently of model pins; pin selection precedes the cap and only pins or cap rewrites enter wire mapping. The [catalog effort contract](catalog.md#ultra-reasoning-level) records the V1/compaction exemptions and caller-preservation boundary. + +The shared Devin adapter owns [SWE-2 model effort selection](adapters/registry.md#swe-2-model-effort-selection); this UID rewrite is not a shared routing rule for other models. diff --git a/structure/transports/inventory.md b/structure/transports/inventory.md index ec75ff03d4..a51dceecde 100644 --- a/structure/transports/inventory.md +++ b/structure/transports/inventory.md @@ -74,3 +74,5 @@ privately to final dispatch; preliminary route selection does not inject Go-only Devin CLI credential path composition in `src/oauth/devin-cli.ts` follows the selected platform: Windows uses Win32 APPDATA paths, other platforms use POSIX XDG-data paths. The explicit absolute override remains verbatim; credential parsing and login behavior are unchanged. Native Chat applies qualifying effort ceilings independently of model pins; pin selection precedes the cap and only pins or cap rewrites enter wire mapping. The [catalog effort contract](../catalog.md#ultra-reasoning-level) records the V1/compaction exemptions and caller-preservation boundary. + +The shared Devin adapter owns [SWE-2 model effort selection](../adapters/registry.md#swe-2-model-effort-selection); this UID rewrite is not a shared routing rule for other models. diff --git a/structure/transports/responses.md b/structure/transports/responses.md index 6e975af2ac..bb667e5c5b 100644 --- a/structure/transports/responses.md +++ b/structure/transports/responses.md @@ -524,3 +524,5 @@ see [Combo editor routing quota](../gui-and-management-api.md#combo-editor-routi Claude replay carries [Go conversation affinity](../data-planes/inbound-compat.md#claude-affinity-at-final-go-dispatch) privately to final dispatch; preliminary route selection does not inject Go-only headers. Native Chat applies qualifying effort ceilings independently of model pins; pin selection precedes the cap and only pins or cap rewrites enter wire mapping. The [catalog effort contract](../catalog.md#ultra-reasoning-level) records the V1/compaction exemptions and caller-preservation boundary. + +The shared Devin adapter owns [SWE-2 model effort selection](../adapters/registry.md#swe-2-model-effort-selection); this UID rewrite is not a shared routing rule for other models. diff --git a/tests/providers/devin-adapter.test.ts b/tests/providers/devin-adapter.test.ts index aa22e0edce..9c9846cf0c 100644 --- a/tests/providers/devin-adapter.test.ts +++ b/tests/providers/devin-adapter.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test"; -import { createDevinAdapter, mapOcxMessagesToDevin, mapOcxToolsToDevin } from "../../src/adapters/devin"; +import { createDevinAdapter, mapOcxMessagesToDevin, mapOcxToolsToDevin, resolveWireModelUid } from "../../src/adapters/devin"; import { sanitizeToolDescriptionForCognitionForTests } from "../../src/adapters/devin/cloud-direct/chat"; import { DEVIN_MODEL_CONTEXT_WINDOWS, DEVIN_STATIC_MODELS, collapseDevinModelUid } from "../../src/adapters/devin/live-models"; import { parseCatalogBuffer } from "../../src/adapters/devin/cloud-direct/catalog"; @@ -201,3 +201,29 @@ describe("devin adapter", () => { } }); }); + +describe("SWE-2 wire effort selection", () => { + test.each(["medium", "high", "max"])("an explicit %s effort overrides every SWE-2 variant", async (effort) => { + for (const model of ["swe-2", "swe-2-medium", "swe-2-high", "swe-2-max", "swe-2.high"]) { + expect(await resolveWireModelUid(model, "unused", "unused", effort)).toBe(`swe-2-${effort}`); + } + }); + + test.each([ + ["none", "medium"], ["off", "medium"], ["minimal", "medium"], + ["low", "medium"], ["xhigh", "max"], ["ultra", "max"], + ])("maps %s to the supported SWE-2 %s lane", async (effort, expected) => { + expect(await resolveWireModelUid("swe-2-high", "unused", "unused", effort)).toBe(`swe-2-${expected}`); + }); + + test("omitted or unknown effort preserves an explicit variant", async () => { + expect(await resolveWireModelUid("swe-2-high", "unused", "unused")).toBe("swe-2-high"); + expect(await resolveWireModelUid("swe-2-max", "unused", "unused", "future-effort")).toBe("swe-2-max"); + }); + + test("other model families keep their existing suffix precedence", async () => { + for (const model of ["claude-opus-5-medium", "gpt-5-6-sol-high", "swe-1-7-high", "swe-20-high"]) { + expect(await resolveWireModelUid(model, "unused", "unused", "max")).toBe(model); + } + }); +});