@zergai/ztc 0.2.6-dev.36101352252 → 0.2.7-dev.36236183737
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/README.md +43 -17
- package/bin/zmux-darwin-arm64 +0 -0
- package/bin/ztc-tui-darwin-arm64 +0 -0
- package/bin/zterm-darwin-arm64 +0 -0
- package/dist/App.js +19 -5
- package/dist/agent/agent.d.ts +5 -1
- package/dist/agent/agent.js +51 -8
- package/dist/agent/backends/account_pool.d.ts +5 -6
- package/dist/agent/backends/account_pool.js +39 -14
- package/dist/agent/backends/account_rotation.d.ts +3 -0
- package/dist/agent/backends/account_rotation.js +14 -3
- package/dist/agent/backends/codex_account_status.d.ts +42 -0
- package/dist/agent/backends/codex_account_status.js +196 -0
- package/dist/agent/backends/codex_app_server.d.ts +12 -2
- package/dist/agent/backends/codex_app_server.js +66 -6
- package/dist/agent/backends/index.d.ts +2 -0
- package/dist/agent/backends/index.js +13 -5
- package/dist/agent/backends/openai.d.ts +2 -2
- package/dist/agent/backends/openai.js +4 -3
- package/dist/agent/backends/openai_compatible.js +10 -10
- package/dist/agent/backends/openai_responses.d.ts +17 -5
- package/dist/agent/backends/openai_responses.js +2 -4
- package/dist/agent/backends/token_usage.js +1 -1
- package/dist/agent/backends/types.d.ts +3 -0
- package/dist/agent/commands/accounts.d.ts +7 -1
- package/dist/agent/commands/accounts.js +299 -101
- package/dist/agent/commands/config.js +11 -3
- package/dist/agent/commands/config_profile_switch.d.ts +2 -0
- package/dist/agent/commands/config_profile_switch.js +22 -0
- package/dist/agent/commands/context.js +4 -3
- package/dist/agent/commands/index.js +1 -2
- package/dist/agent/commands/input_mode.js +1 -1
- package/dist/agent/commands/model.js +7 -2
- package/dist/agent/commands/plan.d.ts +12 -0
- package/dist/agent/commands/plan.js +39 -28
- package/dist/agent/commands/profile.d.ts +1 -1
- package/dist/agent/commands/profile.js +34 -257
- package/dist/agent/commands/provider_choices.d.ts +12 -1
- package/dist/agent/commands/provider_choices.js +29 -2
- package/dist/agent/commands/provider_selection.d.ts +3 -0
- package/dist/agent/commands/provider_selection.js +63 -0
- package/dist/agent/commands/settings.d.ts +2 -0
- package/dist/agent/commands/settings.js +655 -50
- package/dist/agent/commands/types.d.ts +19 -2
- package/dist/agent/tools/ask_user.js +2 -2
- package/dist/app/agent_runner.d.ts +1 -1
- package/dist/app/agent_runner.js +2 -2
- package/dist/app/ask_user_response.d.ts +3 -0
- package/dist/app/ask_user_response.js +16 -0
- package/dist/app/command_context.js +29 -13
- package/dist/app/controller.d.ts +17 -2
- package/dist/app/controller.js +24 -1
- package/dist/app/selection_completion.d.ts +9 -3
- package/dist/app/selection_completion.js +54 -22
- package/dist/cli_args.js +10 -1
- package/dist/cli_main.js +85 -2
- package/dist/cli_ratatui.d.ts +22 -4
- package/dist/cli_ratatui.js +421 -146
- package/dist/config/defaults.d.ts +6 -0
- package/dist/config/defaults.js +20 -0
- package/dist/config/profiles.d.ts +9 -0
- package/dist/config/profiles.js +87 -0
- package/dist/config/types.d.ts +2 -0
- package/dist/config.d.ts +7 -0
- package/dist/config.js +283 -40
- package/dist/headless.js +14 -0
- package/dist/panes/model_catalog.d.ts +1 -1
- package/dist/panes/model_catalog.js +24 -2
- package/dist/panes/ztc_launch.js +6 -0
- package/dist/router_auth.js +3 -2
- package/dist/session.d.ts +2 -0
- package/dist/session.js +3 -0
- package/dist/types.d.ts +23 -2
- package/dist/ui/views/app.d.ts +3 -1
- package/dist/ui/views/app.js +3 -2
- package/dist/ui/views/completion_menu.js +6 -6
- package/dist/ui/views/input_area.d.ts +2 -1
- package/dist/ui/views/input_area.js +9 -8
- package/dist/ui/views/status_bar.d.ts +2 -0
- package/dist/ui/views/status_bar.js +15 -4
- package/dist/ui/widgets/color_panel.d.ts +9 -0
- package/dist/ui/widgets/color_panel.js +41 -26
- package/dist/ui/widgets/navigator_panel.d.ts +9 -0
- package/dist/ui/widgets/navigator_panel.js +17 -16
- package/dist/ui/widgets/panel_frame.d.ts +83 -3
- package/dist/ui/widgets/panel_frame.js +107 -9
- package/dist/ui/widgets/questionnaire.d.ts +9 -0
- package/dist/ui/widgets/questionnaire.js +214 -16
- package/dist/ui/widgets/resume_panel.d.ts +9 -0
- package/dist/ui/widgets/resume_panel.js +18 -17
- package/dist/ui/widgets/rewind_panel.d.ts +9 -0
- package/dist/ui/widgets/rewind_panel.js +15 -16
- package/dist/ui/widgets/select.d.ts +6 -0
- package/dist/ui/widgets/select.js +47 -12
- package/dist/ui/widgets/settings_panel.d.ts +75 -0
- package/dist/ui/widgets/settings_panel.js +197 -39
- package/dist/ui/widgets/status_line_panel.d.ts +9 -0
- package/dist/ui/widgets/status_line_panel.js +18 -19
- package/dist/ui/widgets/transcript_preview.d.ts +8 -0
- package/dist/ui/widgets/transcript_preview.js +10 -0
- package/dist/utils/context.js +2 -0
- package/dist/utils/fireworks.d.ts +20 -0
- package/dist/utils/fireworks.js +23 -0
- package/dist/utils/models.d.ts +2 -1
- package/dist/utils/models.js +19 -0
- package/dist/web/accounts_mirror.d.ts +7 -0
- package/dist/web/accounts_mirror.js +59 -7
- package/dist/web/mirror_stream.js +19 -5
- package/dist/zlm-embed.mjs +5734 -5362
- package/package.json +2 -2
- package/dist/agent/commands/provider.d.ts +0 -2
- package/dist/agent/commands/provider.js +0 -75
|
@@ -13,7 +13,6 @@ import { shellCommand, cdCommand } from './shell.js';
|
|
|
13
13
|
import { clipboardCommand } from './clipboard.js';
|
|
14
14
|
import { modelsCommand } from './models.js';
|
|
15
15
|
import { modelCommand } from './model.js';
|
|
16
|
-
import { providerCommand } from './provider.js';
|
|
17
16
|
import { permissionsCommand } from './permissions.js';
|
|
18
17
|
import { skillsCommand } from './skills.js';
|
|
19
18
|
import { retryCommand } from './retry.js';
|
|
@@ -66,7 +65,7 @@ import { effortCommand, thinkingCommand, fastCommand } from './effort.js';
|
|
|
66
65
|
import { runtimePluginRegistry } from '../../plugin/runtime_registry.js';
|
|
67
66
|
const commandList = [];
|
|
68
67
|
const helpCommand = makeHelpCommand(() => commandList);
|
|
69
|
-
commandList.push(helpCommand, authCommand, loginCommand, logoutCommand, accountsCommand, zergCommand, zergsCommand, configCommand, debugCommand, clearCommand, statusCommand, runtimeCommand, hooksCommand, emulationCommand, shellCommand, cdCommand, clipboardCommand, modelsCommand, modelCommand,
|
|
68
|
+
commandList.push(helpCommand, authCommand, loginCommand, logoutCommand, accountsCommand, zergCommand, zergsCommand, configCommand, debugCommand, clearCommand, statusCommand, runtimeCommand, hooksCommand, emulationCommand, shellCommand, cdCommand, clipboardCommand, modelsCommand, modelCommand, permissionsCommand, initCommand, forkCommand, sideCommand, diffCommand, rawCommand, themeCommand, doctorCommand, intentCommand, profileCommand, skillsCommand, keybindingsCommand, keymapCommand, shortcutsCommand, updateCommand, restartCommand, reloadCommand, inputModeCommand, retryCommand, dictationStatusCommand, embeddedCommand, benchCommand, voiceCommand, sfxCommand, compactCommand, automodeCommand, sessionsCommand, resumeCommand, renameCommand, contextCommand, shannonRunnerCommand, planCommand, executeCommand, modeCommand, modeListCommand, exitModeCommand, validateCommand, previewCommand, saveCommand, dcaigCommand, manifestCommand, swarmCommand, swarmStopCommand, swarmStatusCommand, superviseCommand, superviseStopCommand, superviseStatusCommand, zlickyCommand, zlickyStopCommand, zlickyStatusCommand, zlickyActCommand, zlickyPresenceCommand, zlickyPttCommand, simCommand, simStopCommand, simStatusCommand, simRunCommand, simCheckpointCommand, simResumeCommand, evolveCommand, evolveStopCommand, evolveStatusCommand, evolveBestCommand, optimizeCommand, batchCommand, batchStatusCommand, batchStopCommand, workflowsCommand, mirrorCommand, settingsCommand, mcpCommand, loopCommand, goalCommand, effortCommand, thinkingCommand, fastCommand, exitCommand);
|
|
70
69
|
export const commands = commandList;
|
|
71
70
|
export function refreshPluginCommands() {
|
|
72
71
|
const existing = new Set(commandList.map(command => command.name));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const inputModeCommand = {
|
|
2
2
|
name: 'inputmode',
|
|
3
|
-
description: 'Set
|
|
3
|
+
description: 'Set busy Enter behavior: queue sends after the next tool, interrupt restarts the turn; Tab queues after the turn',
|
|
4
4
|
usage: '<queue|interrupt>',
|
|
5
5
|
handler: (args, ctx) => {
|
|
6
6
|
const mode = (args[0] || '').toLowerCase();
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
import { configCommand } from './config.js';
|
|
2
2
|
import { modelsCommand } from './models.js';
|
|
3
|
+
import { canonicalProvider } from './provider_choices.js';
|
|
4
|
+
import { selectProviderModel } from './provider_selection.js';
|
|
3
5
|
const MODEL_SUBCOMMANDS = ['list', 'latest', 'set'];
|
|
4
6
|
export const modelCommand = {
|
|
5
7
|
name: 'model',
|
|
6
|
-
description: '
|
|
7
|
-
usage: '<model-id|list [provider]|latest [provider]|set <model-id
|
|
8
|
+
description: 'Select provider and model, or set a model on the current provider',
|
|
9
|
+
usage: '<provider> [model] | zergrouter <provider> [model] | <model-id> | list [provider] | latest [provider] | set <model-id>',
|
|
8
10
|
agentRunnable: true,
|
|
9
11
|
category: 'config',
|
|
10
12
|
handler: (args, ctx) => {
|
|
11
13
|
if (args.length > 0 && MODEL_SUBCOMMANDS.includes(args[0].toLowerCase())) {
|
|
12
14
|
return modelsCommand.handler(args, ctx);
|
|
13
15
|
}
|
|
16
|
+
if (args.length > 1 || (args.length === 1 && canonicalProvider(args[0]))) {
|
|
17
|
+
return selectProviderModel(args, ctx);
|
|
18
|
+
}
|
|
14
19
|
return configCommand.handler(['model', ...args], ctx);
|
|
15
20
|
}
|
|
16
21
|
};
|
|
@@ -74,6 +74,18 @@ export declare const planCommand: Command;
|
|
|
74
74
|
* Commit plan: extract tasks, exit plan mode, begin implementation
|
|
75
75
|
*/
|
|
76
76
|
export declare function commitPlan(planFile: string, planContent: string, tasks: string[], ctx: CommandContext, autoApprove: boolean): void;
|
|
77
|
+
export type PlanApprovalDecision = {
|
|
78
|
+
kind: 'approve';
|
|
79
|
+
} | {
|
|
80
|
+
kind: 'edit';
|
|
81
|
+
} | {
|
|
82
|
+
kind: 'cancel';
|
|
83
|
+
} | {
|
|
84
|
+
kind: 'feedback';
|
|
85
|
+
text: string;
|
|
86
|
+
};
|
|
87
|
+
/** Both plan entry points ask through the same question surface and answer shape. */
|
|
88
|
+
export declare function askPlanApproval(ctx: CommandContext, onDecision: (decision: PlanApprovalDecision) => void): void;
|
|
77
89
|
/**
|
|
78
90
|
* /execute command - Exit plan mode and begin implementation
|
|
79
91
|
*
|
|
@@ -473,6 +473,39 @@ ${planContent}`,
|
|
|
473
473
|
});
|
|
474
474
|
}
|
|
475
475
|
}
|
|
476
|
+
/** Both plan entry points ask through the same question surface and answer shape. */
|
|
477
|
+
export function askPlanApproval(ctx, onDecision) {
|
|
478
|
+
const approveLabel = 'Yes, begin implementation (Recommended)';
|
|
479
|
+
const editLabel = 'Edit plan first';
|
|
480
|
+
ctx.questionnaire.showQuestionnaire({
|
|
481
|
+
title: 'Plan Approval',
|
|
482
|
+
questions: [{
|
|
483
|
+
name: 'decision',
|
|
484
|
+
question: 'Ready to begin implementation?',
|
|
485
|
+
type: 'select',
|
|
486
|
+
required: true,
|
|
487
|
+
options: [
|
|
488
|
+
{ value: 'approve', label: approveLabel, description: 'Approve the plan, create tasks, and start coding' },
|
|
489
|
+
{ value: 'edit', label: editLabel, description: 'Return to plan mode and continue editing' },
|
|
490
|
+
],
|
|
491
|
+
}],
|
|
492
|
+
allowOther: true,
|
|
493
|
+
}, answers => {
|
|
494
|
+
const answer = answers?.decision;
|
|
495
|
+
if (typeof answer !== 'string' || !answer.trim()) {
|
|
496
|
+
onDecision({ kind: 'cancel' });
|
|
497
|
+
}
|
|
498
|
+
else if (answer === 'approve' || answer === approveLabel) {
|
|
499
|
+
onDecision({ kind: 'approve' });
|
|
500
|
+
}
|
|
501
|
+
else if (answer === 'edit' || answer === editLabel) {
|
|
502
|
+
onDecision({ kind: 'edit' });
|
|
503
|
+
}
|
|
504
|
+
else {
|
|
505
|
+
onDecision({ kind: 'feedback', text: answer.trim() });
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
}
|
|
476
509
|
/**
|
|
477
510
|
* /execute command - Exit plan mode and begin implementation
|
|
478
511
|
*
|
|
@@ -525,54 +558,32 @@ Tasks: ${tasks.length} implementation step${tasks.length !== 1 ? 's' : ''} found
|
|
|
525
558
|
role: 'system',
|
|
526
559
|
content: planSummary,
|
|
527
560
|
});
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
title: 'Plan Approval',
|
|
531
|
-
question: 'Ready to begin implementation?',
|
|
532
|
-
type: 'select',
|
|
533
|
-
options: [
|
|
534
|
-
{
|
|
535
|
-
value: 'approve',
|
|
536
|
-
label: 'Yes, begin implementation (Recommended)',
|
|
537
|
-
description: 'Approve the plan, create tasks, and start coding',
|
|
538
|
-
},
|
|
539
|
-
{
|
|
540
|
-
value: 'edit',
|
|
541
|
-
label: 'Edit plan first',
|
|
542
|
-
description: 'Return to plan mode and continue editing',
|
|
543
|
-
},
|
|
544
|
-
],
|
|
545
|
-
allowOther: true,
|
|
546
|
-
}, (value) => {
|
|
547
|
-
if (value === null) {
|
|
548
|
-
// Cancelled - stay in plan mode
|
|
561
|
+
askPlanApproval(ctx, decision => {
|
|
562
|
+
if (decision.kind === 'cancel') {
|
|
549
563
|
ctx.addMessage({
|
|
550
564
|
role: 'system',
|
|
551
565
|
content: 'Plan execution cancelled. Still in plan mode.',
|
|
552
566
|
});
|
|
553
567
|
return;
|
|
554
568
|
}
|
|
555
|
-
|
|
556
|
-
if (selection === 'approve') {
|
|
569
|
+
if (decision.kind === 'approve') {
|
|
557
570
|
commitPlan(planFile, planContent, tasks, ctx, false);
|
|
558
571
|
ctx.triggerAgentTurn?.();
|
|
559
572
|
}
|
|
560
|
-
else if (
|
|
573
|
+
else if (decision.kind === 'edit') {
|
|
561
574
|
ctx.addMessage({
|
|
562
575
|
role: 'system',
|
|
563
576
|
content: 'Returning to plan mode. Continue editing your plan, then run /execute when ready.',
|
|
564
577
|
});
|
|
565
578
|
}
|
|
566
579
|
else {
|
|
567
|
-
// Custom "Other" text - treat as feedback, stay in plan mode
|
|
568
580
|
ctx.addMessage({
|
|
569
581
|
role: 'system',
|
|
570
|
-
content: `Feedback noted. Still in plan mode.\n\nFeedback: ${
|
|
582
|
+
content: `Feedback noted. Still in plan mode.\n\nFeedback: ${decision.text}`,
|
|
571
583
|
});
|
|
572
|
-
// Add feedback as user message so the agent can see it
|
|
573
584
|
ctx.addMessage({
|
|
574
585
|
role: 'user',
|
|
575
|
-
content: `Please update the plan based on this feedback: ${
|
|
586
|
+
content: `Please update the plan based on this feedback: ${decision.text}`,
|
|
576
587
|
});
|
|
577
588
|
}
|
|
578
589
|
});
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Command } from './types.js';
|
|
1
|
+
import type { Command } from './types.js';
|
|
2
2
|
export declare const profileCommand: Command;
|
|
@@ -1,274 +1,51 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
' /profile list',
|
|
12
|
-
' /profile show <ztc|ztc-coder|ztc-datascience|scientist>',
|
|
13
|
-
' /profile gates <profile>',
|
|
14
|
-
' /profile command <profile> <gate> [--tier smoke|initial|expanded|sweep] [--limit N]',
|
|
15
|
-
' /profile run <profile> <gate> [--tier smoke|initial|expanded|sweep] [--limit N]',
|
|
16
|
-
' /profile optimize <profile> [--profile quick|balanced|deep|exhaustive] [--run]',
|
|
17
|
-
' /profile sweep <profile> [--tier smoke|initial|expanded|sweep]',
|
|
18
|
-
].join('\n');
|
|
19
|
-
}
|
|
20
|
-
function formatGate(gate) {
|
|
21
|
-
return [
|
|
22
|
-
`- ${gate.name} [${gate.status}]`,
|
|
23
|
-
` benchmark: ${gate.benchmark}`,
|
|
24
|
-
` purpose: ${gate.purpose}`,
|
|
25
|
-
gate.command ? ` command: ${gate.command.join(' ')}` : undefined,
|
|
26
|
-
gate.tierCommands?.expanded ? ` expanded: ${gate.tierCommands.expanded.join(' ')}` : undefined,
|
|
27
|
-
gate.tierCommands?.sweep ? ` sweep: ${gate.tierCommands.sweep.join(' ')}` : undefined,
|
|
28
|
-
gate.notes?.length ? ` notes: ${gate.notes.join(' ')}` : undefined,
|
|
29
|
-
].filter(Boolean).join('\n');
|
|
30
|
-
}
|
|
31
|
-
function replaceLimit(command, limit) {
|
|
32
|
-
if (!limit) {
|
|
33
|
-
return command;
|
|
34
|
-
}
|
|
35
|
-
const idx = command.indexOf('--limit');
|
|
36
|
-
if (idx >= 0) {
|
|
37
|
-
return [...command.slice(0, idx + 1), limit, ...command.slice(idx + 2)];
|
|
38
|
-
}
|
|
39
|
-
return [...command, '--limit', limit];
|
|
40
|
-
}
|
|
41
|
-
function findFlagValue(args, flag) {
|
|
42
|
-
const idx = args.indexOf(flag);
|
|
43
|
-
if (idx < 0) {
|
|
44
|
-
return undefined;
|
|
45
|
-
}
|
|
46
|
-
const value = args[idx + 1];
|
|
47
|
-
return value && !value.startsWith('--') ? value : undefined;
|
|
48
|
-
}
|
|
49
|
-
function shellQuote(value) {
|
|
50
|
-
if (/^[A-Za-z0-9_./:=,@+-]+$/.test(value)) {
|
|
51
|
-
return value;
|
|
52
|
-
}
|
|
53
|
-
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
54
|
-
}
|
|
55
|
-
function normalizeTier(value) {
|
|
56
|
-
if (value === 'smoke' || value === 'expanded' || value === 'sweep') {
|
|
57
|
-
return value;
|
|
58
|
-
}
|
|
59
|
-
return 'initial';
|
|
60
|
-
}
|
|
61
|
-
function gateCommand(profileId, gateName, limit, tier) {
|
|
62
|
-
const manifest = getProfileManifest(profileId);
|
|
63
|
-
if (!manifest) {
|
|
64
|
-
return [];
|
|
65
|
-
}
|
|
66
|
-
const normalizedGate = gateName.toLowerCase();
|
|
67
|
-
const gate = manifest.gates.find(candidate => candidate.benchmark.toLowerCase() === normalizedGate ||
|
|
68
|
-
candidate.name.toLowerCase().replace(/\s+/g, '-') === normalizedGate ||
|
|
69
|
-
candidate.name.toLowerCase() === normalizedGate);
|
|
70
|
-
if (!gate?.command) {
|
|
71
|
-
return [];
|
|
72
|
-
}
|
|
73
|
-
return replaceLimit(getProfileGateCommand(gate, normalizeTier(tier)), limit);
|
|
74
|
-
}
|
|
75
|
-
function profileRunDir(profileId, tier) {
|
|
76
|
-
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
77
|
-
return join(homedir(), '.ztc', 'profile-runs', `${stamp}-${profileId}-${tier}`);
|
|
78
|
-
}
|
|
79
|
-
function tierLimit(tier, benchmark) {
|
|
80
|
-
if (tier === 'smoke') {
|
|
81
|
-
return '2';
|
|
82
|
-
}
|
|
83
|
-
if (tier === 'initial') {
|
|
84
|
-
return '5';
|
|
85
|
-
}
|
|
86
|
-
if (tier === 'expanded') {
|
|
87
|
-
return benchmark === 'polyglot' ? '10' : '25';
|
|
88
|
-
}
|
|
89
|
-
if (tier === 'sweep') {
|
|
90
|
-
if (benchmark === 'ds1000')
|
|
91
|
-
return '100';
|
|
92
|
-
if (benchmark === 'polyglot')
|
|
93
|
-
return '25';
|
|
94
|
-
if (benchmark === 'hotpotqa')
|
|
95
|
-
return '50';
|
|
96
|
-
if (benchmark === 'swebench')
|
|
97
|
-
return '10';
|
|
98
|
-
return '25';
|
|
99
|
-
}
|
|
100
|
-
return undefined;
|
|
101
|
-
}
|
|
102
|
-
function tierOptimizationProfile(tier) {
|
|
103
|
-
if (tier === 'smoke') {
|
|
104
|
-
return 'quick';
|
|
105
|
-
}
|
|
106
|
-
if (tier === 'expanded') {
|
|
107
|
-
return 'deep';
|
|
108
|
-
}
|
|
109
|
-
if (tier === 'sweep') {
|
|
110
|
-
return 'exhaustive';
|
|
111
|
-
}
|
|
112
|
-
return 'balanced';
|
|
113
|
-
}
|
|
114
|
-
function writeJson(path, value) {
|
|
115
|
-
writeFileSync(path, JSON.stringify(value, null, 2), 'utf-8');
|
|
116
|
-
}
|
|
1
|
+
import { activeProfile, builtinProfileEmulation, listConfigProfiles } from '../../config/profiles.js';
|
|
2
|
+
import { switchConfigProfile } from './config_profile_switch.js';
|
|
3
|
+
const USAGE = [
|
|
4
|
+
'Configuration profiles:',
|
|
5
|
+
' /profile list list built-in and saved profiles',
|
|
6
|
+
' /profile <name> switch this session to an existing profile',
|
|
7
|
+
' /profile use <name> same as above',
|
|
8
|
+
' /profile create <name> create and switch to a custom profile',
|
|
9
|
+
].join('\n');
|
|
10
|
+
const OLD_BENCHMARK_COMMANDS = new Set(['show', 'gates', 'command', 'run', 'optimize', 'sweep']);
|
|
117
11
|
export const profileCommand = {
|
|
118
12
|
name: 'profile',
|
|
119
|
-
description: '
|
|
120
|
-
usage: '<
|
|
13
|
+
description: 'List and switch configuration profiles',
|
|
14
|
+
usage: '[list|<name>|use <name>|create <name>]',
|
|
121
15
|
handler: async (args, ctx) => {
|
|
122
|
-
const [
|
|
123
|
-
if (!
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
if (subcommand === 'list') {
|
|
128
|
-
ctx.addMessage({
|
|
129
|
-
role: 'system',
|
|
130
|
-
content: [
|
|
131
|
-
'ZTC profiles:',
|
|
132
|
-
...listProfileManifests().map(profile => `- ${profile.id}: ${profile.name} — ${profile.description}`),
|
|
133
|
-
].join('\n'),
|
|
134
|
-
});
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
if (subcommand === 'show') {
|
|
138
|
-
const id = rest[0];
|
|
139
|
-
const manifest = id ? getProfileManifest(id) : undefined;
|
|
140
|
-
if (!manifest) {
|
|
141
|
-
ctx.addMessage({ role: 'system', content: 'Usage: /profile show <profile>' });
|
|
16
|
+
const [action, ...rest] = args;
|
|
17
|
+
if (!action || action === 'list' || action === 'configs' || action === 'help') {
|
|
18
|
+
if (rest.length > 0) {
|
|
19
|
+
ctx.addMessage({ role: 'system', content: USAGE });
|
|
142
20
|
return;
|
|
143
21
|
}
|
|
144
|
-
|
|
145
|
-
role: 'system',
|
|
146
|
-
content: [
|
|
147
|
-
`${manifest.name} (${manifest.id})`,
|
|
148
|
-
manifest.description,
|
|
149
|
-
'',
|
|
150
|
-
`source: ${manifest.source.repo} ${manifest.source.promptPath}`,
|
|
151
|
-
`runtime: compatibility ${manifest.runtime.compatibilityVersion}, ${manifest.runtime.promptProfile}`,
|
|
152
|
-
`benchmark plan: ${manifest.runtime.benchmarkPlan.join(', ')}`,
|
|
153
|
-
`failure policy: ${manifest.failurePolicy}`,
|
|
154
|
-
'',
|
|
155
|
-
'Gates:',
|
|
156
|
-
...manifest.gates.map(formatGate),
|
|
157
|
-
].join('\n'),
|
|
158
|
-
});
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
if (subcommand === 'gates') {
|
|
162
|
-
const id = rest[0];
|
|
163
|
-
const manifest = id ? getProfileManifest(id) : undefined;
|
|
164
|
-
if (!manifest) {
|
|
165
|
-
ctx.addMessage({ role: 'system', content: 'Usage: /profile gates <profile>' });
|
|
22
|
+
if (action === 'help') {
|
|
23
|
+
ctx.addMessage({ role: 'system', content: USAGE });
|
|
166
24
|
return;
|
|
167
25
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
26
|
+
const names = await listConfigProfiles();
|
|
27
|
+
const lines = names.map(name => {
|
|
28
|
+
const emulation = builtinProfileEmulation(name);
|
|
29
|
+
return ` ${name === activeProfile() ? '*' : ' '} ${name}${emulation ? ` (built-in: ${emulation} emulation)` : ''}`;
|
|
171
30
|
});
|
|
31
|
+
ctx.addMessage({ role: 'system', content: `Config profiles (* active):\n${lines.join('\n')}${!action ? `\n\n${USAGE}` : ''}` });
|
|
172
32
|
return;
|
|
173
33
|
}
|
|
174
|
-
if (
|
|
175
|
-
|
|
176
|
-
const limit = findFlagValue(rest, '--limit');
|
|
177
|
-
const tier = findFlagValue(rest, '--tier');
|
|
178
|
-
const command = profileId && gateName ? gateCommand(profileId, gateName, limit, tier) : [];
|
|
179
|
-
if (command.length === 0) {
|
|
180
|
-
ctx.addMessage({ role: 'system', content: 'Usage: /profile command <profile> <gate> [--limit N]' });
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
const rendered = command.map(shellQuote).join(' ');
|
|
184
|
-
if (subcommand === 'command') {
|
|
185
|
-
ctx.addMessage({ role: 'system', content: rendered });
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
const result = await ctx.shell.run(rendered);
|
|
189
|
-
ctx.addMessage({
|
|
190
|
-
role: 'system',
|
|
191
|
-
content: [
|
|
192
|
-
`Profile gate command: ${rendered}`,
|
|
193
|
-
`Exit code: ${result.exitCode}`,
|
|
194
|
-
result.stdout,
|
|
195
|
-
result.stderr,
|
|
196
|
-
].filter(Boolean).join('\n'),
|
|
197
|
-
});
|
|
34
|
+
if (OLD_BENCHMARK_COMMANDS.has(action)) {
|
|
35
|
+
ctx.addMessage({ role: 'system', content: 'Use /emulation to inspect prompt modes. Profile benchmark gates remain available through npm run profiles:baseline.' });
|
|
198
36
|
return;
|
|
199
37
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if (command.length === 0) {
|
|
205
|
-
ctx.addMessage({ role: 'system', content: 'Usage: /profile optimize <profile> [--profile quick|balanced|deep|exhaustive] [--run]' });
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
const rendered = `/${command.join(' ')}`;
|
|
209
|
-
if (!rest.includes('--run')) {
|
|
210
|
-
ctx.addMessage({ role: 'system', content: rendered });
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
await optimizeCommand.handler(command.slice(1), ctx);
|
|
38
|
+
const namedCommand = action === 'use' || action === 'switch' || action === 'create';
|
|
39
|
+
const name = namedCommand ? rest[0] : action;
|
|
40
|
+
if (!name || (namedCommand ? rest.length !== 1 : rest.length !== 0)) {
|
|
41
|
+
ctx.addMessage({ role: 'system', content: USAGE });
|
|
214
42
|
return;
|
|
215
43
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
ctx.addMessage({ role: 'system', content: 'Usage: /profile sweep <profile> [--tier smoke|initial|expanded|sweep]' });
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
const runDir = profileRunDir(manifest.id, tier);
|
|
225
|
-
mkdirSync(runDir, { recursive: true });
|
|
226
|
-
const results = [];
|
|
227
|
-
for (const gate of manifest.gates.filter(gate => gate.status === 'available' && gate.command)) {
|
|
228
|
-
const limit = tierLimit(tier, gate.benchmark);
|
|
229
|
-
const command = replaceLimit(getProfileGateCommand(gate, normalizeTier(tier)), limit).map(shellQuote).join(' ');
|
|
230
|
-
const result = await ctx.shell.run(command);
|
|
231
|
-
const record = {
|
|
232
|
-
gate: gate.name,
|
|
233
|
-
benchmark: gate.benchmark,
|
|
234
|
-
command,
|
|
235
|
-
exit_code: result.exitCode,
|
|
236
|
-
stdout_path: join(runDir, `${gate.benchmark}-stdout.txt`),
|
|
237
|
-
stderr_path: join(runDir, `${gate.benchmark}-stderr.txt`),
|
|
238
|
-
};
|
|
239
|
-
writeFileSync(record.stdout_path, result.stdout || '', 'utf-8');
|
|
240
|
-
writeFileSync(record.stderr_path, result.stderr || '', 'utf-8');
|
|
241
|
-
results.push(record);
|
|
242
|
-
}
|
|
243
|
-
let optimizeCommandLine = null;
|
|
244
|
-
if (tier !== 'smoke') {
|
|
245
|
-
const command = buildProfileOptimizationCommand(manifest.id, { profile: tierOptimizationProfile(tier) });
|
|
246
|
-
optimizeCommandLine = `/${command.join(' ')} --run`;
|
|
247
|
-
await optimizeCommand.handler([...command.slice(1), '--run'], ctx);
|
|
248
|
-
}
|
|
249
|
-
const summaryPath = join(runDir, 'summary.json');
|
|
250
|
-
writeJson(summaryPath, {
|
|
251
|
-
schema_version: 1,
|
|
252
|
-
created_at: new Date().toISOString(),
|
|
253
|
-
profile: manifest.id,
|
|
254
|
-
tier,
|
|
255
|
-
provider: ctx.config.getProvider(),
|
|
256
|
-
model: ctx.config.getModel?.() || ctx.config.get().model,
|
|
257
|
-
optimize_command: optimizeCommandLine,
|
|
258
|
-
results,
|
|
259
|
-
});
|
|
260
|
-
ctx.addMessage({
|
|
261
|
-
role: 'system',
|
|
262
|
-
content: [
|
|
263
|
-
`Profile sweep complete: ${manifest.id} (${tier})`,
|
|
264
|
-
`Run dir: ${runDir}`,
|
|
265
|
-
`Summary: ${summaryPath}`,
|
|
266
|
-
`Gates: ${results.length}`,
|
|
267
|
-
optimizeCommandLine ? `Optimize: ${optimizeCommandLine}` : 'Optimize: skipped for smoke tier',
|
|
268
|
-
].join('\n'),
|
|
269
|
-
});
|
|
270
|
-
return;
|
|
44
|
+
try {
|
|
45
|
+
await switchConfigProfile(ctx, name, action === 'create');
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
ctx.addMessage({ role: 'system', content: `Profile switch failed: ${error.message}` });
|
|
271
49
|
}
|
|
272
|
-
ctx.addMessage({ role: 'system', content: helpText() });
|
|
273
50
|
},
|
|
274
51
|
};
|
|
@@ -9,6 +9,17 @@ export declare const PROVIDER_CHOICES: ({
|
|
|
9
9
|
description: string;
|
|
10
10
|
aliases?: undefined;
|
|
11
11
|
})[];
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Providers ZTC will never hand to the Router branch, either because Router
|
|
14
|
+
* cannot serve them over HTTP or because nesting them would be wrong.
|
|
15
|
+
*
|
|
16
|
+
* Router's own `listProviderCatalog` (GET /v1/models) is the source of truth for
|
|
17
|
+
* what it offers. This is deliberately an exclusion list: when Router adds a
|
|
18
|
+
* provider, ZTC offers it without a ZTC change. Only add an entry here to block
|
|
19
|
+
* one, never to enable one.
|
|
20
|
+
*/
|
|
21
|
+
export declare const ROUTER_EXCLUDED_PROVIDERS: string[];
|
|
22
|
+
/** True when a provider can be dispatched as a Router upstream. */
|
|
23
|
+
export declare function canRouteThroughRouter(provider: string): boolean;
|
|
13
24
|
export declare function canonicalProvider(value: string): string | undefined;
|
|
14
25
|
export declare function isLocalSubscription(provider: string): boolean;
|
|
@@ -6,6 +6,7 @@ export const PROVIDER_CHOICES = [
|
|
|
6
6
|
{ id: 'gemini', label: 'Google Gemini', description: 'Direct API key', aliases: ['google'] },
|
|
7
7
|
{ id: 'deepseek', label: 'DeepSeek', description: 'Direct API key' },
|
|
8
8
|
{ id: 'kimi', label: 'Kimi (Moonshot)', description: 'Direct API key', aliases: ['moonshot'] },
|
|
9
|
+
{ id: 'fireworks', label: 'Fireworks', description: 'Direct API key or Zerg Router', aliases: ['fireworks-ai'] },
|
|
9
10
|
{ id: 'inception', label: 'Inception', description: 'Direct API key' },
|
|
10
11
|
{ id: 'clarifai', label: 'Clarifai', description: 'Direct API key' },
|
|
11
12
|
{ id: 'openai_compatible', label: 'OpenAI-compatible', description: 'Local or custom endpoint', aliases: ['openai-compatible', 'local', 'ollama'] },
|
|
@@ -15,8 +16,34 @@ export const PROVIDER_CHOICES = [
|
|
|
15
16
|
{ id: 'codex_legacy', label: 'Codex Legacy', description: 'Local Codex credentials' },
|
|
16
17
|
{ id: 'kimi_code', label: 'Kimi Code', description: 'Local Kimi credentials', aliases: ['kimi-code'] },
|
|
17
18
|
];
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Providers ZTC will never hand to the Router branch, either because Router
|
|
21
|
+
* cannot serve them over HTTP or because nesting them would be wrong.
|
|
22
|
+
*
|
|
23
|
+
* Router's own `listProviderCatalog` (GET /v1/models) is the source of truth for
|
|
24
|
+
* what it offers. This is deliberately an exclusion list: when Router adds a
|
|
25
|
+
* provider, ZTC offers it without a ZTC change. Only add an entry here to block
|
|
26
|
+
* one, never to enable one.
|
|
27
|
+
*/
|
|
28
|
+
export const ROUTER_EXCLUDED_PROVIDERS = [
|
|
29
|
+
// Local subscription logins: inference runs on this machine, not Router, and
|
|
30
|
+
// Router holds no credential for them.
|
|
31
|
+
'claude_code',
|
|
32
|
+
'claude_code_direct',
|
|
33
|
+
'codex_app_server',
|
|
34
|
+
'codex_legacy',
|
|
35
|
+
'kimi_code',
|
|
36
|
+
// Routing Router through Router would nest the same gateway in itself.
|
|
37
|
+
'zergrouter',
|
|
38
|
+
// A custom OpenAI-compatible endpoint names its own base URL and models.
|
|
39
|
+
'openai_compatible',
|
|
40
|
+
];
|
|
41
|
+
const ROUTER_EXCLUDED = new Set(ROUTER_EXCLUDED_PROVIDERS);
|
|
42
|
+
/** True when a provider can be dispatched as a Router upstream. */
|
|
43
|
+
export function canRouteThroughRouter(provider) {
|
|
44
|
+
const normalized = normalizeProviderId(provider);
|
|
45
|
+
return normalized.length > 0 && !ROUTER_EXCLUDED.has(normalized);
|
|
46
|
+
}
|
|
20
47
|
export function canonicalProvider(value) {
|
|
21
48
|
const normalized = normalizeProviderId(value);
|
|
22
49
|
return PROVIDER_CHOICES.find(choice => choice.id === normalized || choice.aliases?.includes(normalized))?.id;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { getDefaultModelForProvider } from '../../config/defaults.js';
|
|
2
|
+
import { isBuildableResolvedConfig } from './config.js';
|
|
3
|
+
import { applyRuntimeModelSelection, getEffectiveModel } from './runtime_model.js';
|
|
4
|
+
import { canRouteThroughRouter, canonicalProvider } from './provider_choices.js';
|
|
5
|
+
import { resolveModelAndProvider } from '../../panes/model_catalog.js';
|
|
6
|
+
import { getDefaultModel } from '../../utils/models.js';
|
|
7
|
+
/** Apply a provider and optional model in one validated config transition. */
|
|
8
|
+
export async function selectProviderModel(args, ctx) {
|
|
9
|
+
const values = args.filter(Boolean);
|
|
10
|
+
const current = ctx.config.getProvider();
|
|
11
|
+
const choice = canonicalProvider(values[0]);
|
|
12
|
+
const routed = choice === 'zergrouter' || choice === 'modal';
|
|
13
|
+
// Router's catalog is the source of truth, so a Router upstream ZTC has no
|
|
14
|
+
// PROVIDER_CHOICES entry for still selects; the exclusion rule gates it.
|
|
15
|
+
const upstream = values[1] ? (canonicalProvider(values[1]) ?? values[1]) : undefined;
|
|
16
|
+
const provider = choice === 'zergrouter'
|
|
17
|
+
? (upstream ?? (canRouteThroughRouter(current) ? current : 'anthropic'))
|
|
18
|
+
: choice;
|
|
19
|
+
if (!provider || (routed && !canRouteThroughRouter(provider))) {
|
|
20
|
+
ctx.addMessage({ role: 'system', content: `Unknown provider or unsupported Router provider: ${values.join(' ')}. Run /model to see the choices.` });
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (values.length > (choice === 'zergrouter' ? 3 : 2)) {
|
|
24
|
+
ctx.addMessage({ role: 'system', content: 'Usage: /model <provider> [model] or /model zergrouter <upstream-provider> [model]' });
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const authMode = routed ? 'zrouter' : 'direct';
|
|
28
|
+
const defaultModel = provider === 'inception' ? getDefaultModel(provider) : getDefaultModelForProvider(provider);
|
|
29
|
+
const explicitModel = values[choice === 'zergrouter' ? 2 : 1];
|
|
30
|
+
const model = explicitModel || (provider === current ? getEffectiveModel(ctx)
|
|
31
|
+
: provider === 'openai_compatible'
|
|
32
|
+
? (ctx.config.getOpenAICompatibleSettings?.().defaultModel || getDefaultModelForProvider(provider))
|
|
33
|
+
: resolveModelAndProvider(defaultModel, provider).model);
|
|
34
|
+
try {
|
|
35
|
+
const llm = ctx.config.resolveLLMBackendConfig({ provider, model,
|
|
36
|
+
config: { ...ctx.config.get(), provider, model, authMode } });
|
|
37
|
+
if (llm.authMode !== authMode) {
|
|
38
|
+
throw new Error('Routing is pinned by ZTC_LLM_AUTH_MODE/ZERG_LLM_AUTH_MODE. Unset it before switching routes.');
|
|
39
|
+
}
|
|
40
|
+
if (!isBuildableResolvedConfig(llm)) {
|
|
41
|
+
throw new Error(provider === 'openai_compatible'
|
|
42
|
+
? 'Configure a local or custom endpoint with /config openai-compat, then select /model openai_compatible.'
|
|
43
|
+
: `No usable direct credentials for ${provider}. Set /config key ${provider} <api-key> or configure this provider's local login.`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
ctx.addMessage({ role: 'system', content: `✗ ${error instanceof Error ? error.message : String(error)} Keeping the current selection.` });
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
ctx.config.set('authMode', authMode);
|
|
51
|
+
ctx.config.setProvider(provider);
|
|
52
|
+
// CLI overrides can still expose the previous provider/model until hot-swap.
|
|
53
|
+
ctx.config.set('model', model);
|
|
54
|
+
await ctx.config.save();
|
|
55
|
+
applyRuntimeModelSelection(ctx, provider, model);
|
|
56
|
+
ctx.addMessage({ role: 'system', content: [
|
|
57
|
+
'✓ Model selected',
|
|
58
|
+
`provider: ${provider}`,
|
|
59
|
+
`model: ${model}`,
|
|
60
|
+
`route: ${routed ? 'zergrouter' : 'direct'}`,
|
|
61
|
+
`scope: ${ctx.config.getConfigScope?.() || 'global'}`,
|
|
62
|
+
].join('\n') });
|
|
63
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import { Command, CommandContext, SettingCategory } from './types.js';
|
|
2
2
|
export declare function buildSettingsCategories(ctx: CommandContext): Promise<SettingCategory[]>;
|
|
3
3
|
export declare const settingsCommand: Command;
|
|
4
|
+
/** A separate confirmation page keeps destructive reset out of ordinary row editing. */
|
|
5
|
+
export declare function openFactoryResetConfirmation(ctx: CommandContext, onDone?: () => void | Promise<void>): void;
|