@phi-code-admin/phi-code 0.76.14 → 0.76.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -23,6 +23,7 @@ import { join, resolve } from "node:path";
23
23
  import { homedir } from "node:os";
24
24
  import { existsSync } from "node:fs";
25
25
  import { fetchLiveModels, pingProvider, toPersistedModel } from "./providers/live-models.js";
26
+ import { isOpenCodeGoAnthropicModel, OPENCODE_GO_ANTHROPIC_BASE_URL } from "./providers/opencode-go.js";
26
27
 
27
28
  // ─── Types ───────────────────────────────────────────────────────────────
28
29
 
@@ -387,6 +388,24 @@ _Edit this file to customize Phi Code's behavior for your project._
387
388
  await writeModelsConfig(config);
388
389
  }
389
390
 
391
+ // OpenCode Go's Qwen/MiniMax models must be persisted under a separate
392
+ // Anthropic-compatible provider (the OpenAI shim 401s them).
393
+ async function persistOpenCodeGoAnthropic(
394
+ apiKey: string,
395
+ models: ReturnType<typeof toPersistedModel>[],
396
+ ): Promise<void> {
397
+ const config = await readModelsConfig();
398
+ const existing = config.providers["opencode-go-anthropic"] ?? {};
399
+ config.providers["opencode-go-anthropic"] = {
400
+ ...existing,
401
+ baseUrl: OPENCODE_GO_ANTHROPIC_BASE_URL,
402
+ api: "anthropic-messages",
403
+ apiKey,
404
+ models,
405
+ };
406
+ await writeModelsConfig(config);
407
+ }
408
+
390
409
  // ─── Manual model assignment (one model per orchestration role) ─────
391
410
  //
392
411
  // As of 0.75.6, `/phi-init` ONLY configures orchestration role models
@@ -519,7 +538,24 @@ _Edit this file to customize Phi Code's behavior for your project._
519
538
 
520
539
  const persistedModels = live.models.map(toPersistedModel);
521
540
  try {
522
- await persistProviderModels(provider, persistedModels);
541
+ if (provider.id === "opencode-go") {
542
+ // Qwen/MiniMax are only served via the Anthropic-compatible endpoint;
543
+ // GLM/Kimi/DeepSeek/Mimo/Hy3 use the OpenAI shim. Persist them as two
544
+ // providers so neither family hits a "not supported for format" 401.
545
+ const openaiModels = persistedModels.filter((m) => !isOpenCodeGoAnthropicModel(m.id));
546
+ const anthropicModels = persistedModels.filter((m) => isOpenCodeGoAnthropicModel(m.id));
547
+ provider.models = openaiModels.map((m) => m.id);
548
+ await persistProviderModels(provider, openaiModels);
549
+ if (anthropicModels.length > 0) {
550
+ await persistOpenCodeGoAnthropic(trimmed, anthropicModels);
551
+ ctx.ui.notify(
552
+ `OpenCode Go: ${anthropicModels.length} Qwen/MiniMax model(s) routed via the Anthropic-compatible provider \`opencode-go-anthropic\` (assign them with \`/plan-models\` or \`/model\`).`,
553
+ "info",
554
+ );
555
+ }
556
+ } else {
557
+ await persistProviderModels(provider, persistedModels);
558
+ }
523
559
  } catch (err) {
524
560
  ctx.ui.notify(
525
561
  `Failed to write provider models to ${modelsJsonPath}: ${err instanceof Error ? err.message : String(err)}`,
@@ -23,6 +23,10 @@ export const OPENCODE_GO_AUTH_URL = "https://opencode.ai/auth";
23
23
  export const OPENCODE_GO_MODELS_URL = "https://opencode.ai/zen/go/v1/models";
24
24
  export const OPENCODE_GO_OPENAI_URL = "https://opencode.ai/zen/go/v1/chat/completions";
25
25
  export const OPENCODE_GO_ANTHROPIC_URL = "https://opencode.ai/zen/go/v1/messages";
26
+ // Base URL for the Anthropic SDK (which appends "/v1/messages"). Qwen and
27
+ // MiniMax models on OpenCode Go are ONLY served via this Anthropic-compatible
28
+ // endpoint; the OpenAI shim returns 401 "not supported for format oa-compat".
29
+ export const OPENCODE_GO_ANTHROPIC_BASE_URL = "https://opencode.ai/zen/go";
26
30
 
27
31
  export interface OpenCodeGoModel {
28
32
  id: string;
@@ -184,19 +188,50 @@ export async function pingOpenCodeGo(apiKey: string, timeoutMs = 5_000): Promise
184
188
  * when the user wants to override defaults (e.g., add a new model
185
189
  * not yet in models.generated.ts).
186
190
  */
191
+ /**
192
+ * On OpenCode Go, Qwen and MiniMax models are only served via the Anthropic-
193
+ * compatible Messages endpoint (/v1/messages); GLM, Kimi, DeepSeek, Mimo and Hy3
194
+ * use the OpenAI-compatible shim (/v1/chat/completions). Calling a Qwen/MiniMax
195
+ * model through the OpenAI format yields a 401 "not supported for format oa-compat".
196
+ */
197
+ export function isOpenCodeGoAnthropicModel(id: string): boolean {
198
+ return /^(qwen|minimax)/i.test(id);
199
+ }
200
+
201
+ function toPersistedOpenCodeGoModel(m: OpenCodeGoModel) {
202
+ return {
203
+ id: m.id,
204
+ name: m.name ?? m.id,
205
+ reasoning: true,
206
+ input: ["text"] as const,
207
+ contextWindow: m.contextWindow ?? 128_000,
208
+ maxTokens: m.maxTokens ?? 16_384,
209
+ };
210
+ }
211
+
212
+ /**
213
+ * OpenAI-compatible provider entry (GLM / Kimi / DeepSeek / Mimo / Hy3).
214
+ * Qwen/MiniMax are intentionally excluded — see buildOpenCodeGoAnthropicProviderConfig.
215
+ */
187
216
  export function buildOpenCodeGoProviderConfig(apiKey: string, models: OpenCodeGoModel[]) {
188
217
  return {
189
218
  baseUrl: "https://opencode.ai/zen/go/v1",
190
219
  api: "openai-completions" as const,
191
220
  apiKey,
192
- models: models.map((m) => ({
193
- id: m.id,
194
- name: m.name ?? m.id,
195
- reasoning: true,
196
- input: ["text"] as const,
197
- contextWindow: m.contextWindow ?? 128_000,
198
- maxTokens: m.maxTokens ?? 16_384,
199
- })),
221
+ models: models.filter((m) => !isOpenCodeGoAnthropicModel(m.id)).map(toPersistedOpenCodeGoModel),
222
+ };
223
+ }
224
+
225
+ /**
226
+ * Anthropic-compatible provider entry for OpenCode Go's Qwen / MiniMax models.
227
+ * `models` is empty when the list contains none.
228
+ */
229
+ export function buildOpenCodeGoAnthropicProviderConfig(apiKey: string, models: OpenCodeGoModel[]) {
230
+ return {
231
+ baseUrl: OPENCODE_GO_ANTHROPIC_BASE_URL,
232
+ api: "anthropic-messages" as const,
233
+ apiKey,
234
+ models: models.filter((m) => isOpenCodeGoAnthropicModel(m.id)).map(toPersistedOpenCodeGoModel),
200
235
  };
201
236
  }
202
237
 
@@ -35,6 +35,7 @@ import {
35
35
  import {
36
36
  OPENCODE_GO_AUTH_URL,
37
37
  OPENCODE_GO_ENV_VAR,
38
+ buildOpenCodeGoAnthropicProviderConfig,
38
39
  buildOpenCodeGoProviderConfig,
39
40
  getOpenCodeGoModels,
40
41
  pingOpenCodeGo,
@@ -385,8 +386,20 @@ async function configureOpenCodeGo(
385
386
  models: config.models,
386
387
  });
387
388
 
389
+ // Qwen / MiniMax are only served via the Anthropic-compatible endpoint on
390
+ // OpenCode Go, so they go to a separate provider (the OpenAI shim 401s them).
391
+ const anthConfig = buildOpenCodeGoAnthropicProviderConfig(trimmed, models);
392
+ if (anthConfig.models.length > 0) {
393
+ store.setKey("opencode-go-anthropic", trimmed, {
394
+ baseUrl: anthConfig.baseUrl,
395
+ api: anthConfig.api,
396
+ models: anthConfig.models,
397
+ });
398
+ }
399
+
388
400
  ui.notify(
389
- `OpenCode Go configured: \`${maskKeyForDisplay(trimmed)}\` (${models.length} models)`,
401
+ `OpenCode Go configured: \`${maskKeyForDisplay(trimmed)}\` ` +
402
+ `(${config.models.length} OpenAI-compat + ${anthConfig.models.length} Anthropic-compat models)`,
390
403
  "info",
391
404
  );
392
405
  return { providerId: "opencode-go", modelCount: models.length };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phi-code-admin/phi-code",
3
- "version": "0.76.14",
3
+ "version": "0.76.16",
4
4
  "description": "Coding agent CLI with persistent memory, sub-agents, intelligent routing, and orchestration",
5
5
  "type": "module",
6
6
  "piConfig": {