FN-7759: fix GPT-5.6 codex models missing from model dropdown

Ensure GPT-5.6 codex models (luna, sol, terra) actually surface in the codex model picker by validating the openai-codex supplemental merge against the real pi-coding-agent ModelRegistry instead of only a mocked one.

- Harden openai-models.ts supplemental merge logic against real ModelRegistry auth filtering, registerProvider full-replacement, and OAuth provider validation
- Add @earendil-works/pi-coding-agent devDependency to @fusion/core for real-registry testing
- Add regression test (openai-models.test.ts) exercising the real registry path
- Extend register-model-routes-openai-codex-supplemental.test.ts dashboard test coverage
- Update docs/settings-reference.md
- Add changeset fn-7759-codex-gpt-5-6-dropdown.md (patch, fix)

Files changed:
 .changeset/fn-7759-codex-gpt-5-6-dropdown.md       |   7 ++
 docs/settings-reference.md                         |   2 +-
 packages/core/package.json                         |   1 +
 packages/core/src/__tests__/openai-models.test.ts  |  68 +++++++++++++
 packages/core/src/openai-models.ts                 | 107 ++++++++++++++++-----
 ...-model-routes-openai-codex-supplemental.test.ts |  77 +++++++++++++--
 pnpm-lock.yaml                                     |  68 +++++++++++++
 7 files changed, 300 insertions(+), 30 deletions(-)

Fusion-Task-Id: FN-7759

Fusion-Task-Lineage: 4c03838f-b8d8-4f6f-ae55-c3356c1c3176

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-07-09 23:12:02 -07:00
parent 2be6040b02
commit fed5d3d951
7 changed files with 300 additions and 30 deletions

View File

@@ -0,0 +1,7 @@
---
"@runfusion/fusion": patch
---
summary: GPT-5.6 codex models (luna, sol, terra) now actually appear in the codex model picker.
category: fix
dev: Prior fixes (FN-7742/7745/7754) validated the openai-codex supplemental merge only against a mocked ModelRegistry, so gpt-5.6-luna/sol/terra could fail to reach the picker through the real pi-coding-agent registry (getAvailable() auth filtering + registerProvider full-replacement + OAuth provider validation) and/or the /api/models configuredProviders filter. This closes that gap and adds a real-registry regression test.

View File

@@ -977,7 +977,7 @@ When the Cursor Runtime plugin (`fusion-plugin-cursor-runtime`) is installed and
When the Grok Runtime plugin (`fusion-plugin-grok-runtime`) is installed and the `useGrokCli` toggle is enabled (Settings → Authentication), Grok CLI-discovered models (`grok models`) are surfaced additively in `/api/models` under the `grok-cli` provider — id/name derived from the discovered model id/label. This surfacing is fetched through a short-TTL, single-flight cache so the model picker never spawns `grok` on every request; a missing/failed/unavailable Grok CLI binary simply yields zero `grok-cli` rows without affecting other providers. Disabling `useGrokCli` hides all `grok-cli` rows. Unlike Cursor (OAuth/session auth), Grok direct-endpoint auth is API-key based, but CLI-routed execution lets the `grok` binary use any auth source it supports; the Settings card still surfaces Fusion-visible key detection only as an informational hint.
The three GPT-5.6 codenamed OpenAI Codex variants (`gpt-5.6-luna`, `gpt-5.6-sol`, `gpt-5.6-terra`) are additively surfaced under the `openai-codex` provider (FN-7745/FN-7754, mirroring the Anthropic/Z.ai supplemental-merge pattern above) so they appear both in dashboard `/api/models` and the engine/pi `createFnAgent` registry-seeding surface whenever `openai-codex` is configured — deduped against any pinned pi-ai catalog row that already carries one of the ids.
The three GPT-5.6 codenamed OpenAI Codex variants (`gpt-5.6-luna`, `gpt-5.6-sol`, `gpt-5.6-terra`) are additively surfaced under the `openai-codex` provider (FN-7745/FN-7754/FN-7759, mirroring the Anthropic/Z.ai supplemental-merge pattern above) so they appear both in dashboard `/api/models` and the engine/pi `createFnAgent` registry-seeding surface whenever `openai-codex` is configured — deduped against any pinned pi-ai catalog row that already carries one of the ids. FN-7759 specifically keeps the supplemental registration compatible with the real pi-coding-agent `ModelRegistry` by preserving the OpenAI Codex OAuth provider during dynamic full-provider replacement, so legacy catalogs without native 5.6 rows still survive `getAvailable()` auth filtering and remain executable.
### Planning model

View File

@@ -39,6 +39,7 @@
"test": "vitest run --silent=passed-only --reporter=dot"
},
"devDependencies": {
"@earendil-works/pi-coding-agent": "^0.80.6",
"@types/dockerode": "^3.3.41",
"@types/node": "^25.5.0",
"@vitest/coverage-v8": "^4.1.0",

View File

@@ -1,3 +1,4 @@
import { ModelRegistry } from "@earendil-works/pi-coding-agent";
import { describe, expect, it } from "vitest";
import {
GPT_5_6_LUNA_MODEL_ID,
@@ -9,6 +10,42 @@ import {
} from "../openai-models.js";
const EXPECTED_IDS = [GPT_5_6_LUNA_MODEL_ID, GPT_5_6_SOL_MODEL_ID, GPT_5_6_TERRA_MODEL_ID];
const BUILT_IN_CODEX_IDS = ["gpt-5.3-codex-spark", "gpt-5.4", "gpt-5.4-mini", "gpt-5.5"];
const OPENAI_CODEX_OAUTH_CREDENTIAL = {
refresh: "test-refresh-token",
access: "test-access-token",
expires: Date.now() + 60_000,
};
const OPENAI_CODEX_OAUTH_PROVIDER = {
id: OPENAI_CODEX_PROVIDER_ID,
name: "ChatGPT Plus/Pro (Codex Subscription)",
login: async () => OPENAI_CODEX_OAUTH_CREDENTIAL,
refreshToken: async () => OPENAI_CODEX_OAUTH_CREDENTIAL,
getApiKey: () => OPENAI_CODEX_OAUTH_CREDENTIAL.access,
};
function createOpenAiCodexAuthStorage() {
const credential = {
type: "oauth",
access: "test-access-token",
refresh: "test-refresh-token",
expires: Date.now() + 60_000,
};
return {
getOAuthProviders: () => [OPENAI_CODEX_OAUTH_PROVIDER],
get: (providerId: string) => providerId === OPENAI_CODEX_PROVIDER_ID ? credential : undefined,
hasAuth: (providerId: string) => providerId === OPENAI_CODEX_PROVIDER_ID,
getProviderEnv: () => ({}),
getApiKey: async (providerId: string) => providerId === OPENAI_CODEX_PROVIDER_ID ? credential.access : undefined,
};
}
function createRealRegistryWithLegacyCodexCatalog() {
const registry = ModelRegistry.inMemory(createOpenAiCodexAuthStorage() as never) as unknown as ModelRegistry & { models: Array<Record<string, unknown>> };
registry.models = registry.models.filter((model) => model.provider !== OPENAI_CODEX_PROVIDER_ID || !EXPECTED_IDS.includes(String(model.id)));
return registry;
}
describe("SUPPLEMENTAL_OPENAI_CODEX_PROVIDER_REGISTRATION", () => {
it("targets the openai-codex-responses API and ChatGPT backend baseUrl", () => {
@@ -131,4 +168,35 @@ describe("mergeSupplementalOpenAiCodexModels", () => {
expect(() => mergeSupplementalOpenAiCodexModels(registry)).not.toThrow();
expect(registeredProviders.has(OPENAI_CODEX_PROVIDER_ID)).toBe(true);
});
it("surfaces GPT-5.6 rows through the real ModelRegistry auth and full-replacement path", () => {
const registry = createRealRegistryWithLegacyCodexCatalog();
const beforeIds = registry.getAvailable()
.filter((model) => model.provider === OPENAI_CODEX_PROVIDER_ID)
.map((model) => model.id);
expect(beforeIds).not.toEqual(expect.arrayContaining(EXPECTED_IDS));
expect(beforeIds).toEqual(expect.arrayContaining(BUILT_IN_CODEX_IDS));
const warnings: string[] = [];
mergeSupplementalOpenAiCodexModels(registry, (message) => warnings.push(message));
expect(warnings).toEqual([]);
const codexRows = registry.getAvailable().filter((model) => model.provider === OPENAI_CODEX_PROVIDER_ID);
const codexIds = codexRows.map((model) => model.id);
expect(codexIds).toEqual(expect.arrayContaining([...BUILT_IN_CODEX_IDS, ...EXPECTED_IDS]));
expect(new Set(codexIds).size).toBe(codexIds.length);
for (const id of EXPECTED_IDS) {
const row = codexRows.find((model) => model.id === id);
expect(row).toMatchObject({
provider: OPENAI_CODEX_PROVIDER_ID,
api: "openai-codex-responses",
baseUrl: "https://chatgpt.com/backend-api",
reasoning: true,
contextWindow: 372_000,
maxTokens: 128_000,
});
expect(row?.thinkingLevelMap).toMatchObject({ xhigh: "xhigh", max: "max", minimal: "low" });
}
});
});

View File

@@ -5,16 +5,44 @@ export const GPT_5_6_LUNA_MODEL_ID = "gpt-5.6-luna";
export const GPT_5_6_SOL_MODEL_ID = "gpt-5.6-sol";
export const GPT_5_6_TERRA_MODEL_ID = "gpt-5.6-terra";
interface OpenAiCodexCostTier {
inputTokensAbove: number;
input: number;
output: number;
cacheRead: number;
cacheWrite: number;
}
interface OpenAiCodexOAuthCredentials {
refresh: string;
access: string;
expires: number;
[key: string]: unknown;
}
interface OpenAiCodexOAuthProviderRegistration {
id?: unknown;
name: string;
login: (...args: never[]) => Promise<OpenAiCodexOAuthCredentials>;
refreshToken: (credentials: OpenAiCodexOAuthCredentials) => Promise<OpenAiCodexOAuthCredentials>;
getApiKey: (credentials: OpenAiCodexOAuthCredentials) => string;
usesCallbackServer?: boolean;
}
interface OpenAiCodexModelRegistration {
id: string;
name: string;
api?: "openai-codex-responses";
baseUrl?: string;
reasoning: boolean;
thinkingLevelMap?: Record<string, string | null>;
input: OpenAiCodexModelInput[];
cost: {
input: number;
output: number;
cacheRead: number;
cacheWrite: number;
tiers?: OpenAiCodexCostTier[];
};
contextWindow: number;
maxTokens: number;
@@ -25,6 +53,7 @@ export interface OpenAiCodexProviderRegistration {
name: string;
baseUrl: string;
apiKey?: string;
oauth?: OpenAiCodexOAuthProviderRegistration;
api: "openai-codex-responses";
models: OpenAiCodexModelRegistration[];
}
@@ -38,13 +67,19 @@ export interface OpenAiCodexProviderRegistration {
* did not carry the three GPT-5.6 codenamed ids under "openai-codex", so no picker
* surfaced them. Mirror the SUPPLEMENTAL_ANTHROPIC_PROVIDER_REGISTRATION seam
* (anthropic-models.ts) to additively register them: if a later pi-ai bump already
* carries an id (confirmed true as of the pinned 0.80.5 used by this task), the merge
* below is a dedupe-safe no-op — the existing catalog row always wins, never displaced
* or duplicated. Field shape (api/baseUrl) copied verbatim from the pinned catalog's
* openai-codex.models.js entries; apiKey is intentionally omitted because the real
* "openai-codex" provider authenticates via ChatGPT Plus/Pro OAuth, not an env-var API
* key — the merge preserves whatever auth the provider was already registered with
* (see mergeSupplementalOpenAiCodexModels's `...registeredProvider` override below).
* carries an id, the merge below is a dedupe-safe no-op — the existing catalog row
* always wins, never displaced or duplicated. Field shape (api/baseUrl) is copied
* from the pinned catalog's openai-codex.models.js entries.
*
* FNXC:ModelCatalog 2026-07-09-23:55:
* FN-7759: the real pi-coding-agent ModelRegistry rejects dynamic providers that
* define models without `apiKey` or `oauth`, and `registerProvider` full-replaces
* the provider's rows before getAvailable() runs. Prior mock-only tests missed that
* the supplemental OpenAI Codex registration could be logged-and-dropped on installs
* whose pinned catalog lacked 5.6. Keep model fields aligned with the real Codex
* catalog (per-model api/baseUrl/thinking levels/pricing) and carry the built-in
* OAuth provider object into the dynamic registration so validation, getAvailable()
* auth filtering, and execution-time OAuth auth treatment stay identical to 5.3/5.4/5.5.
*/
export const SUPPLEMENTAL_OPENAI_CODEX_PROVIDER_REGISTRATION: OpenAiCodexProviderRegistration = {
name: "OpenAI Codex",
@@ -54,53 +89,67 @@ export const SUPPLEMENTAL_OPENAI_CODEX_PROVIDER_REGISTRATION: OpenAiCodexProvide
{
id: GPT_5_6_LUNA_MODEL_ID,
name: "GPT-5.6 Luna",
api: "openai-codex-responses",
baseUrl: "https://chatgpt.com/backend-api",
reasoning: true,
thinkingLevelMap: { xhigh: "xhigh", max: "max", minimal: "low" },
input: ["text", "image"],
cost: {
input: 1.25,
output: 10,
cacheRead: 0.125,
input: 1,
output: 6,
cacheRead: 0.1,
cacheWrite: 1.25,
tiers: [{ inputTokensAbove: 272_000, input: 2, output: 9, cacheRead: 0.2, cacheWrite: 2.5 }],
},
contextWindow: 272_000,
contextWindow: 372_000,
maxTokens: 128_000,
},
{
id: GPT_5_6_SOL_MODEL_ID,
name: "GPT-5.6 Sol",
api: "openai-codex-responses",
baseUrl: "https://chatgpt.com/backend-api",
reasoning: true,
thinkingLevelMap: { xhigh: "xhigh", max: "max", minimal: "low" },
input: ["text", "image"],
cost: {
input: 1.25,
output: 10,
cacheRead: 0.125,
cacheWrite: 1.25,
input: 5,
output: 30,
cacheRead: 0.5,
cacheWrite: 6.25,
tiers: [{ inputTokensAbove: 272_000, input: 10, output: 45, cacheRead: 1, cacheWrite: 12.5 }],
},
contextWindow: 272_000,
contextWindow: 372_000,
maxTokens: 128_000,
},
{
id: GPT_5_6_TERRA_MODEL_ID,
name: "GPT-5.6 Terra",
api: "openai-codex-responses",
baseUrl: "https://chatgpt.com/backend-api",
reasoning: true,
thinkingLevelMap: { xhigh: "xhigh", max: "max", minimal: "low" },
input: ["text", "image"],
cost: {
input: 1.25,
output: 10,
cacheRead: 0.125,
cacheWrite: 1.25,
input: 2.5,
output: 15,
cacheRead: 0.25,
cacheWrite: 3.125,
tiers: [{ inputTokensAbove: 272_000, input: 5, output: 22.5, cacheRead: 0.5, cacheWrite: 6.25 }],
},
contextWindow: 272_000,
contextWindow: 372_000,
maxTokens: 128_000,
},
],
};
type OpenAiCodexModelLike = Partial<Omit<OpenAiCodexModelRegistration, "name" | "compat">> & {
type OpenAiCodexModelLike = Partial<Omit<OpenAiCodexModelRegistration, "name" | "api" | "compat" | "thinkingLevelMap">> & {
id: string;
api?: string;
name?: unknown;
provider?: string;
compat?: unknown;
thinkingLevelMap?: unknown;
};
interface OpenAiCodexModelRegistryLike {
@@ -110,14 +159,25 @@ interface OpenAiCodexModelRegistryLike {
type RegistryWithProviderState = OpenAiCodexModelRegistryLike & {
registeredProviders?: Map<string, Partial<OpenAiCodexProviderRegistration>>;
authStorage?: { getOAuthProviders?: () => OpenAiCodexOAuthProviderRegistration[] };
};
function getOpenAiCodexOAuthProvider(registryWithState: RegistryWithProviderState, registeredProvider: Partial<OpenAiCodexProviderRegistration> | undefined): OpenAiCodexOAuthProviderRegistration | undefined {
return registeredProvider?.oauth
?? registryWithState.authStorage?.getOAuthProviders?.().find((provider) => provider.id === OPENAI_CODEX_PROVIDER_ID);
}
function toOpenAiCodexModelRegistration(model: OpenAiCodexModelLike): OpenAiCodexModelRegistration {
const supplemental = SUPPLEMENTAL_OPENAI_CODEX_PROVIDER_REGISTRATION.models.find((entry) => entry.id === model.id);
return {
id: model.id,
name: String(model.name ?? supplemental?.name ?? model.id),
api: model.api === "openai-codex-responses" ? model.api : supplemental?.api,
baseUrl: model.baseUrl ?? supplemental?.baseUrl,
reasoning: model.reasoning ?? supplemental?.reasoning ?? false,
thinkingLevelMap: typeof model.thinkingLevelMap === "object" && model.thinkingLevelMap !== null
? { ...(model.thinkingLevelMap as Record<string, string | null>) }
: supplemental?.thinkingLevelMap ? { ...supplemental.thinkingLevelMap } : undefined,
input: Array.isArray(model.input) ? model.input as OpenAiCodexModelInput[] : supplemental?.input ?? ["text"],
cost: model.cost ?? supplemental?.cost ?? { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: Number(model.contextWindow ?? supplemental?.contextWindow ?? 0),
@@ -154,9 +214,12 @@ export function mergeSupplementalOpenAiCodexModels(
if (missingModels.length === 0) return;
const oauth = getOpenAiCodexOAuthProvider(registryWithState, registeredProvider);
modelRegistry.registerProvider(OPENAI_CODEX_PROVIDER_ID, {
...cloneOpenAiCodexProviderRegistration(SUPPLEMENTAL_OPENAI_CODEX_PROVIDER_REGISTRATION),
...registeredProvider,
oauth,
models: [...currentModels, ...missingModels.map((model) => toOpenAiCodexModelRegistration(model))],
});
} catch (error) {

View File

@@ -9,11 +9,51 @@ registry lacking these ids would never surface them even with openai-codex confi
this suite encodes that failing-before/passing-after contract plus dedupe and the
configuredProviders allow-list gate.
*/
import { ModelRegistry } from "@earendil-works/pi-coding-agent";
import type { Router } from "express";
import { describe, expect, it, vi } from "vitest";
import { registerModelRoutes } from "../routes/register-model-routes.js";
const GPT_5_6_IDS = ["gpt-5.6-luna", "gpt-5.6-sol", "gpt-5.6-terra"];
const BUILT_IN_CODEX_IDS = ["gpt-5.3-codex-spark", "gpt-5.4", "gpt-5.4-mini", "gpt-5.5"];
const OPENAI_CODEX_OAUTH_CREDENTIAL = {
refresh: "test-refresh-token",
access: "test-access-token",
expires: Date.now() + 60_000,
};
const OPENAI_CODEX_OAUTH_PROVIDER = {
id: "openai-codex",
name: "ChatGPT Plus/Pro (Codex Subscription)",
login: async () => OPENAI_CODEX_OAUTH_CREDENTIAL,
refreshToken: async () => OPENAI_CODEX_OAUTH_CREDENTIAL,
getApiKey: () => OPENAI_CODEX_OAUTH_CREDENTIAL.access,
};
function createOpenAiCodexAuthStorage(openAiCodexConfigured: boolean) {
const credential = {
type: "oauth",
access: "test-access-token",
refresh: "test-refresh-token",
expires: Date.now() + 60_000,
};
return {
reload: vi.fn(),
getOAuthProviders: vi.fn(() => [OPENAI_CODEX_OAUTH_PROVIDER]),
getApiKeyProviders: vi.fn(() => []),
get: vi.fn((providerId: string) => openAiCodexConfigured && providerId === "openai-codex" ? credential : undefined),
hasAuth: vi.fn((providerId: string) => openAiCodexConfigured && providerId === "openai-codex"),
hasApiKey: vi.fn(() => false),
getProviderEnv: vi.fn(() => ({})),
getApiKey: vi.fn(async (providerId: string) => openAiCodexConfigured && providerId === "openai-codex" ? credential.access : undefined),
};
}
function createRealModelRegistryWithLegacyCodexCatalog(authStorage: ReturnType<typeof createOpenAiCodexAuthStorage>) {
const registry = ModelRegistry.inMemory(authStorage as never) as unknown as ModelRegistry & { models: Array<Record<string, unknown>> };
registry.models = registry.models.filter((model) => model.provider !== "openai-codex" || !GPT_5_6_IDS.includes(String(model.id)));
return registry;
}
interface FakeOpenAiCodexModel {
id: string;
@@ -61,7 +101,7 @@ function createFakeModelRegistry(initialOpenAiCodexModels: FakeOpenAiCodexModel[
};
}
function createRouterHarness(modelRegistry: ReturnType<typeof createFakeModelRegistry>, options: { openAiCodexConfigured: boolean }) {
function createRouterHarness(modelRegistry: ReturnType<typeof createFakeModelRegistry> | ModelRegistry, options: { openAiCodexConfigured: boolean }, authStorage = createOpenAiCodexAuthStorage(options.openAiCodexConfigured)) {
const getHandlers = new Map<string, (req: unknown, res: { json: (body: unknown) => void }) => Promise<void>>();
const router = {
get: vi.fn((path: string, handler: (req: unknown, res: { json: (body: unknown) => void }) => Promise<void>) => {
@@ -76,12 +116,6 @@ function createRouterHarness(modelRegistry: ReturnType<typeof createFakeModelReg
const runtimeLogger = { child: vi.fn(() => ({ warn: vi.fn() })) };
const authStorage = {
reload: vi.fn(),
getOAuthProviders: vi.fn(() => (options.openAiCodexConfigured ? [{ id: "openai-codex", name: "OpenAI Codex" }] : [])),
hasAuth: vi.fn((providerId: string) => options.openAiCodexConfigured && providerId === "openai-codex"),
};
registerModelRoutes({
router,
store: store as never,
@@ -140,4 +174,33 @@ describe("FN-7745: GPT-5.6 codenamed OpenAI Codex variants — /api/models", ()
const response = await callModels(handler);
expect(response.models.some((m) => m.provider === "openai-codex")).toBe(false);
});
it("drives the real ModelRegistry path from supplemental merge through /api/models filtering", async () => {
const authStorage = createOpenAiCodexAuthStorage(true);
const modelRegistry = createRealModelRegistryWithLegacyCodexCatalog(authStorage);
const beforeIds = modelRegistry.getAvailable()
.filter((model) => model.provider === "openai-codex")
.map((model) => model.id);
expect(beforeIds).not.toEqual(expect.arrayContaining(GPT_5_6_IDS));
expect(beforeIds).toEqual(expect.arrayContaining(BUILT_IN_CODEX_IDS));
const handler = createRouterHarness(modelRegistry, { openAiCodexConfigured: true }, authStorage);
const response = await callModels(handler);
const codexRows = response.models.filter((m) => m.provider === "openai-codex");
const codexIds = codexRows.map((m) => m.id);
expect(codexIds).toEqual(expect.arrayContaining([...BUILT_IN_CODEX_IDS, ...GPT_5_6_IDS]));
const keys = response.models.map((m) => `${m.provider}/${m.id}`);
expect(new Set(keys).size).toBe(keys.length);
});
it("keeps real-registry GPT-5.6 rows gated when openai-codex auth is absent", async () => {
const authStorage = createOpenAiCodexAuthStorage(false);
const modelRegistry = createRealModelRegistryWithLegacyCodexCatalog(authStorage);
const handler = createRouterHarness(modelRegistry, { openAiCodexConfigured: false }, authStorage);
const response = await callModels(handler);
expect(response.models.some((m) => m.provider === "openai-codex")).toBe(false);
});
});

68
pnpm-lock.yaml generated
View File

@@ -182,6 +182,9 @@ importers:
specifier: ^2.8.3
version: 2.8.3
devDependencies:
'@earendil-works/pi-coding-agent':
specifier: ^0.80.6
version: 0.80.6
'@types/dockerode':
specifier: ^3.3.41
version: 3.3.47
@@ -8523,6 +8526,20 @@ snapshots:
- ws
- zod
'@earendil-works/pi-agent-core@0.80.6':
dependencies:
'@earendil-works/pi-ai': 0.80.6
ignore: 7.0.5
typebox: 1.1.38
yaml: 2.9.0
transitivePeerDependencies:
- '@modelcontextprotocol/sdk'
- bufferutil
- supports-color
- utf-8-validate
- ws
- zod
'@earendil-works/pi-agent-core@0.80.6(@modelcontextprotocol/sdk@1.28.0(zod@3.25.76))(ws@8.20.0)(zod@3.25.76)':
dependencies:
'@earendil-works/pi-ai': 0.80.6(@modelcontextprotocol/sdk@1.28.0(zod@3.25.76))(ws@8.20.0)(zod@3.25.76)
@@ -8666,6 +8683,27 @@ snapshots:
- ws
- zod
'@earendil-works/pi-ai@0.80.6':
dependencies:
'@anthropic-ai/sdk': 0.91.1
'@aws-sdk/client-bedrock-runtime': 3.1048.0
'@google/genai': 1.52.0
'@mistralai/mistralai': 2.2.6(@opentelemetry/api@1.9.0)
'@opentelemetry/api': 1.9.0
'@smithy/node-http-handler': 4.7.3
http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.6
openai: 6.26.0
partial-json: 0.1.7
typebox: 1.1.38
transitivePeerDependencies:
- '@modelcontextprotocol/sdk'
- bufferutil
- supports-color
- utf-8-validate
- ws
- zod
'@earendil-works/pi-ai@0.80.6(@modelcontextprotocol/sdk@1.28.0(zod@3.25.76))(ws@8.20.0)(zod@3.25.76)':
dependencies:
'@anthropic-ai/sdk': 0.91.1(zod@3.25.76)
@@ -8875,6 +8913,36 @@ snapshots:
- ws
- zod
'@earendil-works/pi-coding-agent@0.80.6':
dependencies:
'@earendil-works/pi-agent-core': 0.80.6
'@earendil-works/pi-ai': 0.80.6
'@earendil-works/pi-tui': 0.80.6
'@silvia-odwyer/photon-node': 0.3.4
chalk: 5.6.2
cross-spawn: 7.0.6
diff: 8.0.4
glob: 13.0.6
highlight.js: 10.7.3
hosted-git-info: 9.0.3
ignore: 7.0.5
jiti: 2.7.0
minimatch: 10.2.5
proper-lockfile: 4.1.2
semver: 7.8.0
typebox: 1.1.38
undici: 8.5.0
yaml: 2.9.0
optionalDependencies:
'@mariozechner/clipboard': 0.3.9
transitivePeerDependencies:
- '@modelcontextprotocol/sdk'
- bufferutil
- supports-color
- utf-8-validate
- ws
- zod
'@earendil-works/pi-coding-agent@0.80.6(@modelcontextprotocol/sdk@1.28.0(zod@3.25.76))(ws@8.20.0)(zod@3.25.76)':
dependencies:
'@earendil-works/pi-agent-core': 0.80.6(@modelcontextprotocol/sdk@1.28.0(zod@3.25.76))(ws@8.20.0)(zod@3.25.76)