@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 +8 -8
- package/src/commit/model-selection.ts +3 -6
- package/src/config/model-registry.ts +18 -1
- package/src/config/model-resolver.ts +38 -31
- package/src/config/settings-schema.ts +12 -0
- package/src/internal-urls/build-info.generated.ts +9 -9
- package/src/internal-urls/docs-index.generated.ts +4 -3
- package/src/modes/controllers/login-model.ts +72 -5
- package/src/routing/commands.ts +15 -1
- package/src/routing/coordinator.ts +11 -0
- package/src/routing/effort.ts +19 -1
- package/src/routing/index.ts +1 -0
- package/src/routing/presets.ts +2 -0
- package/src/routing/subscription-profiles.ts +82 -0
- package/src/routing/types.ts +14 -0
- package/src/session/agent-session.ts +60 -11
- package/src/slash-commands/builtin-registry.ts +8 -0
- package/src/utils/commit-message-generator.ts +2 -9
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@f5-sales-demo/xcsh",
|
|
4
|
-
"version": "20.
|
|
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.
|
|
65
|
-
"@f5-sales-demo/pi-ai": "20.
|
|
66
|
-
"@f5-sales-demo/pi-natives": "20.
|
|
67
|
-
"@f5-sales-demo/pi-resource-management": "20.
|
|
68
|
-
"@f5-sales-demo/pi-tui": "20.
|
|
69
|
-
"@f5-sales-demo/pi-utils": "20.
|
|
70
|
-
"@f5-sales-demo/xcsh-stats": "20.
|
|
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
|
|
58
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
1259
|
+
add(match);
|
|
1235
1260
|
}
|
|
1236
1261
|
|
|
1237
1262
|
// 2. Try priority chain
|
|
1238
|
-
for (const pattern of MODEL_PRIO
|
|
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
|
-
|
|
1266
|
+
add(providerMatch);
|
|
1242
1267
|
|
|
1243
1268
|
// Try exact match first
|
|
1244
1269
|
const exactMatch = parseModelPattern(pattern, availableModels, undefined, { modelRegistry }).model;
|
|
1245
|
-
|
|
1270
|
+
add(exactMatch);
|
|
1246
1271
|
|
|
1247
1272
|
// Try fuzzy match (substring)
|
|
1248
|
-
const fuzzyMatch = availableModels.find(m => m.id.toLowerCase().includes(pattern));
|
|
1249
|
-
|
|
1273
|
+
const fuzzyMatch = availableModels.find(m => m.id.toLowerCase().includes(pattern.toLowerCase()));
|
|
1274
|
+
add(fuzzyMatch);
|
|
1250
1275
|
}
|
|
1251
1276
|
|
|
1252
|
-
// 3.
|
|
1253
|
-
|
|
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
|
-
|
|
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.
|
|
21
|
-
"commit": "
|
|
22
|
-
"shortCommit": "
|
|
23
|
-
"branch": "
|
|
24
|
-
"tag": "v20.
|
|
25
|
-
"commitDate": "2026-08-
|
|
26
|
-
"buildDate": "2026-08-
|
|
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/
|
|
32
|
-
"releaseUrl": "https://github.com/f5-sales-demo/xcsh/releases/tag/v20.
|
|
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
|
};
|