FN-9008: add Grok 4.6 model catalog support
Register Grok 4.6 across Fusion's shared Grok provider catalog. - Add Grok 4.6 metadata and preserve it through provider merges - Cover registration and extension model-list behavior - Update Grok model references and publish a minor changeset Files changed: .changeset/fn-9008-grok-4-6.md | 7 +++ docs/grok-cli-contract.md | 4 +- docs/settings-reference.md | 2 +- packages/core/src/__tests__/grok-provider.test.ts | 54 +++++++++++++++++------ packages/core/src/ai/grok-provider.ts | 21 +++++++++ 5 files changed, 72 insertions(+), 16 deletions(-) Fusion-Task-Id: FN-9008 Fusion-Task-Lineage: 5ee7688e-a1eb-4b57-8f72-1f69616ff456 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
7
.changeset/fn-9008-grok-4-6.md
Normal file
7
.changeset/fn-9008-grok-4-6.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@runfusion/fusion": minor
|
||||
---
|
||||
|
||||
summary: Add Grok 4.6 to the built-in Grok model catalog across every model picker.
|
||||
category: feature
|
||||
dev: Registers Grok 4.6 in `GROK_PROVIDER_REGISTRATION` in `packages/core/src/ai/grok-provider.ts`, which fans out through `seedDashboardProviders` and `pi.ts`.
|
||||
@@ -35,7 +35,7 @@ Fusion's `GrokRuntimeAdapter` drives Grok as an ACP (Agent Client Protocol) agen
|
||||
# Official automation shape (docs.x.ai): suppress update checks in CI/scripts
|
||||
grok --no-auto-update agent stdio
|
||||
# with optional model + session skills plugin:
|
||||
grok --no-auto-update agent --plugin-dir <session-plugin> -m grok-4.5 stdio
|
||||
grok --no-auto-update agent --plugin-dir <session-plugin> -m grok-4.6 stdio
|
||||
```
|
||||
|
||||
ACP session lifecycle (official contract):
|
||||
@@ -77,7 +77,7 @@ mcpServers through createResolvedAgentSession. Grok ACP must not drop them.
|
||||
|
||||
When custom Fusion tools were requested but the bridge cannot start, the session emits `FUSION_TOOL_BRIDGE_FAILED: mcp-schema-server-missing` or `FUSION_TOOL_BRIDGE_FAILED: bridge-start-failed`. It deliberately omits the broken MCP server entry, and the engine records `fusionToolBridgeFailed`, its fixed reason code, and a requested-tool count on the ids-only `session:runtime-resolved` run-audit event. Paths, schemas, error prose, and credentials are not persisted there.
|
||||
|
||||
Grok ACP sessions store a string model plus `lastModelDescription`; lane markers normalize this to `grok/<model>` (for example `grok/grok-4.5`) before appending thinking metadata, never `undefined/undefined`.
|
||||
Grok ACP sessions store a string model plus `lastModelDescription`; lane markers normalize this to `grok/<model>` (for example `grok/grok-4.6`) before appending thinking metadata, never `undefined/undefined`.
|
||||
|
||||
### Session lifecycle
|
||||
|
||||
|
||||
@@ -1140,7 +1140,7 @@ When the planning lane has neither `planningFallback*` nor a global `fallback*`
|
||||
|
||||
Z.ai's built-in provider uses the existing `zai` auth entry / `ZAI_API_KEY` environment variable and includes `zai/glm-5.2` as a selectable model in the same dropdowns and workflow lane controls as the other built-in GLM models. If a pi extension also registers the `zai` provider, Fusion preserves the extension's models and re-adds any missing built-in Z.ai models so built-in GLM choices remain available.
|
||||
|
||||
Grok (`grok-cli`) is likewise seeded as a built-in provider — xAI's OpenAI-compatible endpoint (`https://api.x.ai/v1`, api type `openai-completions`), API key `GROK_API_KEY` — into every model registry Fusion seeds (task execution, dashboard `/api/models`, and CLI `serve`/`daemon`/`dashboard`), mirroring the Z.ai pattern above. This makes `grok-cli/<model>` selections (e.g. `grok-cli/grok-4.5`) resolvable for execution even before the `grok` CLI binary is discovered or the picker surfaces additional Grok models (see the CLI-discovery paragraph below). If `GROK_API_KEY` is not set in the environment, provider registration falls back to `~/.grok/user-settings.json`'s `apiKey` field (the same file the `grok` CLI itself writes on login) and hydrates `process.env.GROK_API_KEY` from it, so an operator who authenticated via the `grok` CLI but never exported the env var still resolves a key; an already-set env var always wins, and a missing/malformed/empty settings file is fail-soft (no error, no env mutation). When no Fusion-visible key resolves and the Grok Runtime plugin has registered runtime id `grok`, `createResolvedAgentSession()` derives that runtime automatically for `grok-cli` execution and passes the selected model to the CLI via `--model <id>`; explicit runtime hints and key-visible direct-endpoint routing take precedence.
|
||||
Grok (`grok-cli`) is likewise seeded as a built-in provider — xAI's OpenAI-compatible endpoint (`https://api.x.ai/v1`, api type `openai-completions`), API key `GROK_API_KEY` — into every model registry Fusion seeds (task execution, dashboard `/api/models`, and CLI `serve`/`daemon`/`dashboard`), mirroring the Z.ai pattern above. This makes `grok-cli/<model>` selections (e.g. `grok-cli/grok-4.6`) resolvable for execution even before the `grok` CLI binary is discovered or the picker surfaces additional Grok models (see the CLI-discovery paragraph below). If `GROK_API_KEY` is not set in the environment, provider registration falls back to `~/.grok/user-settings.json`'s `apiKey` field (the same file the `grok` CLI itself writes on login) and hydrates `process.env.GROK_API_KEY` from it, so an operator who authenticated via the `grok` CLI but never exported the env var still resolves a key; an already-set env var always wins, and a missing/malformed/empty settings file is fail-soft (no error, no env mutation). When no Fusion-visible key resolves and the Grok Runtime plugin has registered runtime id `grok`, `createResolvedAgentSession()` derives that runtime automatically for `grok-cli` execution and passes the selected model to the CLI via `--model <id>`; explicit runtime hints and key-visible direct-endpoint routing take precedence.
|
||||
|
||||
When the Hermes Runtime plugin (`fusion-plugin-hermes-runtime`) is installed and the local `hermes` CLI has configured profiles (`hermes profile list`), those profiles are surfaced additively in `/api/models` under the `hermes` provider — one row per profile, id/name derived from the profile name and its configured model. This surfacing is read-only (Fusion does not create or edit Hermes profiles) and is fetched through a short-TTL, single-flight cache so the model picker never spawns the `hermes` CLI on every request; a missing/failed `hermes` binary simply yields zero Hermes rows without affecting other providers.
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
} from "../ai/grok-provider.js";
|
||||
import { ZAI_PROVIDER_ID, ZAI_PROVIDER_REGISTRATION, registerBuiltInZaiProvider } from "../ai/zai-provider.js";
|
||||
|
||||
const EXPECTED_GROK_MODELS = ["grok-4.5", "grok-4", "grok-code-fast-1", "grok-3", "grok-3-mini"];
|
||||
const EXPECTED_GROK_MODELS = ["grok-4.6", "grok-4.5", "grok-4", "grok-code-fast-1", "grok-3", "grok-3-mini"];
|
||||
|
||||
describe("GROK_PROVIDER_REGISTRATION", () => {
|
||||
it("uses the xAI OpenAI-compatible endpoint and API-key auth", () => {
|
||||
@@ -26,6 +26,19 @@ describe("GROK_PROVIDER_REGISTRATION", () => {
|
||||
expect(modelIds).toContain("grok-4.5");
|
||||
});
|
||||
|
||||
it("registers Grok 4.6 with its compatible model shape", () => {
|
||||
const grok46 = GROK_PROVIDER_REGISTRATION.models.find((model) => model.id === "grok-4.6");
|
||||
|
||||
expect(grok46).toMatchObject({
|
||||
id: "grok-4.6",
|
||||
name: "Grok 4.6",
|
||||
reasoning: true,
|
||||
});
|
||||
expect(grok46?.contextWindow).toBeGreaterThan(0);
|
||||
expect(grok46?.maxTokens).toBeGreaterThan(0);
|
||||
expect(grok46?.compat).not.toHaveProperty("thinkingFormat");
|
||||
});
|
||||
|
||||
it("does not copy Z.ai's thinkingFormat compat field", () => {
|
||||
for (const model of GROK_PROVIDER_REGISTRATION.models) {
|
||||
expect(model.compat).not.toHaveProperty("thinkingFormat");
|
||||
@@ -87,9 +100,21 @@ describe("registerBuiltInGrokProvider", () => {
|
||||
|
||||
describe("mergeBuiltInGrokProviderModels", () => {
|
||||
it("re-adds missing built-in models after a user grok extension replacement", () => {
|
||||
const extensionModels = GROK_PROVIDER_REGISTRATION.models
|
||||
.filter((model) => model.id !== "grok-4.5")
|
||||
.map((model) => ({ ...model }));
|
||||
const extensionModels = [
|
||||
...GROK_PROVIDER_REGISTRATION.models
|
||||
.filter((model) => model.id !== "grok-4.6")
|
||||
.map((model) => ({ ...model })),
|
||||
{
|
||||
id: "extension-grok-preview",
|
||||
name: "Extension Grok Preview",
|
||||
reasoning: false,
|
||||
input: ["text"] as const,
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||
contextWindow: 1024,
|
||||
maxTokens: 512,
|
||||
compat: { supportsDeveloperRole: false },
|
||||
},
|
||||
];
|
||||
const registeredProviders = new Map<string, Partial<typeof GROK_PROVIDER_REGISTRATION>>();
|
||||
const registry = {
|
||||
registeredProviders,
|
||||
@@ -109,22 +134,25 @@ describe("mergeBuiltInGrokProviderModels", () => {
|
||||
|
||||
const mergedIds = registeredProviders.get(GROK_CLI_PROVIDER_ID)?.models?.map((model) => model.id);
|
||||
expect(mergedIds).toEqual(expect.arrayContaining(EXPECTED_GROK_MODELS));
|
||||
expect(mergedIds).toContain("grok-4.5");
|
||||
expect(mergedIds).toContain("grok-4.6");
|
||||
expect(mergedIds).toContain("extension-grok-preview");
|
||||
expect(registeredProviders.get(GROK_CLI_PROVIDER_ID)?.name).toBe("User Grok extension");
|
||||
});
|
||||
|
||||
it("is a no-op when all built-in models are already present", () => {
|
||||
it("does not duplicate, reorder, or drop models when Grok 4.6 is already present", () => {
|
||||
const registeredProviders = new Map<string, Partial<typeof GROK_PROVIDER_REGISTRATION>>();
|
||||
const registry = {
|
||||
registeredProviders,
|
||||
registerProvider(providerName: string, config: typeof GROK_PROVIDER_REGISTRATION) {
|
||||
registeredProviders.set(providerName, { ...registeredProviders.get(providerName), ...config });
|
||||
},
|
||||
const registerProvider = (providerName: string, config: typeof GROK_PROVIDER_REGISTRATION) => {
|
||||
registeredProviders.set(providerName, { ...registeredProviders.get(providerName), ...config });
|
||||
};
|
||||
const registry = { registeredProviders, registerProvider };
|
||||
|
||||
registerBuiltInGrokProvider(registry);
|
||||
const before = registeredProviders.get(GROK_CLI_PROVIDER_ID)?.models?.map((model) => model.id);
|
||||
expect(() => mergeBuiltInGrokProviderModels(registry)).not.toThrow();
|
||||
const mergedIds = registeredProviders.get(GROK_CLI_PROVIDER_ID)?.models?.map((model) => model.id);
|
||||
expect(mergedIds).toEqual(EXPECTED_GROK_MODELS);
|
||||
const after = registeredProviders.get(GROK_CLI_PROVIDER_ID)?.models?.map((model) => model.id);
|
||||
|
||||
expect(before).toEqual(EXPECTED_GROK_MODELS);
|
||||
expect(after).toEqual(before);
|
||||
expect(after?.filter((id) => id === "grok-4.6")).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -62,6 +62,27 @@ export const GROK_PROVIDER_REGISTRATION: GrokProviderRegistration = {
|
||||
apiKey: "$GROK_API_KEY",
|
||||
api: "openai-completions",
|
||||
models: [
|
||||
/*
|
||||
FNXC:ModelRegistry 2026-08-12-20:17:
|
||||
FN-9008 requires Grok 4.6 to be selectable in every model area. Keep it in this shared
|
||||
catalog because pi's registerProvider() replaces provider model lists wholesale, while every
|
||||
Fusion host seeds and re-merges this export. xAI's model docs verify grok-4.6, its 500k-token
|
||||
context window, and configurable reasoning; max output and per-model image input remain
|
||||
upstream-pending-verification, so retain the conservative text-only and 65536-token defaults.
|
||||
Source: https://docs.x.ai/developers/models
|
||||
*/
|
||||
{
|
||||
id: "grok-4.6",
|
||||
name: "Grok 4.6",
|
||||
reasoning: true,
|
||||
input: ["text"],
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||
contextWindow: 500000,
|
||||
maxTokens: 65536,
|
||||
compat: {
|
||||
supportsDeveloperRole: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "grok-4.5",
|
||||
name: "Grok 4.5",
|
||||
|
||||
Reference in New Issue
Block a user