From 557605c0e3fe4f23bf262178d38b8f14a82d04b0 Mon Sep 17 00:00:00 2001 From: ScottN-PV Date: Fri, 16 Oct 2026 02:11:59 -0400 Subject: [PATCH] fix(provider-grok): report model windows for handoff budgets --- apps/server/scripts/acp-mock-agent.ts | 7 +- .../Adapters/GrokAdapterV2.test.ts | 91 +++++++++++++++++++ packages/provider-acp/src/server/adapter.ts | 14 +++ packages/provider-grok/src/server/adapter.ts | 14 +++ 4 files changed, 123 insertions(+), 3 deletions(-) diff --git a/apps/server/scripts/acp-mock-agent.ts b/apps/server/scripts/acp-mock-agent.ts index bbf669f31068..3fc82e5dc63a 100644 --- a/apps/server/scripts/acp-mock-agent.ts +++ b/apps/server/scripts/acp-mock-agent.ts @@ -427,10 +427,11 @@ function modeState(): AcpCompat.SessionModeState { // "grok-build" product name. const grokAcpModels: ReadonlyArray = [ { - modelId: "grok-4.6", + modelId: process.env.T3_ACP_GROK_MODEL ?? "grok-4.6", name: "Grok 4.6", _meta: { - totalContextTokens: 500_000, + totalContextTokens: Number(process.env.T3_ACP_GROK_CONTEXT_TOKENS ?? 500_000), + contextWindows: [256_000, 500_000], supportsReasoningEffort: true, reasoningEffort: initialGrokReasoningEffort ?? "high", reasoningEfforts: [ @@ -449,7 +450,7 @@ function modelState(): AcpCompat.SessionModelState { } const modelId = grokAcpModels.some((model) => model.modelId === currentModelId) ? currentModelId - : "grok-4.6"; + : (process.env.T3_ACP_GROK_MODEL ?? "grok-4.6"); return { currentModelId: modelId, availableModels: grokAcpModels, diff --git a/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.test.ts index 262ce2e8f77e..dbc2cfdbfff7 100644 --- a/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.test.ts @@ -16,6 +16,7 @@ import * as DateTime from "effect/DateTime"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as PlatformError from "effect/PlatformError"; +import * as Path from "effect/Path"; import * as Schema from "effect/Schema"; import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; import type * as EffectAcpSchema from "effect-acp/compat"; @@ -27,6 +28,8 @@ import type { ProviderInstance } from "@t3tools/provider-core/server/driver"; import * as ProviderInstanceRegistry from "../../provider/ProviderInstanceRegistry.ts"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; +import * as AcpSessionRuntime from "@t3tools/provider-acp/server/AcpSessionRuntime"; +import { handoffBudget } from "@t3tools/provider-core/server/handoffBudget"; import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; import * as RuntimePolicy from "../RuntimePolicy.ts"; import { acpPermissionDisposition } from "@t3tools/provider-acp/server/clientPolicy"; @@ -314,6 +317,94 @@ describe("Grok permission prompts", () => { }); }); +describe("Grok model context windows", () => { + const layerHost = TestProviderHost.layer().pipe(Layer.provide(NodeServices.layer)); + const layerTest = Layer.mergeAll( + NodeServices.layer, + IdAllocator.layer, + McpProviderSessions.layer, + layerHost, + ); + + it.effect("budgets handoffs with the selected Grok model's window", () => + Effect.gen(function* () { + const instanceId = ProviderInstanceId.make("grok-window-test"); + const threadId = ThreadId.make("grok-window-test"); + const modelSelection = { instanceId, model: "grok-build" }; + const policy = runtimePolicy({ runtimeMode: "full-access" }); + const path = yield* Path.Path; + const mockAgentPath = yield* path.fromFileUrl( + new URL("../../../scripts/acp-mock-agent.ts", import.meta.url), + ); + const adapter = yield* makeGrokAdapterV2({ + instanceId, + settings: LAUNCH_TEST_GROK_SETTINGS, + environment: {}, + hostPlatform: "linux", + selfInvocation: yield* resolveSelfInvocation(), + makeRuntime: (input) => + Effect.gen(function* () { + const context = yield* Layer.build( + AcpSessionRuntime.layer({ + ...input, + spawn: { + command: process.execPath, + args: [mockAgentPath], + cwd: process.cwd(), + env: { + T3_ACP_SESSION_LIFECYCLE: "1", + T3_ACP_GROK_MODEL: "grok-4.7", + T3_ACP_GROK_CONTEXT_TOKENS: "256000", + }, + }, + authMethodId: "test", + }), + ); + return yield* AcpSessionRuntime.AcpSessionRuntime.pipe(Effect.provide(context)); + }), + }); + const runtime = yield* adapter.openSession({ + threadId, + providerSessionId: ProviderSessionId.make("grok-window-test"), + modelSelection, + runtimePolicy: policy, + }); + const providerThread = yield* runtime.ensureThread({ + threadId, + modelSelection, + runtimePolicy: policy, + }); + const budgetInput = { + tokenCap: 16_000, + userText: "Continue", + attachments: [], + providerThread, + nativeContextEstimate: 160_684, + }; + assert.isNull(providerThread.contextUsage); + assert.equal(handoffBudget(budgetInput), 0); + assert.equal( + handoffBudget({ + ...budgetInput, + modelContextWindow: runtime.getModelContextWindow?.({ instanceId, model: "grok-4.7" }), + }), + 16_000, + ); + assert.equal(runtime.getModelContextWindow?.({ instanceId, model: "grok-4.7" }), 256_000); + assert.equal(runtime.getModelContextWindow?.({ instanceId, model: " grok-4.7 " }), 256_000); + for (const model of ["custom-model", "grok-build", "toString"]) { + assert.isUndefined(runtime.getModelContextWindow?.({ instanceId, model })); + } + assert.isUndefined( + runtime.getModelContextWindow?.({ + instanceId: ProviderInstanceId.make("another-grok"), + model: "grok-4.7", + }), + ); + }).pipe(Effect.scoped, Effect.provide(layerTest)), + ); +}); + describe("Grok launch permission mode", () => { const layerHost = TestProviderHost.layer().pipe(Layer.provide(NodeServices.layer)); const layerTest = Layer.mergeAll( diff --git a/packages/provider-acp/src/server/adapter.ts b/packages/provider-acp/src/server/adapter.ts index 6bb8555bf99f..e94cfd172519 100644 --- a/packages/provider-acp/src/server/adapter.ts +++ b/packages/provider-acp/src/server/adapter.ts @@ -222,6 +222,10 @@ export interface AcpAdapterV2Flavor { ChildProcessSpawner.ChildProcessSpawner | Crypto.Crypto | Scope.Scope >; readonly resolveModelId?: (selection: ModelSelection) => string | undefined; + readonly getModelContextWindow?: ( + selection: ModelSelection, + session: AcpSessionRuntime.AcpSessionRuntimeStartResult, + ) => number | undefined; readonly applyModelSelection?: (input: { readonly runtime: AcpSessionRuntime.AcpSessionRuntime["Service"]; readonly startResult: AcpSessionRuntime.AcpSessionRuntimeStartResult; @@ -6808,6 +6812,16 @@ export const makeAcpAdapterV2 = Effect.fn("makeAcpAdapterV2")(function* ( providerSessionId: input.providerSessionId, providerSession, events: Stream.fromEffectRepeat(Queue.take(events)), + ...(flavor.getModelContextWindow === undefined + ? {} + : { + getModelContextWindow: (selection) => { + const session = Ref.getUnsafe(activeSessionSetup); + return session === null + ? undefined + : flavor.getModelContextWindow?.(selection, session); + }, + }), ...(postSettleContinuationEnabled ? { hasPendingBackgroundWork: Effect.gen(function* () { diff --git a/packages/provider-grok/src/server/adapter.ts b/packages/provider-grok/src/server/adapter.ts index bb78c1584867..09017fa22052 100644 --- a/packages/provider-grok/src/server/adapter.ts +++ b/packages/provider-grok/src/server/adapter.ts @@ -56,6 +56,7 @@ import { } from "./xaiAcpExtension.ts"; import { mergeProviderInstanceEnvironment } from "@t3tools/provider-core/server/instanceEnvironment"; import { acpPermissionDisposition } from "@t3tools/provider-acp/server/clientPolicy"; +import { sessionModelStateFromInitialize } from "@t3tools/provider-acp/server/runtimeModel"; import * as AcpSessionRuntime from "@t3tools/provider-acp/server/AcpSessionRuntime"; import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; @@ -249,6 +250,19 @@ export function makeGrokAcpAdapterFlavor( supportsImagePrompts: true, supportsCompaction: true, resolveModelId: (selection) => resolveGrokAcpBaseModelId(selection.model), + getModelContextWindow: (selection, session) => { + if (selection.instanceId !== options.instanceId) return undefined; + const modelId = resolveGrokAcpBaseModelId(selection.model); + const models = + session.sessionSetupResult.models ?? + sessionModelStateFromInitialize(session.initializeResult); + const window = models?.availableModels.find((model) => model.modelId === modelId)?._meta + ?.totalContextTokens; + // contextWindows includes selectable larger windows; totalContextTokens is the default. + return typeof window === "number" && Number.isSafeInteger(window) && window > 0 + ? window + : undefined; + }, applyModelSelection: ({ runtime, startResult, modelSelection }) => Effect.gen(function* () { const legacy = startResult.initializeResult.protocolVersion === 1;