fix(delegation): dynamic model display from settings

- Read ANTHROPIC_MODEL from profile settings instead of hardcoding
- Display model name in full uppercase (GLM-4.7, not Glm-4.7)
- Add null/undefined guard to getModelDisplayName
- Remove hardcoded GLM-4.6/GLM-4.6 (Thinking) display names

Closes #431
This commit is contained in:
kaitranntt
2026-02-03 00:01:30 -05:00
parent 332d150a83
commit f6b7045023
4 changed files with 48 additions and 25 deletions
+2 -3
View File
@@ -14,7 +14,7 @@ import { ui, warn, info } from '../utils/ui';
import { type ExecutionOptions, type ExecutionResult, type StreamMessage } from './executor/types';
import { StreamBuffer, formatToolVerbose } from './executor/stream-parser';
import { buildExecutionResult } from './executor/result-aggregator';
import { getCcsDir } from '../utils/config-manager';
import { getCcsDir, getModelDisplayName } from '../utils/config-manager';
// Re-export types for consumers
export type { ExecutionOptions, ExecutionResult, StreamMessage } from './executor/types';
@@ -196,8 +196,7 @@ export class HeadlessExecutor {
const streamBuffer = new StreamBuffer();
if (showProgress) {
const modelName =
profile === 'glm' ? 'GLM-4.6' : profile === 'kimi' ? 'Kimi' : profile.toUpperCase();
const modelName = getModelDisplayName(profile);
console.error(ui.info(`Delegating to ${modelName}...`));
}
+5 -18
View File
@@ -9,6 +9,7 @@ import * as path from 'path';
import { execSync } from 'child_process';
import * as fs from 'fs';
import { ui } from '../utils/ui';
import { getModelDisplayName } from '../utils/config-manager';
import type { ExecutionResult, ExecutionError, PermissionDenial } from './executor/types';
// Alias for backward compatibility
@@ -58,7 +59,7 @@ class ResultFormatter {
let output = '';
// Header box
const modelName = this.getModelDisplayName(profile);
const modelName = getModelDisplayName(profile);
const headerIcon = success ? '[i]' : '[X]';
output += ui.box(`${headerIcon} Delegated to ${modelName} (ccs:${profile})`, {
borderStyle: 'round',
@@ -225,7 +226,7 @@ class ResultFormatter {
*/
private static formatInfoTable(result: ExecutionResult): string {
const { cwd, profile, duration, exitCode, sessionId, totalCost, numTurns } = result;
const modelName = this.getModelDisplayName(profile);
const modelName = getModelDisplayName(profile);
const durationSec = (duration / 1000).toFixed(1);
const rows: string[][] = [
@@ -253,20 +254,6 @@ class ResultFormatter {
});
}
/**
* Get display name for model profile
*/
private static getModelDisplayName(profile: string): string {
const displayNames: Record<string, string> = {
glm: 'GLM-4.6',
glmt: 'GLM-4.6 (Thinking)',
kimi: 'Kimi',
default: 'Claude',
};
return displayNames[profile] || profile.toUpperCase();
}
/**
* Truncate string to max length
*/
@@ -283,7 +270,7 @@ class ResultFormatter {
static async formatMinimal(result: ExecutionResult): Promise<string> {
await ui.init();
const { profile, success, duration } = result;
const modelName = this.getModelDisplayName(profile);
const modelName = getModelDisplayName(profile);
const icon = success ? ui.ok('') : ui.fail('');
const durationSec = (duration / 1000).toFixed(1);
@@ -326,7 +313,7 @@ class ResultFormatter {
await ui.init();
const { profile, duration, sessionId, totalCost, permissionDenials } = result;
const modelName = this.getModelDisplayName(profile);
const modelName = getModelDisplayName(profile);
const timeoutMin = (duration / 60000).toFixed(1);
let output = '';
+34
View File
@@ -246,3 +246,37 @@ export function getSettingsPath(profile: string): string {
return expandedPath;
}
/**
* Get display name for a profile by reading ANTHROPIC_MODEL from settings
* @param profile - Profile name (glm, glmt, kimi, custom, etc.)
* @returns Formatted display name (e.g., 'GLM-4.7', 'Kimi', 'Custom-Model')
*/
export function getModelDisplayName(profile: string): string {
if (!profile) {
return '';
}
const settingsPath = path.join(getCcsDir(), `${profile}.settings.json`);
try {
if (fs.existsSync(settingsPath)) {
const content = fs.readFileSync(settingsPath, 'utf8');
const settings = JSON.parse(content) as { env?: { ANTHROPIC_MODEL?: string } };
const model = settings.env?.ANTHROPIC_MODEL;
if (model) {
// Format: 'glm-4.7' -> 'GLM-4.7' (uppercase letters, preserve numbers)
return model
.split('-')
.map((part) => part.toUpperCase())
.join('-');
}
}
} catch {
// Fall through to default
}
// Fallback: profile name uppercase
return profile.toUpperCase();
}
@@ -16,7 +16,7 @@ describe('ResultFormatter', () => {
const formatted = await ResultFormatter.format(result);
assert.ok(formatted.includes('Delegated to GLM-4.6'));
assert.ok(formatted.toLowerCase().includes('delegated to glm'));
assert.ok(formatted.includes('ccs:glm'));
assert.ok(formatted.includes('/home/user/project'));
assert.ok(formatted.includes('2.3s'));
@@ -179,11 +179,13 @@ describe('ResultFormatter', () => {
};
const glmFormatted = await ResultFormatter.format(glmResult);
assert.ok(glmFormatted.includes('GLM-4.6'));
// Model display reads from settings or falls back to profile uppercase
// Use case-insensitive check since format may vary (GLM, Glm-4.7, etc.)
assert.ok(glmFormatted.toLowerCase().includes('glm'));
const kimiResult = { ...glmResult, profile: 'kimi' };
const kimiFormatted = await ResultFormatter.format(kimiResult);
assert.ok(kimiFormatted.includes('Kimi'));
assert.ok(kimiFormatted.toLowerCase().includes('kimi'));
});
});
@@ -220,7 +222,8 @@ describe('ResultFormatter', () => {
const minimal = await ResultFormatter.formatMinimal(result);
assert.ok(minimal.includes('[OK]'));
assert.ok(minimal.includes('GLM-4.6'));
// Model display reads from settings or falls back to profile uppercase
assert.ok(minimal.toLowerCase().includes('glm'));
assert.ok(minimal.includes('1.5s'));
assert.ok(minimal.split('\n').length <= 3);
});