@f5-sales-demo/xcsh 20.13.2 → 20.15.0

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@f5-sales-demo/xcsh",
4
- "version": "20.13.2",
4
+ "version": "20.15.0",
5
5
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://github.com/f5-sales-demo/xcsh",
7
7
  "author": "Can Boluk",
@@ -61,13 +61,13 @@
61
61
  },
62
62
  "dependencies": {
63
63
  "@agentclientprotocol/sdk": "1.3.0",
64
- "@f5-sales-demo/pi-agent-core": "20.13.2",
65
- "@f5-sales-demo/pi-ai": "20.13.2",
66
- "@f5-sales-demo/pi-natives": "20.13.2",
67
- "@f5-sales-demo/pi-resource-management": "20.13.2",
68
- "@f5-sales-demo/pi-tui": "20.13.2",
69
- "@f5-sales-demo/pi-utils": "20.13.2",
70
- "@f5-sales-demo/xcsh-stats": "20.13.2",
64
+ "@f5-sales-demo/pi-agent-core": "20.15.0",
65
+ "@f5-sales-demo/pi-ai": "20.15.0",
66
+ "@f5-sales-demo/pi-natives": "20.15.0",
67
+ "@f5-sales-demo/pi-resource-management": "20.15.0",
68
+ "@f5-sales-demo/pi-tui": "20.15.0",
69
+ "@f5-sales-demo/pi-utils": "20.15.0",
70
+ "@f5-sales-demo/xcsh-stats": "20.15.0",
71
71
  "@mozilla/readability": "^0.6",
72
72
  "@sinclair/typebox": "^0.34",
73
73
  "@xterm/headless": "^6.0",
@@ -2,13 +2,12 @@ import type { ThinkingLevel } from "@f5-sales-demo/pi-agent-core";
2
2
  import type { Api, Model } from "@f5-sales-demo/pi-ai";
3
3
  import { MODEL_ROLE_IDS } from "../config/model-registry";
4
4
  import {
5
+ findPriorityRoleModel,
5
6
  type ModelLookupRegistry,
6
- parseModelPattern,
7
7
  resolveModelRoleValue,
8
8
  resolveRoleSelection,
9
9
  } from "../config/model-resolver";
10
10
  import type { Settings } from "../config/settings";
11
- import MODEL_PRIO from "../priority.json" with { type: "json" };
12
11
 
13
12
  export interface ResolvedCommitModel {
14
13
  model: Model<Api>;
@@ -54,10 +53,8 @@ export async function resolveSmolModel(
54
53
  if (apiKey) return { model: resolvedSmol.model, apiKey, thinkingLevel: resolvedSmol.thinkingLevel };
55
54
  }
56
55
 
57
- const matchPreferences = { usageOrder: settings.getStorage()?.getModelUsageOrder() };
58
- for (const pattern of MODEL_PRIO.smol) {
59
- const candidate = parseModelPattern(pattern, available, matchPreferences, { modelRegistry }).model;
60
- if (!candidate) continue;
56
+ const candidate = await findPriorityRoleModel(modelRegistry, "smol", undefined, false);
57
+ if (candidate) {
61
58
  const apiKey = await modelRegistry.getApiKey(candidate);
62
59
  if (apiKey) return { model: candidate, apiKey };
63
60
  }
@@ -1470,10 +1470,27 @@ export class ModelRegistry {
1470
1470
  try {
1471
1471
  const manager = createModelManager({ ...options, cacheDbPath: this.#cacheDbPath });
1472
1472
  const result = await manager.refresh(strategy);
1473
- return result.models.map(model =>
1473
+ const models = result.models.map(model =>
1474
1474
  model.provider === options.providerId ? model : { ...model, provider: options.providerId },
1475
1475
  );
1476
+ this.#providerDiscoveryStates.set(options.providerId, {
1477
+ provider: options.providerId,
1478
+ status: result.stale ? "cached" : "ok",
1479
+ optional: false,
1480
+ stale: result.stale,
1481
+ fetchedAt: Date.now(),
1482
+ models: models.map(model => model.id),
1483
+ });
1484
+ return models;
1476
1485
  } catch (error) {
1486
+ this.#providerDiscoveryStates.set(options.providerId, {
1487
+ provider: options.providerId,
1488
+ status: "unavailable",
1489
+ optional: false,
1490
+ stale: true,
1491
+ models: [],
1492
+ error: error instanceof Error ? error.message : String(error),
1493
+ });
1477
1494
  logger.warn("model discovery failed for provider", {
1478
1495
  provider: options.providerId,
1479
1496
  error: error instanceof Error ? error.message : String(error),
@@ -1225,32 +1225,60 @@ export async function findSmolModel(
1225
1225
  modelRegistry: ModelLookupRegistry,
1226
1226
  savedModel?: string,
1227
1227
  ): Promise<Model<Api> | undefined> {
1228
+ return findPriorityRoleModel(modelRegistry, "smol", savedModel);
1229
+ }
1230
+
1231
+ export async function findPriorityRoleModel(
1232
+ modelRegistry: ModelLookupRegistry,
1233
+ role: "smol" | "slow",
1234
+ savedModel?: string,
1235
+ fallbackToFirst = true,
1236
+ ): Promise<Model<Api> | undefined> {
1237
+ return resolvePriorityRoleCandidates(modelRegistry, role, savedModel, fallbackToFirst)[0];
1238
+ }
1239
+
1240
+ /** Shared ordered candidates for role consumers that need fallback retries. */
1241
+ export function resolvePriorityRoleCandidates(
1242
+ modelRegistry: ModelLookupRegistry,
1243
+ role: "smol" | "slow",
1244
+ savedModel?: string,
1245
+ includeAllFallbacks = true,
1246
+ ): Model<Api>[] {
1228
1247
  const availableModels = modelRegistry.getAvailable();
1229
- if (availableModels.length === 0) return undefined;
1248
+ if (availableModels.length === 0) return [];
1249
+ const candidates: Model<Api>[] = [];
1250
+ const add = (model: Model<Api> | undefined): void => {
1251
+ if (!model) return;
1252
+ if (candidates.some(item => item.provider === model.provider && item.id === model.id)) return;
1253
+ candidates.push(model);
1254
+ };
1230
1255
 
1231
1256
  // 1. Try saved model from settings
1232
1257
  if (savedModel) {
1233
1258
  const match = resolveModelFromString(savedModel, availableModels, undefined, modelRegistry);
1234
- if (match) return match;
1259
+ add(match);
1235
1260
  }
1236
1261
 
1237
1262
  // 2. Try priority chain
1238
- for (const pattern of MODEL_PRIO.smol) {
1263
+ for (const pattern of MODEL_PRIO[role]) {
1239
1264
  // Try exact match with provider prefix
1240
1265
  const providerMatch = availableModels.find(m => `${m.provider}/${m.id}`.toLowerCase() === pattern);
1241
- if (providerMatch) return providerMatch;
1266
+ add(providerMatch);
1242
1267
 
1243
1268
  // Try exact match first
1244
1269
  const exactMatch = parseModelPattern(pattern, availableModels, undefined, { modelRegistry }).model;
1245
- if (exactMatch) return exactMatch;
1270
+ add(exactMatch);
1246
1271
 
1247
1272
  // Try fuzzy match (substring)
1248
- const fuzzyMatch = availableModels.find(m => m.id.toLowerCase().includes(pattern));
1249
- if (fuzzyMatch) return fuzzyMatch;
1273
+ const fuzzyMatch = availableModels.find(m => m.id.toLowerCase().includes(pattern.toLowerCase()));
1274
+ add(fuzzyMatch);
1250
1275
  }
1251
1276
 
1252
- // 3. Fallback to first available (same as default)
1253
- return availableModels[0];
1277
+ // 3. Optional full fallback order for retrying consumers.
1278
+ if (includeAllFallbacks) {
1279
+ for (const model of availableModels) add(model);
1280
+ }
1281
+ return candidates;
1254
1282
  }
1255
1283
 
1256
1284
  /**
@@ -1265,26 +1293,5 @@ export async function findSlowModel(
1265
1293
  modelRegistry: ModelLookupRegistry,
1266
1294
  savedModel?: string,
1267
1295
  ): Promise<Model<Api> | undefined> {
1268
- const availableModels = modelRegistry.getAvailable();
1269
- if (availableModels.length === 0) return undefined;
1270
-
1271
- // 1. Try saved model from settings
1272
- if (savedModel) {
1273
- const match = resolveModelFromString(savedModel, availableModels, undefined, modelRegistry);
1274
- if (match) return match;
1275
- }
1276
-
1277
- // 2. Try priority chain
1278
- for (const pattern of MODEL_PRIO.slow) {
1279
- // Try exact match first
1280
- const exactMatch = parseModelPattern(pattern, availableModels, undefined, { modelRegistry }).model;
1281
- if (exactMatch) return exactMatch;
1282
-
1283
- // Try fuzzy match (substring)
1284
- const fuzzyMatch = availableModels.find(m => m.id.toLowerCase().includes(pattern.toLowerCase()));
1285
- if (fuzzyMatch) return fuzzyMatch;
1286
- }
1287
-
1288
- // 3. Fallback to first available (same as default)
1289
- return availableModels[0];
1296
+ return findPriorityRoleModel(modelRegistry, "slow", savedModel);
1290
1297
  }
@@ -544,6 +544,18 @@ export const SETTINGS_SCHEMA = {
544
544
  },
545
545
  },
546
546
 
547
+ "routing.profile": {
548
+ type: "enum",
549
+ values: ["none", "google-antigravity", "openai-codex"] as const,
550
+ default: "none",
551
+ ui: {
552
+ tab: "model",
553
+ label: "Routing Profile",
554
+ description: "Provider-sticky subscription routing profile",
555
+ submenu: true,
556
+ },
557
+ },
558
+
547
559
  "routing.profiler": {
548
560
  type: "enum",
549
561
  values: ["rules", "hybrid"] as const,
@@ -17,17 +17,17 @@ export interface BuildInfo {
17
17
  }
18
18
 
19
19
  export const BUILD_INFO: BuildInfo = {
20
- "version": "20.13.2",
21
- "commit": "2c8c472faa5935f368999d276bc0b877b437c43c",
22
- "shortCommit": "2c8c472",
23
- "branch": "main",
24
- "tag": "v20.13.2",
25
- "commitDate": "2026-08-11T14:56:07Z",
26
- "buildDate": "2026-08-11T15:23:40.081Z",
20
+ "version": "20.15.0",
21
+ "commit": "2b7a063773092e34e250fb3fba795c8ba23ac756",
22
+ "shortCommit": "2b7a063",
23
+ "branch": "unknown",
24
+ "tag": "v20.15.0",
25
+ "commitDate": "2026-08-12T07:50:19Z",
26
+ "buildDate": "2026-08-16T15:49:49.954Z",
27
27
  "dirty": true,
28
28
  "prNumber": "",
29
29
  "repoUrl": "https://github.com/f5-sales-demo/xcsh",
30
30
  "repoSlug": "f5-sales-demo/xcsh",
31
- "commitUrl": "https://github.com/f5-sales-demo/xcsh/commit/2c8c472faa5935f368999d276bc0b877b437c43c",
32
- "releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v20.13.2"
31
+ "commitUrl": "https://github.com/f5-sales-demo/xcsh/commit/2b7a063773092e34e250fb3fba795c8ba23ac756",
32
+ "releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v20.15.0"
33
33
  };