From b707eeb052782cfd8b1ff0445f84b2aaf01da38b Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Thu, 8 Oct 2026 18:30:58 -0700 Subject: [PATCH] refactor(provider-grok): move Grok into its own provider package (#17357) Co-authored-by: Claude Opus 5.5 --- apps/mobile/package.json | 1 + apps/mobile/src/components/ProviderIcon.tsx | 16 ------- apps/mobile/src/lib/providerClients.ts | 2 + apps/server/package.json | 1 + .../scripts/record-grok-acp-replay-fixture.ts | 7 +-- .../Adapters/AcpAdapterV2.test.ts | 6 +-- .../Adapters/GrokAdapterV2.test.ts | 8 ++-- .../Adapters/GrokAdapterV2.testkit.ts | 15 +++--- .../orchestration-v2/CommandPolicy.test.ts | 2 +- .../builtInProviderAdapterDrivers.ts | 2 +- apps/server/src/provider/GrokProvider.test.ts | 6 +-- .../provider/ProviderInstanceRegistry.test.ts | 4 +- apps/server/src/provider/builtInDrivers.ts | 2 +- .../textGeneration/GrokTextGeneration.test.ts | 5 +- apps/web/package.json | 1 + apps/web/src/components/Icons.tsx | 12 ----- .../components/chat/ProviderInstanceIcon.tsx | 4 +- .../components/settings/providerDriverMeta.ts | 8 +--- docs/internals/adding-a-provider.md | 2 +- docs/internals/providers.md | 2 +- package.json | 2 +- packages/contracts/src/settings.ts | 26 ----------- packages/provider-grok/package.json | 42 +++++++++++++++++ packages/provider-grok/src/client.ts | 28 +++++++++++ packages/provider-grok/src/server.ts | 7 +++ .../src/server/acpCliProbe.test.ts | 2 +- .../src/server/acpSupport.test.ts | 2 +- .../provider-grok/src/server/acpSupport.ts | 4 +- .../provider-grok/src/server/adapter.ts | 10 ++-- .../provider-grok/src/server/driver.test.ts | 24 ++-------- .../provider-grok/src/server/driver.ts | 32 ++++++------- .../provider-grok/src/server/skills.test.ts | 2 +- .../provider-grok/src/server/skills.ts | 3 +- .../provider-grok/src/server/status.ts | 6 +-- .../src/server/textGeneration.ts | 5 +- .../provider-grok/src/server/usageLimits.ts | 0 .../src/server/xaiAcpExtension.test.ts | 2 +- .../src/server/xaiAcpExtension.ts | 0 .../src/server/xaiBackgroundTasks.test.ts | 2 +- .../src/server/xaiBackgroundTasks.ts | 0 packages/provider-grok/src/settings.ts | 39 ++++++++++++++++ packages/provider-grok/src/testing.ts | 38 +++++++++++++++ packages/provider-grok/tsconfig.json | 7 +++ pnpm-lock.yaml | 46 +++++++++++++++++++ scripts/release-smoke.ts | 1 + 45 files changed, 287 insertions(+), 149 deletions(-) create mode 100644 packages/provider-grok/package.json create mode 100644 packages/provider-grok/src/client.ts create mode 100644 packages/provider-grok/src/server.ts rename apps/server/src/provider/acp/GrokAcpCliProbe.test.ts => packages/provider-grok/src/server/acpCliProbe.test.ts (98%) rename apps/server/src/provider/acp/GrokAcpSupport.test.ts => packages/provider-grok/src/server/acpSupport.test.ts (99%) rename apps/server/src/provider/acp/GrokAcpSupport.ts => packages/provider-grok/src/server/acpSupport.ts (98%) rename apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.ts => packages/provider-grok/src/server/adapter.ts (98%) rename apps/server/src/provider/Drivers/GrokDriver.test.ts => packages/provider-grok/src/server/driver.test.ts (80%) rename apps/server/src/provider/Drivers/GrokDriver.ts => packages/provider-grok/src/server/driver.ts (90%) rename apps/server/src/provider/Drivers/GrokSkills.test.ts => packages/provider-grok/src/server/skills.test.ts (99%) rename apps/server/src/provider/Drivers/GrokSkills.ts => packages/provider-grok/src/server/skills.ts (97%) rename apps/server/src/provider/GrokProvider.ts => packages/provider-grok/src/server/status.ts (99%) rename apps/server/src/textGeneration/GrokTextGeneration.ts => packages/provider-grok/src/server/textGeneration.ts (97%) rename apps/server/src/provider/grokUsageLimits.ts => packages/provider-grok/src/server/usageLimits.ts (100%) rename apps/server/src/provider/acp/XAiAcpExtension.test.ts => packages/provider-grok/src/server/xaiAcpExtension.test.ts (99%) rename apps/server/src/provider/acp/XAiAcpExtension.ts => packages/provider-grok/src/server/xaiAcpExtension.ts (100%) rename apps/server/src/provider/acp/XAiBackgroundTasks.test.ts => packages/provider-grok/src/server/xaiBackgroundTasks.test.ts (99%) rename apps/server/src/provider/acp/XAiBackgroundTasks.ts => packages/provider-grok/src/server/xaiBackgroundTasks.ts (100%) create mode 100644 packages/provider-grok/src/settings.ts create mode 100644 packages/provider-grok/src/testing.ts create mode 100644 packages/provider-grok/tsconfig.json diff --git a/apps/mobile/package.json b/apps/mobile/package.json index 2b73304bebd16a7eed4001419783cc891f565711..97a0cd33ac744a6672e7e5f47d939d8d29a14353 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -71,6 +71,7 @@ "@t3tools/mobile-terminal-native": "file:./modules/t3-terminal", "@t3tools/provider-core": "workspace:*", "@t3tools/provider-cursor": "workspace:*", + "@t3tools/provider-grok": "workspace:*", "@t3tools/provider-muse": "workspace:*", "@t3tools/provider-opencode": "workspace:*", "@t3tools/provider-pi": "workspace:*", diff --git a/apps/mobile/src/components/ProviderIcon.tsx b/apps/mobile/src/components/ProviderIcon.tsx index f2eb4e3c7f70fd2da7b5f4078278536ba8ba33b5..34141a3121d3ddb9c51997f3de120b16a8a00bb4 100644 --- a/apps/mobile/src/components/ProviderIcon.tsx +++ b/apps/mobile/src/components/ProviderIcon.tsx @@ -93,22 +93,6 @@ export function ProviderIcon(props: ProviderIconProps) { ); } - if (props.provider === "grok") { - const fill = isDarkMode ? "#F5F5F5" : "#0F0F0F"; - return ( - - - - - ); - } - const packageIcon = getProviderClient(props.provider)?.icon; if (packageIcon) { const themeFill = (fill: { readonly light: string; readonly dark: string }) => diff --git a/apps/mobile/src/lib/providerClients.ts b/apps/mobile/src/lib/providerClients.ts index cb469a28706c86a7c93020238c84ea2d348dc80e..d434e6bde328a507056fa6d09f2f5a4254dfbd83 100644 --- a/apps/mobile/src/lib/providerClients.ts +++ b/apps/mobile/src/lib/providerClients.ts @@ -1,6 +1,7 @@ import { isProviderDriverKind } from "@t3tools/contracts"; import { makeProviderClientRegistry } from "@t3tools/provider-core/client"; import { cursorClient } from "@t3tools/provider-cursor/client"; +import { grokClient } from "@t3tools/provider-grok/client"; import { museClient } from "@t3tools/provider-muse/client"; import { openCodeClient } from "@t3tools/provider-opencode/client"; import { piClient } from "@t3tools/provider-pi/client"; @@ -8,6 +9,7 @@ import { piClient } from "@t3tools/provider-pi/client"; /** The provider client definitions this mobile build ships. */ const providerClients = makeProviderClientRegistry([ cursorClient, + grokClient, museClient, openCodeClient, piClient, diff --git a/apps/server/package.json b/apps/server/package.json index 5e731012c583a1be73dc1d890d53368580f7d8bd..cb0f2dfcdc135d2f964f1a6fb45b832b9803db37 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -41,6 +41,7 @@ "@t3tools/provider-acp": "workspace:*", "@t3tools/provider-core": "workspace:*", "@t3tools/provider-cursor": "workspace:*", + "@t3tools/provider-grok": "workspace:*", "@t3tools/provider-muse": "workspace:*", "@t3tools/provider-opencode": "workspace:*", "@t3tools/provider-pi": "workspace:*", diff --git a/apps/server/scripts/record-grok-acp-replay-fixture.ts b/apps/server/scripts/record-grok-acp-replay-fixture.ts index 30db5e0ef85a7feb95ba5768364bb08bb32d641a..d7c7da2c8e08e97ec73863bd0cdb4ba734f1589f 100644 --- a/apps/server/scripts/record-grok-acp-replay-fixture.ts +++ b/apps/server/scripts/record-grok-acp-replay-fixture.ts @@ -10,7 +10,8 @@ * node scripts/record-grok-acp-replay-fixture.ts --scenario simple */ import * as NodeServices from "@effect/platform-node/NodeServices"; -import { GrokSettings, type ProviderReplayEntry } from "@t3tools/contracts"; +import { type ProviderReplayEntry } from "@t3tools/contracts"; +import { GrokSettings } from "@t3tools/provider-grok/settings"; import { HostProcessEnvironment, HostProcessPlatform } from "@t3tools/shared/hostProcess"; import { resolveSelfInvocation } from "@t3tools/shared/nodeRuntime"; import * as Clock from "effect/Clock"; @@ -31,7 +32,7 @@ import { GROK_PROVIDER, grokLaunchRuntimeMode, makeGrokAdapterV2, -} from "../src/orchestration-v2/Adapters/GrokAdapterV2.ts"; +} from "@t3tools/provider-grok/testing"; import { ACP_PROTOCOL } from "@t3tools/provider-acp/server/adapter"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; import type { ProviderAdapterV2SessionRuntime } from "@t3tools/provider-core/server/ProviderAdapter"; @@ -43,7 +44,7 @@ import { materializeFixtureInput } from "../src/orchestration-v2/testkit/fixture import { runOrchestratorV2Scenario } from "../src/orchestration-v2/testkit/OrchestratorScenario.ts"; import * as ProviderReplayHarness from "../src/orchestration-v2/testkit/ProviderReplayHarness.ts"; import { checkpointWorkspace } from "@t3tools/provider-testing/replayWorkspace"; -import { makeGrokAcpRuntime } from "../src/provider/acp/GrokAcpSupport.ts"; +import { makeGrokAcpRuntime } from "@t3tools/provider-grok/testing"; import { buildRuntimeInstructions } from "@t3tools/provider-core/server/runtimeInstructions"; const wallClock = Clock.Clock.defaultValue(); diff --git a/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.test.ts index 5e225d570d7a4f79bcc09f92efbf4d9e2c84faee..62bff5add8ef7966f84d4515672a9726c33888b2 100644 --- a/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.test.ts @@ -12,7 +12,6 @@ import * as NodeServices from "@effect/platform-node/NodeServices"; import { assert, describe, it } from "@effect/vitest"; import { CheckpointId, - GrokSettings, EnvironmentId, MessageId, type ModelSelection, @@ -27,6 +26,7 @@ import { ThreadId, type OrchestrationV2ProviderThread, } from "@t3tools/contracts"; +import { GrokSettings } from "@t3tools/provider-grok/settings"; import { HostProcessIsExecutable, HostProcessPlatform } from "@t3tools/shared/hostProcess"; import { resolveSelfInvocation } from "@t3tools/shared/nodeRuntime"; import * as DateTime from "effect/DateTime"; @@ -64,7 +64,7 @@ import { makeXAiPromptCompletionRuntime, normalizeXAiAcpToolCallState, registerXAiBackgroundTaskTracking, -} from "../../provider/acp/XAiAcpExtension.ts"; +} from "@t3tools/provider-grok/testing"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; import { ProviderAdapterProtocolError, @@ -93,7 +93,7 @@ import { type AcpAdapterV2SubagentUpdate, } from "@t3tools/provider-acp/server/adapter"; -import { makeGrokAdapterV2 } from "./GrokAdapterV2.ts"; +import { makeGrokAdapterV2 } from "@t3tools/provider-grok/testing"; import { acpRegistryPromptFailure, registerMistralVibeAcpExtensions, diff --git a/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.test.ts index 23ebb1b4f2a13258fca847fc126535595a4c760e..1dcb231ea103aff5dead71b06aa9e398e9915736 100644 --- a/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.test.ts @@ -1,16 +1,16 @@ import * as NodeServices from "@effect/platform-node/NodeServices"; import { - GrokSettings, ProjectId, ProviderInstanceId, ProviderSessionId, type RuntimeMode, ThreadId, } from "@t3tools/contracts"; +import { GrokSettings } from "@t3tools/provider-grok/settings"; import { HostProcessPlatform } from "@t3tools/shared/hostProcess"; import { resolveSelfInvocation } from "@t3tools/shared/nodeRuntime"; import * as EffectAcpErrors from "effect-acp/errors"; -import { xAiRateLimitedErrorCode } from "../../provider/acp/XAiAcpExtension.ts"; +import { xAiRateLimitedErrorCode } from "@t3tools/provider-grok/testing"; import { assert, describe, it } from "@effect/vitest"; import * as Crypto from "effect/Crypto"; import * as DateTime from "effect/DateTime"; @@ -25,7 +25,7 @@ import type * as EffectAcpSchema from "effect-acp/compat"; import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; import { layerTestProviderHost } from "@t3tools/provider-testing/host"; import * as ProjectStore from "../ProjectStore.ts"; -import { buildInitialGrokProviderSnapshot } from "../../provider/GrokProvider.ts"; +import { buildInitialGrokProviderSnapshot } from "@t3tools/provider-grok/testing"; 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"; @@ -43,7 +43,7 @@ import { makeGrokAdapterV2, GrokProviderCapabilitiesV2, type GrokAdapterV2Options, -} from "./GrokAdapterV2.ts"; +} from "@t3tools/provider-grok/testing"; const LAUNCH_TEST_GROK_SETTINGS = Schema.decodeSync(GrokSettings)({ binaryPath: "grok-launch-test", diff --git a/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.testkit.ts b/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.testkit.ts index 58446793decd9bd221b7ae4adc27415024a0a352..172150432f537bf93afd9bd425ddb764d67b919b 100644 --- a/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.testkit.ts +++ b/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.testkit.ts @@ -1,5 +1,5 @@ import * as NodeServices from "@effect/platform-node/NodeServices"; -import { GrokSettings } from "@t3tools/contracts"; +import { GrokSettings } from "@t3tools/provider-grok/settings"; import * as Effect from "effect/Effect"; import * as Crypto from "effect/Crypto"; import * as FileSystem from "effect/FileSystem"; @@ -12,11 +12,8 @@ import { resolveSelfInvocation } from "@t3tools/shared/nodeRuntime"; import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; import { layerTestProviderHost } from "@t3tools/provider-testing/host"; -import { - GROK_ACP_CANCEL_META, - GROK_ACP_INITIALIZE_META, -} from "../../provider/acp/GrokAcpSupport.ts"; -import { makeXAiPromptCompletionRuntime } from "../../provider/acp/XAiAcpExtension.ts"; +import { GROK_ACP_CANCEL_META, GROK_ACP_INITIALIZE_META } from "@t3tools/provider-grok/testing"; +import { makeXAiPromptCompletionRuntime } from "@t3tools/provider-grok/testing"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests"; import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts"; @@ -29,7 +26,11 @@ import { makeAcpReplayCompletenessAssertion, makeAcpReplayRuntime, } from "./AcpAdapterV2.testkit.ts"; -import { GROK_DEFAULT_INSTANCE_ID, GROK_PROVIDER, makeGrokAdapterV2 } from "./GrokAdapterV2.ts"; +import { + GROK_DEFAULT_INSTANCE_ID, + GROK_PROVIDER, + makeGrokAdapterV2, +} from "@t3tools/provider-grok/testing"; const DEFAULT_GROK_SETTINGS = Schema.decodeUnknownSync(GrokSettings)({}); diff --git a/apps/server/src/orchestration-v2/CommandPolicy.test.ts b/apps/server/src/orchestration-v2/CommandPolicy.test.ts index 517229c222ea9773172d20ff9c662b6107dd5e4c..8df7da5cacdcf9670dc80931db875cd331ff7789 100644 --- a/apps/server/src/orchestration-v2/CommandPolicy.test.ts +++ b/apps/server/src/orchestration-v2/CommandPolicy.test.ts @@ -13,7 +13,7 @@ import * as Effect from "effect/Effect"; import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts"; import { CursorProviderCapabilitiesV2 } from "@t3tools/provider-cursor/testing"; -import { GrokProviderCapabilitiesV2 } from "./Adapters/GrokAdapterV2.ts"; +import { GrokProviderCapabilitiesV2 } from "@t3tools/provider-grok/testing"; import * as CommandPolicy from "./CommandPolicy.ts"; const commandId = CommandId.make("command-policy-test"); diff --git a/apps/server/src/orchestration-v2/builtInProviderAdapterDrivers.ts b/apps/server/src/orchestration-v2/builtInProviderAdapterDrivers.ts index e48ceb60177c95fbfcf5162b0a59b2f1720291fd..f1ce18c12f88c83bcd6c7ea28bb51d2c45d4a010 100644 --- a/apps/server/src/orchestration-v2/builtInProviderAdapterDrivers.ts +++ b/apps/server/src/orchestration-v2/builtInProviderAdapterDrivers.ts @@ -13,7 +13,7 @@ import { CursorAdapterV2Driver, type CursorAdapterV2DriverEnv, } from "@t3tools/provider-cursor/server"; -import { GrokAdapterV2Driver, type GrokAdapterV2DriverEnv } from "./Adapters/GrokAdapterV2.ts"; +import { GrokAdapterV2Driver, type GrokAdapterV2DriverEnv } from "@t3tools/provider-grok/server"; import { OpenCodeAdapterV2Driver, type OpenCodeAdapterV2DriverEnv, diff --git a/apps/server/src/provider/GrokProvider.test.ts b/apps/server/src/provider/GrokProvider.test.ts index 000176e9bb2318e68142cd72a5f5ec954ab9657c..73f56a3558db36e535969aa0444190f5617f9be7 100644 --- a/apps/server/src/provider/GrokProvider.test.ts +++ b/apps/server/src/provider/GrokProvider.test.ts @@ -8,7 +8,7 @@ import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; import * as Schema from "effect/Schema"; import { HttpClient, HttpClientResponse } from "effect/http"; -import { GrokSettings } from "@t3tools/contracts"; +import { GrokSettings } from "@t3tools/provider-grok/settings"; import { buildGrokModelCapabilities, @@ -17,9 +17,9 @@ import { checkGrokProviderStatus, grokSlashCommandsFromInitialize, parseGrokModelsCliOutput, -} from "./GrokProvider.ts"; +} from "@t3tools/provider-grok/testing"; import { execScriptSource, writeFakeCli } from "@t3tools/provider-testing/fakeCli"; -import { grokUsageResponseToLimits, readGrokAccount } from "./grokUsageLimits.ts"; +import { grokUsageResponseToLimits, readGrokAccount } from "@t3tools/provider-grok/testing"; const decodeGrokSettings = Schema.decodeSync(GrokSettings); const __dirname = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url)); diff --git a/apps/server/src/provider/ProviderInstanceRegistry.test.ts b/apps/server/src/provider/ProviderInstanceRegistry.test.ts index 353c29fb90daaee7a7692b2478d6b6caec71ef7f..933751bdd39b73da00f56970e83c301915c032eb 100644 --- a/apps/server/src/provider/ProviderInstanceRegistry.test.ts +++ b/apps/server/src/provider/ProviderInstanceRegistry.test.ts @@ -31,11 +31,11 @@ import { EnvironmentId, type ClaudeSettings, type CodexSettings, - type GrokSettings, ProviderDriverKind, type ProviderInstanceConfigMap, ProviderInstanceId, } from "@t3tools/contracts"; +import type { GrokSettings } from "@t3tools/provider-grok/settings"; import type { CursorSettings } from "@t3tools/provider-cursor/settings"; import { HostProcessPlatform, isHostWindows } from "@t3tools/shared/hostProcess"; import * as DateTime from "effect/DateTime"; @@ -54,7 +54,7 @@ import * as ServerSettings from "../serverSettings.ts"; import { ClaudeDriver, type ClaudeDriverEnv } from "./Drivers/ClaudeDriver.ts"; import { CodexDriver, type CodexDriverEnv } from "./Drivers/CodexDriver.ts"; import { CursorDriver, type CursorDriverEnv } from "@t3tools/provider-cursor/server"; -import { GrokDriver, type GrokDriverEnv } from "./Drivers/GrokDriver.ts"; +import { GrokDriver, type GrokDriverEnv } from "@t3tools/provider-grok/server"; import { OpenCodeDriver, type OpenCodeDriverEnv } from "@t3tools/provider-opencode/server"; import type { OpenCodeSettings } from "@t3tools/provider-opencode/settings"; import * as ModelManifest from "./ModelManifest.ts"; diff --git a/apps/server/src/provider/builtInDrivers.ts b/apps/server/src/provider/builtInDrivers.ts index 516e52d0e49ba291cb731f851eb42ebd97606948..1e21b8dc7cc199b0057dbe31975f5828f442495d 100644 --- a/apps/server/src/provider/builtInDrivers.ts +++ b/apps/server/src/provider/builtInDrivers.ts @@ -25,7 +25,7 @@ import { AntigravityDriver, type AntigravityDriverEnv } from "./Drivers/Antigrav import { ClaudeDriver, type ClaudeDriverEnv } from "./Drivers/ClaudeDriver.ts"; import { CodexDriver, type CodexDriverEnv } from "./Drivers/CodexDriver.ts"; import { CursorDriver, type CursorDriverEnv } from "@t3tools/provider-cursor/server"; -import { GrokDriver, type GrokDriverEnv } from "./Drivers/GrokDriver.ts"; +import { GrokDriver, type GrokDriverEnv } from "@t3tools/provider-grok/server"; import { OpenCodeDriver, type OpenCodeDriverEnv } from "@t3tools/provider-opencode/server"; import { MuseDriver, type MuseDriverEnv } from "@t3tools/provider-muse/server"; import { PiDriver, type PiDriverEnv } from "@t3tools/provider-pi/server"; diff --git a/apps/server/src/textGeneration/GrokTextGeneration.test.ts b/apps/server/src/textGeneration/GrokTextGeneration.test.ts index 8165683f62cf02a8f28884cd82bb5f2b9b1e3744..80f5c236259de564cdc350351d0fb19510442c5c 100644 --- a/apps/server/src/textGeneration/GrokTextGeneration.test.ts +++ b/apps/server/src/textGeneration/GrokTextGeneration.test.ts @@ -11,11 +11,12 @@ import * as Layer from "effect/Layer"; import * as Schema from "effect/Schema"; import { createModelSelection } from "@t3tools/shared/model"; import { expect } from "vite-plus/test"; -import { GrokSettings, ProviderInstanceId } from "@t3tools/contracts"; +import { ProviderInstanceId } from "@t3tools/contracts"; +import { GrokSettings } from "@t3tools/provider-grok/settings"; import * as ServerConfig from "../config.ts"; import * as TextGeneration from "./TextGeneration.ts"; -import { makeGrokTextGeneration } from "./GrokTextGeneration.ts"; +import { makeGrokTextGeneration } from "@t3tools/provider-grok/testing"; import { execScriptSource, writeFakeCli } from "@t3tools/provider-testing/fakeCli"; const decodeGrokSettings = Schema.decodeSync(GrokSettings); diff --git a/apps/web/package.json b/apps/web/package.json index 64012d9bdab346789d7677e19a99767157eae830..5e9c5e1e97852aaf70f268a537f82773d68d7c04 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -30,6 +30,7 @@ "@t3tools/contracts": "workspace:*", "@t3tools/provider-core": "workspace:*", "@t3tools/provider-cursor": "workspace:*", + "@t3tools/provider-grok": "workspace:*", "@t3tools/provider-muse": "workspace:*", "@t3tools/provider-opencode": "workspace:*", "@t3tools/provider-pi": "workspace:*", diff --git a/apps/web/src/components/Icons.tsx b/apps/web/src/components/Icons.tsx index be008bac333089870dd2640de1099c4155b2f2d1..3e4d53d55b63f2ae584045ba5db8d3e08ad4e86f 100644 --- a/apps/web/src/components/Icons.tsx +++ b/apps/web/src/components/Icons.tsx @@ -364,18 +364,6 @@ export const CursorIcon: Icon = ({ className, ...props }) => ( ); -export const GrokIcon: Icon = ({ className, ...props }) => ( - - - - -); - export const TraeIcon: Icon = (props) => ( {/* Back rectangle: left strip + bottom strip drawn separately — empty bottom-left corner is the gap between them */} diff --git a/apps/web/src/components/chat/ProviderInstanceIcon.tsx b/apps/web/src/components/chat/ProviderInstanceIcon.tsx index 927f78ceb4f279df0dd0416019543227afd5b551..13322e19bef83f6b49c59da04ad18ec85836735e 100644 --- a/apps/web/src/components/chat/ProviderInstanceIcon.tsx +++ b/apps/web/src/components/chat/ProviderInstanceIcon.tsx @@ -3,7 +3,7 @@ import { type CSSProperties, memo } from "react"; import { providerInstanceInitials } from "@t3tools/client-runtime/state/provider-instance-display"; import { ProviderDriverKind } from "@t3tools/contracts"; -import { AntigravityIcon, ClaudeAI, GrokIcon, Icon, OpenAI } from "../Icons"; +import { AntigravityIcon, ClaudeAI, Icon, OpenAI } from "../Icons"; import { cn } from "~/lib/utils"; import { providerClients } from "../settings/providerDriverMeta"; @@ -17,14 +17,12 @@ import { const PROVIDER_ICON_BY_PROVIDER: Partial> = { [ProviderDriverKind.make("codex")]: OpenAI, [ProviderDriverKind.make("claudeAgent")]: ClaudeAI, - [ProviderDriverKind.make("grok")]: GrokIcon, [ProviderDriverKind.make("antigravity")]: AntigravityIcon, }; const PROVIDER_TEXT_COLOR_BY_PROVIDER: Partial> = { [ProviderDriverKind.make("codex")]: "text-black dark:text-white", [ProviderDriverKind.make("claudeAgent")]: "text-[#d97757]", - [ProviderDriverKind.make("grok")]: "text-[#0F0F0F] dark:text-[#F5F5F5]", [ProviderDriverKind.make("antigravity")]: "text-[#5b87bf]", }; diff --git a/apps/web/src/components/settings/providerDriverMeta.ts b/apps/web/src/components/settings/providerDriverMeta.ts index aca6ffbc724f21ae6b51d0a2f56552666dcecacf..e18a271f9278b38bd87428e37f727369086790d9 100644 --- a/apps/web/src/components/settings/providerDriverMeta.ts +++ b/apps/web/src/components/settings/providerDriverMeta.ts @@ -3,11 +3,11 @@ import { AntigravitySettings, ClaudeSettings, CodexSettings, - GrokSettings, ProviderDriverKind, } from "@t3tools/contracts"; import { makeProviderClientRegistry } from "@t3tools/provider-core/client"; import { cursorClient } from "@t3tools/provider-cursor/client"; +import { grokClient } from "@t3tools/provider-grok/client"; import { museClient } from "@t3tools/provider-muse/client"; import { openCodeClient } from "@t3tools/provider-opencode/client"; import { piClient } from "@t3tools/provider-pi/client"; @@ -25,11 +25,7 @@ export const providerClients = makeProviderClientRegistry([ settingsSchema: ClaudeSettings, }, cursorClient, - { - driverKind: ProviderDriverKind.make("grok"), - label: "Grok", - settingsSchema: GrokSettings, - }, + grokClient, openCodeClient, { driverKind: ProviderDriverKind.make("antigravity"), diff --git a/docs/internals/adding-a-provider.md b/docs/internals/adding-a-provider.md index 518e3ea05b23ff6f9124b455927dc5952b45d39c..3df65eaf047a6b55b6c14835f7c72d815f7dbffd 100644 --- a/docs/internals/adding-a-provider.md +++ b/docs/internals/adding-a-provider.md @@ -26,7 +26,7 @@ capabilities, never the driver kind. falls back, for example to portable context handoff for forks. A capability left on that fails at runtime is a bug. - **Permission modes.** Offer only modes the provider enforces natively, through - `supportedRuntimeModes` in the provider presentation ([Grok](../../apps/server/src/provider/GrokProvider.ts) + `supportedRuntimeModes` in the provider presentation ([Grok](../../packages/provider-grok/src/server/status.ts) and [Pi](../../packages/provider-pi/src/server/status.ts) are examples). Do not imitate a missing mode by answering approvals in T3: T3's check is weaker than the agent's own enforcement. The server runs an unoffered stored mode as Supervised diff --git a/docs/internals/providers.md b/docs/internals/providers.md index 6de371a95084745b046d12595c48e1ca24a5a44a..5f9642c2db27dd6cfaf1a9ae7257bd41b8ce8814 100644 --- a/docs/internals/providers.md +++ b/docs/internals/providers.md @@ -52,7 +52,7 @@ and removal must respect those leases instead of replacing executables under a r ## Setup must not happen as a health-check side effect Opening a provider session can start MCP servers, run hooks, or launch a login browser. -[Grok probes](../../apps/server/src/provider/GrokProvider.ts) avoid authentication and +[Grok probes](../../packages/provider-grok/src/server/status.ts) avoid authentication and session creation for this reason. Antigravity likewise reserves authenticated catalog sessions for explicit setup or model refresh; background checks use initialization only. diff --git a/package.json b/package.json index f85d50c4a2268dda8ca075b4299b09528be9d9a6..df724bd09a289136ce73673c1b07f8abdeac45cc 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "tc": "vp run -r --concurrency-limit 2 typecheck", "lint": "vp lint --report-unused-disable-directives", "knip": "knip --preprocessor ./scripts/knip-schemas.ts", - "knip:check": "knip --include files,dependencies --no-config-hints && knip --workspace apps/server --workspace apps/desktop --workspace apps/web --workspace packages/client-runtime --workspace packages/contracts --workspace packages/effect-acp --workspace packages/effect-codex-app-server --workspace packages/shared --workspace packages/ssh --workspace packages/tailscale --workspace packages/provider-core --workspace packages/provider-acp --workspace packages/provider-testing --workspace packages/provider-pi --workspace packages/provider-muse --workspace packages/provider-opencode --workspace packages/provider-cursor --exports --preprocessor ./scripts/knip-schemas.ts --no-config-hints", + "knip:check": "knip --include files,dependencies --no-config-hints && knip --workspace apps/server --workspace apps/desktop --workspace apps/web --workspace packages/client-runtime --workspace packages/contracts --workspace packages/effect-acp --workspace packages/effect-codex-app-server --workspace packages/shared --workspace packages/ssh --workspace packages/tailscale --workspace packages/provider-core --workspace packages/provider-acp --workspace packages/provider-testing --workspace packages/provider-pi --workspace packages/provider-muse --workspace packages/provider-opencode --workspace packages/provider-cursor --workspace packages/provider-grok --exports --preprocessor ./scripts/knip-schemas.ts --no-config-hints", "knip:production": "knip --production --preprocessor ./scripts/knip-schemas.ts", "lint:mobile": "node scripts/mobile-native-static-check.ts", "test": "vp run -r test", diff --git a/packages/contracts/src/settings.ts b/packages/contracts/src/settings.ts index c43df190af3f724b9d934c7d6966f485ee72bcb8..bd8af77a4ea15dab19fdcfa8a6e2b258be10522e 100644 --- a/packages/contracts/src/settings.ts +++ b/packages/contracts/src/settings.ts @@ -707,32 +707,6 @@ export const ClaudeSettings = makeProviderSettingsSchema( ); export type ClaudeSettings = typeof ClaudeSettings.Type; -export const GrokSettings = makeProviderSettingsSchema( - { - // Off by default (like Cursor and OpenCode): the binding is not yet - // stable enough to probe on every install. Users opt in from Settings. - enabled: Schema.Boolean.pipe( - Schema.withDecodingDefault(Effect.succeed(false)), - Schema.annotateKey({ providerSettingsForm: { hidden: true } }), - ), - binaryPath: makeBinaryPathSetting("grok").pipe( - Schema.annotateKey({ - title: "Binary path", - description: "Path to the Grok CLI binary.", - providerSettingsForm: { placeholder: "grok", clearWhenEmpty: "omit" }, - }), - ), - customModels: Schema.Array(CustomModelSetting).pipe( - Schema.withDecodingDefault(Effect.succeed([])), - Schema.annotateKey({ providerSettingsForm: { hidden: true } }), - ), - }, - { - order: ["binaryPath"], - }, -); -export type GrokSettings = typeof GrokSettings.Type; - /** * Antigravity ACP auth methods. Personal and Enterprise open a Google sign-in * in the browser. The API key and Agent Platform methods take credentials from diff --git a/packages/provider-grok/package.json b/packages/provider-grok/package.json new file mode 100644 index 0000000000000000000000000000000000000000..f72b76a11dfefcdcb8125f70a4b3140af2ff09f7 --- /dev/null +++ b/packages/provider-grok/package.json @@ -0,0 +1,42 @@ +{ + "name": "@t3tools/provider-grok", + "private": true, + "type": "module", + "exports": { + "./client": { + "types": "./src/client.ts", + "import": "./src/client.ts" + }, + "./settings": { + "types": "./src/settings.ts", + "import": "./src/settings.ts" + }, + "./server": { + "types": "./src/server.ts", + "import": "./src/server.ts" + }, + "./testing": { + "types": "./src/testing.ts", + "import": "./src/testing.ts" + } + }, + "scripts": { + "typecheck": "tsc --noEmit", + "test": "vp test run --config ../../vite.config.ts --dir ." + }, + "dependencies": { + "@t3tools/contracts": "workspace:*", + "@t3tools/provider-acp": "workspace:*", + "@t3tools/provider-core": "workspace:*", + "@t3tools/shared": "workspace:*", + "effect": "catalog:" + }, + "devDependencies": { + "@effect/platform-node": "catalog:", + "@effect/vitest": "catalog:", + "@t3tools/provider-testing": "workspace:*", + "@types/node": "catalog:", + "effect-acp": "workspace:*", + "vite-plus": "catalog:" + } +} diff --git a/packages/provider-grok/src/client.ts b/packages/provider-grok/src/client.ts new file mode 100644 index 0000000000000000000000000000000000000000..d2ff95424f8c933afc95487396d0b90491492731 --- /dev/null +++ b/packages/provider-grok/src/client.ts @@ -0,0 +1,28 @@ +/** + * Grok's client definition: settings form, label, and glyph. + * Browser- and React Native-safe. + * + * @module provider-grok/client + */ +import { ProviderDriverKind } from "@t3tools/contracts"; +import { defineProviderClient } from "@t3tools/provider-core/client"; + +import { GrokSettings } from "./settings.ts"; + +export const grokClient = defineProviderClient({ + driverKind: ProviderDriverKind.make("grok"), + label: "Grok", + settingsSchema: GrokSettings, + icon: { + viewBox: "0 0 24 24", + fill: { light: "#0F0F0F", dark: "#F5F5F5" }, + paths: [ + { + d: "M9.26905 15.284L17.2479 9.36086C17.6391 9.07047 18.1981 9.18374 18.3845 9.63478C19.3655 12.0135 18.9272 14.8721 16.9755 16.8349C15.0238 18.7976 12.3082 19.228 9.8261 18.2477L7.1146 19.5102C11.0037 22.1834 15.7263 21.5223 18.6774 18.5525C21.0182 16.1985 21.7432 12.9897 21.0653 10.0961L21.0714 10.1023C20.0884 5.85143 21.3131 4.15233 23.8218 0.677913C23.8812 0.595532 23.9406 0.513151 24 0.428711L20.6987 3.74866V3.73836L9.267 15.2861", + }, + { + d: "M7.62249 16.7237C4.83113 14.0422 5.3124 9.89222 7.69417 7.49905C9.45541 5.72786 12.341 5.00497 14.86 6.06768L17.5653 4.81138C17.0779 4.45714 16.4533 4.07613 15.7365 3.80839C12.4966 2.46764 8.6178 3.13492 5.98413 5.78141C3.45081 8.32904 2.65415 12.2463 4.02219 15.5889C5.04412 18.0871 3.36889 19.8541 1.68137 21.6377C1.08337 22.2699 0.483318 22.9022 0 23.5716L7.62045 16.7257", + }, + ], + }, +}); diff --git a/packages/provider-grok/src/server.ts b/packages/provider-grok/src/server.ts new file mode 100644 index 0000000000000000000000000000000000000000..4fe180316a8413cf1c1b3c5a22da4bdb4ed94646 --- /dev/null +++ b/packages/provider-grok/src/server.ts @@ -0,0 +1,7 @@ +/** + * Grok's server entry: the driver and adapter driver the server registers. + * + * @module provider-grok/server + */ +export { GrokDriver, type GrokDriverEnv } from "./server/driver.ts"; +export { GrokAdapterV2Driver, type GrokAdapterV2DriverEnv } from "./server/adapter.ts"; diff --git a/apps/server/src/provider/acp/GrokAcpCliProbe.test.ts b/packages/provider-grok/src/server/acpCliProbe.test.ts similarity index 98% rename from apps/server/src/provider/acp/GrokAcpCliProbe.test.ts rename to packages/provider-grok/src/server/acpCliProbe.test.ts index b5c8177d61f0f07aebb38cfc25da1a978042cac9..aa1a7704675825a4ed12565acd3445952bec78d9 100644 --- a/apps/server/src/provider/acp/GrokAcpCliProbe.test.ts +++ b/packages/provider-grok/src/server/acpCliProbe.test.ts @@ -18,7 +18,7 @@ import * as Stream from "effect/Stream"; import { ChildProcessSpawner } from "effect/process"; import { describe, expect } from "vite-plus/test"; -import { makeGrokAcpRuntime } from "./GrokAcpSupport.ts"; +import { makeGrokAcpRuntime } from "./acpSupport.ts"; const makeProbeRuntime = Effect.gen(function* () { const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; diff --git a/apps/server/src/provider/acp/GrokAcpSupport.test.ts b/packages/provider-grok/src/server/acpSupport.test.ts similarity index 99% rename from apps/server/src/provider/acp/GrokAcpSupport.test.ts rename to packages/provider-grok/src/server/acpSupport.test.ts index d15b12fbc2dad20a1392b4cd03cd2f323749862c..e49f29566a2b12bca1b3cfe28b1f289994ecbe11 100644 --- a/apps/server/src/provider/acp/GrokAcpSupport.test.ts +++ b/packages/provider-grok/src/server/acpSupport.test.ts @@ -10,7 +10,7 @@ import { grokAcpSpawnArgs, isValidGrokReasoningEffortToken, resolveGrokAcpBaseModelId, -} from "./GrokAcpSupport.ts"; +} from "./acpSupport.ts"; describe("grokAcpRuntimeProcessOwnership", () => { it("opts Grok into detached process-tree ownership on the injected host platform", () => { diff --git a/apps/server/src/provider/acp/GrokAcpSupport.ts b/packages/provider-grok/src/server/acpSupport.ts similarity index 98% rename from apps/server/src/provider/acp/GrokAcpSupport.ts rename to packages/provider-grok/src/server/acpSupport.ts index 57b4c8fb9d421145613af2f2041ad4f1142960fb..e30b475219ef64934125b946e91332481ef1799a 100644 --- a/apps/server/src/provider/acp/GrokAcpSupport.ts +++ b/packages/provider-grok/src/server/acpSupport.ts @@ -1,11 +1,11 @@ import type * as EffectAcpSchema from "effect-acp/compat"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { - type GrokSettings, type ProviderApprovalOption, ProviderDriverKind, type RuntimeMode, } from "@t3tools/contracts"; +import type { GrokSettings } from "../settings.ts"; import * as Crypto from "effect/Crypto"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; @@ -16,7 +16,7 @@ import { HostProcessPlatform } from "@t3tools/shared/hostProcess"; import { normalizeModelSlug } from "@t3tools/shared/model"; import * as AcpSessionRuntime from "@t3tools/provider-acp/server/AcpSessionRuntime"; -import { makeXAiPromptCompletionRuntime } from "./XAiAcpExtension.ts"; +import { makeXAiPromptCompletionRuntime } from "./xaiAcpExtension.ts"; const GROK_API_KEY_ENV = "XAI_API_KEY"; const GROK_OAUTH2_REFERRER_ENV = "GROK_OAUTH2_REFERRER"; diff --git a/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.ts b/packages/provider-grok/src/server/adapter.ts similarity index 98% rename from apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.ts rename to packages/provider-grok/src/server/adapter.ts index e44c2da421a9e39b29d164922be16b033c293180..757e27bceef5e2c8fb017d2e13900811d2e4fb9a 100644 --- a/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.ts +++ b/packages/provider-grok/src/server/adapter.ts @@ -3,16 +3,16 @@ import { XAiPromptFailureText, isXAiTaskCompletedWakeNotification, xAiRateLimitedErrorCode, -} from "../../provider/acp/XAiAcpExtension.ts"; +} from "./xaiAcpExtension.ts"; import { HostProcessEnvironment, HostProcessPlatform } from "@t3tools/shared/hostProcess"; import { resolveSelfInvocation, type SelfInvocation } from "@t3tools/shared/nodeRuntime"; import { defaultInstanceIdForDriver, - GrokSettings, ProviderDriverKind, type OrchestrationV2ProviderCapabilities, type RuntimeMode, } from "@t3tools/contracts"; +import { GrokSettings } from "../settings.ts"; import * as Crypto from "effect/Crypto"; import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; @@ -31,7 +31,7 @@ import { grokApprovalOptions, makeGrokAcpRuntime, resolveGrokAcpBaseModelId, -} from "../../provider/acp/GrokAcpSupport.ts"; +} from "./acpSupport.ts"; import { extractGrokPlanMarkdownFromToolCallData, extractXAiAcpBackgroundToolMutation, @@ -52,11 +52,11 @@ import { registerXAiBackgroundTaskTracking, XAiAskUserQuestionRequest, XAiExitPlanModeRequest, -} from "../../provider/acp/XAiAcpExtension.ts"; +} from "./xaiAcpExtension.ts"; import { mergeProviderInstanceEnvironment } from "@t3tools/provider-core/server/instanceEnvironment"; import { acpPermissionDisposition } from "@t3tools/provider-acp/server/clientPolicy"; import * as AcpSessionRuntime from "@t3tools/provider-acp/server/AcpSessionRuntime"; -import * as ProviderEventLoggers from "../../provider/ProviderEventLoggers.ts"; +import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests"; import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; diff --git a/apps/server/src/provider/Drivers/GrokDriver.test.ts b/packages/provider-grok/src/server/driver.test.ts similarity index 80% rename from apps/server/src/provider/Drivers/GrokDriver.test.ts rename to packages/provider-grok/src/server/driver.test.ts index e0da3203902c1eea50c1de3f69b9661f31ba883f..1885a11d22e610f1b0d5b4f19c98f3d2204eb55e 100644 --- a/apps/server/src/provider/Drivers/GrokDriver.test.ts +++ b/packages/provider-grok/src/server/driver.test.ts @@ -9,27 +9,15 @@ import * as Path from "effect/Path"; import { HttpClient } from "effect/http"; import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; -import * as BackgroundPolicy from "../../background/BackgroundPolicy.ts"; -import * as ServerConfig from "../../config.ts"; -import * as ServerSettings from "../../serverSettings.ts"; -import * as ProviderEventLoggers from "../ProviderEventLoggers.ts"; -import { GrokDriver } from "./GrokDriver.ts"; +import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; +import { layerTestProviderHost } from "@t3tools/provider-testing/host"; +import { GrokDriver } from "./driver.ts"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; -import * as ProviderHostLive from "../ProviderHostLive.ts"; -import * as ServerSecretStore from "../../auth/ServerSecretStore.ts"; -const layerDeps = ServerConfig.layerTest(process.cwd(), { - prefix: "t3-grok-driver-update-", -}).pipe( +const layerTest = layerTestProviderHost({ runBackgroundWork: false }).pipe( Layer.provideMerge(NodeServices.layer), Layer.provideMerge(IdAllocator.layer), - Layer.provideMerge(ServerSettings.layerTest()), - Layer.provideMerge( - Layer.mock(BackgroundPolicy.BackgroundPolicy)({ - shouldRunScopeWork: () => Effect.succeed(false), - }), - ), Layer.provideMerge( Layer.succeed( ProviderEventLoggers.ProviderEventLoggers, @@ -43,10 +31,6 @@ const layerDeps = ServerConfig.layerTest(process.cwd(), { ), ), ); -const layerTest = ProviderHostLive.layer.pipe( - Layer.provideMerge(ServerSecretStore.layer), - Layer.provideMerge(layerDeps), -); const noSpawner = ChildProcessSpawner.make(() => Effect.die("Disabled Grok must not spawn a process"), diff --git a/apps/server/src/provider/Drivers/GrokDriver.ts b/packages/provider-grok/src/server/driver.ts similarity index 90% rename from apps/server/src/provider/Drivers/GrokDriver.ts rename to packages/provider-grok/src/server/driver.ts index f7226d5f1e9fb1b85016fa41c41cdec1d9fef0e2..e3201d0f03c306669968c7ba2b89b5715af0a0b9 100644 --- a/apps/server/src/provider/Drivers/GrokDriver.ts +++ b/packages/provider-grok/src/server/driver.ts @@ -1,4 +1,5 @@ -import { GrokSettings, ProviderDriverKind } from "@t3tools/contracts"; +import { ProviderDriverKind } from "@t3tools/contracts"; +import { GrokSettings } from "../settings.ts"; import * as Crypto from "effect/Crypto"; import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; @@ -7,21 +8,17 @@ import * as Schema from "effect/Schema"; import { HttpClient } from "effect/http"; import { ChildProcessSpawner } from "effect/process"; -import { ProviderHost } from "@t3tools/provider-core/server/ProviderHost"; -import * as ServerConfig from "../../config.ts"; -import * as ProviderEventLoggers from "../ProviderEventLoggers.ts"; -import { makeGrokTextGeneration } from "../../textGeneration/GrokTextGeneration.ts"; -import { - GrokAdapterV2Driver, - type GrokAdapterV2DriverEnv, -} from "../../orchestration-v2/Adapters/GrokAdapterV2.ts"; -import { ProviderDriverError } from "../Errors.ts"; +import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; +import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; +import { makeGrokTextGeneration } from "./textGeneration.ts"; +import { GrokAdapterV2Driver, type GrokAdapterV2DriverEnv } from "./adapter.ts"; +import { ProviderDriverError } from "@t3tools/provider-core/server/errors"; import { buildInitialGrokProviderSnapshot, checkGrokProviderStatus, enrichGrokSnapshot, -} from "../GrokProvider.ts"; -import { readGrokAccount } from "../grokUsageLimits.ts"; +} from "./status.ts"; +import { readGrokAccount } from "./usageLimits.ts"; import { makeManagedServerProvider } from "@t3tools/provider-core/server/managedProvider"; import { defaultProviderContinuationIdentity, @@ -30,7 +27,7 @@ import { } from "@t3tools/provider-core/server/driver"; import { withInstanceIdentity } from "@t3tools/provider-core/server/instanceIdentity"; import { mergeProviderInstanceEnvironment } from "@t3tools/provider-core/server/instanceEnvironment"; -import { discoverGrokSkills } from "./GrokSkills.ts"; +import { discoverGrokSkills } from "./skills.ts"; import { makeCachedProviderMaintenanceResolution, makeManualOnlyProviderMaintenanceCapabilities, @@ -75,14 +72,13 @@ const UPDATE: ProviderMaintenanceCapabilitiesResolver = { export type GrokDriverEnv = | GrokAdapterV2DriverEnv - | ProviderHost + | ProviderHost.ProviderHost | ChildProcessSpawner.ChildProcessSpawner | Crypto.Crypto | FileSystem.FileSystem | HttpClient.HttpClient | Path.Path - | ProviderEventLoggers.ProviderEventLoggers - | ServerConfig.ServerConfig; + | ProviderEventLoggers.ProviderEventLoggers; export const GrokDriver: ProviderDriver = { driverKind: DRIVER_KIND, @@ -99,8 +95,8 @@ export const GrokDriver: ProviderDriver = { const httpClient = yield* HttpClient.HttpClient; const fileSystem = yield* FileSystem.FileSystem; const path = yield* Path.Path; - const host = yield* ProviderHost; - const { cwd } = yield* ServerConfig.ServerConfig; + const host = yield* ProviderHost.ProviderHost; + const { cwd } = host.paths; const processEnv = mergeProviderInstanceEnvironment(environment); const continuationIdentity = defaultProviderContinuationIdentity({ driverKind: DRIVER_KIND, diff --git a/apps/server/src/provider/Drivers/GrokSkills.test.ts b/packages/provider-grok/src/server/skills.test.ts similarity index 99% rename from apps/server/src/provider/Drivers/GrokSkills.test.ts rename to packages/provider-grok/src/server/skills.test.ts index 6f194644617f2e07671e06f8fec44fa5cf6c06f5..e05d377e71a6d1d20344807237437aaa2b05e203 100644 --- a/apps/server/src/provider/Drivers/GrokSkills.test.ts +++ b/packages/provider-grok/src/server/skills.test.ts @@ -4,7 +4,7 @@ import * as Sink from "effect/Sink"; import * as Stream from "effect/Stream"; import { ChildProcessSpawner } from "effect/process"; -import { discoverGrokSkills } from "./GrokSkills.ts"; +import { discoverGrokSkills } from "./skills.ts"; const inspectPayload = (skills: ReadonlyArray) => JSON.stringify({ skills }); diff --git a/apps/server/src/provider/Drivers/GrokSkills.ts b/packages/provider-grok/src/server/skills.ts similarity index 97% rename from apps/server/src/provider/Drivers/GrokSkills.ts rename to packages/provider-grok/src/server/skills.ts index f552b0f4b2bcc676a163ba6ea13bca2d89005d7e..1a65c794357caf09ff9ec48aa785d7a4db90eb9a 100644 --- a/apps/server/src/provider/Drivers/GrokSkills.ts +++ b/packages/provider-grok/src/server/skills.ts @@ -15,7 +15,8 @@ * * @module provider/Drivers/GrokSkills */ -import type { GrokSettings, ServerProviderSkill } from "@t3tools/contracts"; +import type { ServerProviderSkill } from "@t3tools/contracts"; +import type { GrokSettings } from "../settings.ts"; import * as Effect from "effect/Effect"; import * as Option from "effect/Option"; import * as Schema from "effect/Schema"; diff --git a/apps/server/src/provider/GrokProvider.ts b/packages/provider-grok/src/server/status.ts similarity index 99% rename from apps/server/src/provider/GrokProvider.ts rename to packages/provider-grok/src/server/status.ts index 2899583a20c3df03095b72c8069b5a95745218c2..0d8dc59b0ddc7530f955bc9376a65435c4c63e2f 100644 --- a/apps/server/src/provider/GrokProvider.ts +++ b/packages/provider-grok/src/server/status.ts @@ -1,12 +1,12 @@ import { type CustomModelSetting, - type GrokSettings, type ModelCapabilities, type ServerProvider, type ServerProviderAuth, type ServerProviderModel, type ServerProviderSlashCommand, } from "@t3tools/contracts"; +import type { GrokSettings } from "../settings.ts"; import type * as EffectAcpSchema from "effect-acp/compat"; import { causeErrorTag } from "@t3tools/shared/observability"; import * as Crypto from "effect/Crypto"; @@ -41,9 +41,9 @@ import { isValidGrokReasoningEffortToken, makeGrokAcpRuntime, resolveGrokAcpBaseModelId, -} from "./acp/GrokAcpSupport.ts"; +} from "./acpSupport.ts"; import { sessionModelStateFromInitialize } from "@t3tools/provider-acp/server/runtimeModel"; -import { discoverGrokSkills } from "./Drivers/GrokSkills.ts"; +import { discoverGrokSkills } from "./skills.ts"; const GROK_PRESENTATION = { displayName: "Grok", diff --git a/apps/server/src/textGeneration/GrokTextGeneration.ts b/packages/provider-grok/src/server/textGeneration.ts similarity index 97% rename from apps/server/src/textGeneration/GrokTextGeneration.ts rename to packages/provider-grok/src/server/textGeneration.ts index 6e14fa101ee278d244e38be792603b3f99e154f7..b908c7e6e00ce15dac2fdfde7bbf6393fff29e14 100644 --- a/apps/server/src/textGeneration/GrokTextGeneration.ts +++ b/packages/provider-grok/src/server/textGeneration.ts @@ -6,7 +6,8 @@ import * as Schema from "effect/Schema"; import { ChildProcessSpawner } from "effect/process"; import type * as EffectAcpErrors from "effect-acp/errors"; -import { type GrokSettings, TextGenerationError } from "@t3tools/contracts"; +import { TextGenerationError } from "@t3tools/contracts"; +import type { GrokSettings } from "../settings.ts"; import { getModelSelectionStringOptionValue } from "@t3tools/shared/model"; import * as TextGenerationOperations from "@t3tools/provider-core/server/textGenerationOperations"; @@ -16,7 +17,7 @@ import { currentGrokReasoningEffortFromSessionSetup, makeGrokAcpRuntime, resolveGrokAcpBaseModelId, -} from "../provider/acp/GrokAcpSupport.ts"; +} from "./acpSupport.ts"; const GROK_TIMEOUT_MS = 180_000; diff --git a/apps/server/src/provider/grokUsageLimits.ts b/packages/provider-grok/src/server/usageLimits.ts similarity index 100% rename from apps/server/src/provider/grokUsageLimits.ts rename to packages/provider-grok/src/server/usageLimits.ts diff --git a/apps/server/src/provider/acp/XAiAcpExtension.test.ts b/packages/provider-grok/src/server/xaiAcpExtension.test.ts similarity index 99% rename from apps/server/src/provider/acp/XAiAcpExtension.test.ts rename to packages/provider-grok/src/server/xaiAcpExtension.test.ts index 84e5511785b40de5c52abd1b1d32fdcfadb174eb..5dce41edead95495072b465df1fad3db4da54673 100644 --- a/apps/server/src/provider/acp/XAiAcpExtension.test.ts +++ b/packages/provider-grok/src/server/xaiAcpExtension.test.ts @@ -33,7 +33,7 @@ import { xAiPromptCompleteFromSessionUpdate, xAiSubagentFinishedNotice, XAiAskUserQuestionRequest, -} from "./XAiAcpExtension.ts"; +} from "./xaiAcpExtension.ts"; import * as AcpSessionRuntime from "@t3tools/provider-acp/server/AcpSessionRuntime"; import { type AcpToolCallState, diff --git a/apps/server/src/provider/acp/XAiAcpExtension.ts b/packages/provider-grok/src/server/xaiAcpExtension.ts similarity index 100% rename from apps/server/src/provider/acp/XAiAcpExtension.ts rename to packages/provider-grok/src/server/xaiAcpExtension.ts diff --git a/apps/server/src/provider/acp/XAiBackgroundTasks.test.ts b/packages/provider-grok/src/server/xaiBackgroundTasks.test.ts similarity index 99% rename from apps/server/src/provider/acp/XAiBackgroundTasks.test.ts rename to packages/provider-grok/src/server/xaiBackgroundTasks.test.ts index 93227b260481a58a7b23a8c80135d2551ea661b6..34c80e761d405d931d8967c36099f6cff1acacf3 100644 --- a/apps/server/src/provider/acp/XAiBackgroundTasks.test.ts +++ b/packages/provider-grok/src/server/xaiBackgroundTasks.test.ts @@ -4,7 +4,7 @@ import { TurnId } from "@t3tools/contracts"; import { buildGrokBackgroundTaskEvents, type GrokBackgroundTaskRecord, -} from "./XAiBackgroundTasks.ts"; +} from "./xaiBackgroundTasks.ts"; const turnId = TurnId.make("turn-1"); const monitor = { type: "Monitor", taskId: "monitor-1", timeoutMs: 60_000 }; diff --git a/apps/server/src/provider/acp/XAiBackgroundTasks.ts b/packages/provider-grok/src/server/xaiBackgroundTasks.ts similarity index 100% rename from apps/server/src/provider/acp/XAiBackgroundTasks.ts rename to packages/provider-grok/src/server/xaiBackgroundTasks.ts diff --git a/packages/provider-grok/src/settings.ts b/packages/provider-grok/src/settings.ts new file mode 100644 index 0000000000000000000000000000000000000000..ab26b2f2148d292c78174fd0e4acc834287af8d0 --- /dev/null +++ b/packages/provider-grok/src/settings.ts @@ -0,0 +1,39 @@ +/** + * Grok instance settings. Shared by the server driver and the client + * settings form, so it holds only browser-safe schema code. + * + * @module provider-grok/settings + */ +import { + CustomModelSetting, + makeBinaryPathSetting, + makeProviderSettingsSchema, +} from "@t3tools/contracts"; +import * as Effect from "effect/Effect"; +import * as Schema from "effect/Schema"; + +export const GrokSettings = makeProviderSettingsSchema( + { + // Off by default (like Cursor and OpenCode): the binding is not yet + // stable enough to probe on every install. Users opt in from Settings. + enabled: Schema.Boolean.pipe( + Schema.withDecodingDefault(Effect.succeed(false)), + Schema.annotateKey({ providerSettingsForm: { hidden: true } }), + ), + binaryPath: makeBinaryPathSetting("grok").pipe( + Schema.annotateKey({ + title: "Binary path", + description: "Path to the Grok CLI binary.", + providerSettingsForm: { placeholder: "grok", clearWhenEmpty: "omit" }, + }), + ), + customModels: Schema.Array(CustomModelSetting).pipe( + Schema.withDecodingDefault(Effect.succeed([])), + Schema.annotateKey({ providerSettingsForm: { hidden: true } }), + ), + }, + { + order: ["binaryPath"], + }, +); +export type GrokSettings = typeof GrokSettings.Type; diff --git a/packages/provider-grok/src/testing.ts b/packages/provider-grok/src/testing.ts new file mode 100644 index 0000000000000000000000000000000000000000..519d76a6d62de8094fd6b4dce5f661bead5826cc --- /dev/null +++ b/packages/provider-grok/src/testing.ts @@ -0,0 +1,38 @@ +/** + * Grok internals the server's replay testkit, fixture recorder, and + * orchestration tests drive directly. + * + * @module provider-grok/testing + */ +export { + GROK_DEFAULT_INSTANCE_ID, + GROK_PROVIDER, + GrokProviderCapabilitiesV2, + grokLaunchRuntimeMode, + makeGrokAcpAdapterFlavor, + makeGrokAdapterV2, + type GrokAdapterV2Options, +} from "./server/adapter.ts"; +export { + GROK_ACP_CANCEL_META, + GROK_ACP_INITIALIZE_META, + makeGrokAcpRuntime, +} from "./server/acpSupport.ts"; +export { + buildGrokModelCapabilities, + buildGrokModelsFromSessionModelState, + buildInitialGrokProviderSnapshot, + checkGrokProviderStatus, + grokSlashCommandsFromInitialize, + parseGrokModelsCliOutput, +} from "./server/status.ts"; +export { makeGrokTextGeneration } from "./server/textGeneration.ts"; +export { grokUsageResponseToLimits, readGrokAccount } from "./server/usageLimits.ts"; +export { + extractXAiAcpSubagentEndNotice, + extractXAiAcpSubagentUpdate, + makeXAiPromptCompletionRuntime, + normalizeXAiAcpToolCallState, + registerXAiBackgroundTaskTracking, + xAiRateLimitedErrorCode, +} from "./server/xaiAcpExtension.ts"; diff --git a/packages/provider-grok/tsconfig.json b/packages/provider-grok/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..374bac55202ddcad695ede1c567ec219a61e9c48 --- /dev/null +++ b/packages/provider-grok/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "types": ["node"] + }, + "include": ["src"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d0b0d970c12bd65948f79eb89f21724c066c975b..a965df78a8243ff1c4fb79748fada9c67ea4e341 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -359,6 +359,9 @@ importers: '@t3tools/provider-cursor': specifier: workspace:* version: link:../../packages/provider-cursor + '@t3tools/provider-grok': + specifier: workspace:* + version: link:../../packages/provider-grok '@t3tools/provider-muse': specifier: workspace:* version: link:../../packages/provider-muse @@ -609,6 +612,9 @@ importers: '@t3tools/provider-cursor': specifier: workspace:* version: link:../../packages/provider-cursor + '@t3tools/provider-grok': + specifier: workspace:* + version: link:../../packages/provider-grok '@t3tools/provider-muse': specifier: workspace:* version: link:../../packages/provider-muse @@ -748,6 +754,9 @@ importers: '@t3tools/provider-cursor': specifier: workspace:* version: link:../../packages/provider-cursor + '@t3tools/provider-grok': + specifier: workspace:* + version: link:../../packages/provider-grok '@t3tools/provider-muse': specifier: workspace:* version: link:../../packages/provider-muse @@ -1194,6 +1203,43 @@ importers: specifier: 1.0.0 version: 1.0.0(@types/node@24.12.4)(@vitest/ui@5.0.1(vitest@5.0.1))(bufferutil@4.1.0)(esbuild@0.28.2)(jiti@2.7.0)(jsdom@30.1.0(@noble/hashes@1.8.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) + packages/provider-grok: + dependencies: + '@t3tools/contracts': + specifier: workspace:* + version: link:../contracts + '@t3tools/provider-acp': + specifier: workspace:* + version: link:../provider-acp + '@t3tools/provider-core': + specifier: workspace:* + version: link:../provider-core + '@t3tools/shared': + specifier: workspace:* + version: link:../shared + effect: + specifier: 4.0.1 + version: 4.0.1(patch_hash=a33cba07c41f32374c2aaa86ea4a84d3ab9a872a78c94b65e929b19f8361856f) + devDependencies: + '@effect/platform-node': + specifier: 4.0.1 + version: 4.0.1(bufferutil@4.1.0)(effect@4.0.1(patch_hash=a33cba07c41f32374c2aaa86ea4a84d3ab9a872a78c94b65e929b19f8361856f))(redis@6.2.1)(utf-8-validate@6.0.6) + '@effect/vitest': + specifier: 4.0.1 + version: 4.0.1(patch_hash=359f6fb2f7b3ec145bb72208edb9034f02489791aa2491a55cdbd69bd56ee0d2)(@types/node@24.12.4)(@vitest/ui@5.0.1)(bufferutil@4.1.0)(effect@4.0.1(patch_hash=a33cba07c41f32374c2aaa86ea4a84d3ab9a872a78c94b65e929b19f8361856f))(esbuild@0.28.2)(jiti@2.7.0)(jsdom@30.1.0(@noble/hashes@1.8.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) + '@t3tools/provider-testing': + specifier: workspace:* + version: link:../provider-testing + '@types/node': + specifier: 24.12.4 + version: 24.12.4 + effect-acp: + specifier: workspace:* + version: link:../effect-acp + vite-plus: + specifier: 1.0.0 + version: 1.0.0(@types/node@24.12.4)(@vitest/ui@5.0.1(vitest@5.0.1))(bufferutil@4.1.0)(esbuild@0.28.2)(jiti@2.7.0)(jsdom@30.1.0(@noble/hashes@1.8.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) + packages/provider-muse: dependencies: '@muse-code/sdk': diff --git a/scripts/release-smoke.ts b/scripts/release-smoke.ts index 40dfbc7c8d3cf62b23403640f992a3c9a3299083..3eabff5674fe25e94b1f30afce2aa22f15eceeff 100644 --- a/scripts/release-smoke.ts +++ b/scripts/release-smoke.ts @@ -33,6 +33,7 @@ const workspaceFiles = [ "packages/provider-pi/package.json", "packages/provider-acp/package.json", "packages/provider-cursor/package.json", + "packages/provider-grok/package.json", "packages/provider-muse/package.json", "packages/provider-opencode/package.json", "packages/effect-acp/package.json", -- 2.54.0