fix(tui): reserve /runtime and block local tool-loop fallback
This commit is contained in:
@@ -113,6 +113,11 @@ describe('parseCommand', () => {
|
||||
expect(parseCommand('/backend ollama')).toEqual({ type: 'backend', provider: 'ollama' });
|
||||
});
|
||||
|
||||
it('parses /runtime command', () => {
|
||||
expect(parseCommand('/runtime')).toEqual({ type: 'runtime' });
|
||||
expect(parseCommand('/runtime status')).toEqual({ type: 'runtime', input: 'status' });
|
||||
});
|
||||
|
||||
it('parses /transfer command', () => {
|
||||
expect(parseCommand('/transfer telegram')).toEqual({ type: 'transfer', target: 'telegram' });
|
||||
expect(parseCommand('/transfer')).toEqual({ type: 'transfer', target: '' });
|
||||
@@ -146,6 +151,7 @@ describe('getHelpText', () => {
|
||||
expect(help).toContain('/help');
|
||||
expect(help).toContain('/paste');
|
||||
expect(help).toContain('/model');
|
||||
expect(help).toContain('/runtime');
|
||||
expect(help).toContain('/tools');
|
||||
expect(help).toContain('/research');
|
||||
expect(help).toContain('/council');
|
||||
@@ -215,6 +221,11 @@ describe('getCommandCompletions', () => {
|
||||
const completions = getCommandCompletions('/council pre');
|
||||
expect(completions).toEqual(['/council preflight']);
|
||||
});
|
||||
|
||||
it('completes /runtime command', () => {
|
||||
const completions = getCommandCompletions('/ru');
|
||||
expect(completions).toContain('/runtime');
|
||||
});
|
||||
});
|
||||
|
||||
describe('isToolInventoryQuery', () => {
|
||||
|
||||
@@ -14,6 +14,7 @@ export type Command =
|
||||
| { type: 'verbose' }
|
||||
| { type: 'model'; name?: string; providerModel?: string }
|
||||
| { type: 'backend'; provider?: string }
|
||||
| { type: 'runtime'; input?: string }
|
||||
| { type: 'login'; provider?: string }
|
||||
| { type: 'transfer'; target: string }
|
||||
| { type: 'pair'; action?: 'generate' | 'list' | 'revoke'; args?: string }
|
||||
@@ -154,6 +155,15 @@ export function parseCommand(input: string): Command | null {
|
||||
return { type: 'backend', provider };
|
||||
}
|
||||
|
||||
// Runtime backend mode control (daemon/channel command; reserved in TUI)
|
||||
if (trimmed === '/runtime') {
|
||||
return { type: 'runtime' };
|
||||
}
|
||||
if (trimmed.startsWith('/runtime ')) {
|
||||
const input = trimmed.slice('/runtime '.length).trim();
|
||||
return { type: 'runtime', input };
|
||||
}
|
||||
|
||||
// Transfer
|
||||
if (trimmed === '/transfer') {
|
||||
return { type: 'transfer', target: '' };
|
||||
@@ -223,6 +233,7 @@ Commands:
|
||||
/model [name] Show or switch model tier (local, default, fast, complex)
|
||||
/model <tier> <p/m> Change tier's provider/model (e.g. /model default anthropic/claude-sonnet-4)
|
||||
/backend [provider] Show or switch local backend (ollama, llamacpp)
|
||||
/runtime [args] Runtime backend mode control (daemon/channel sessions)
|
||||
/research <task> Delegate a task to the configured research agent
|
||||
/council <task> Run the councils pipeline for a task
|
||||
/council preflight Check council tier routing, endpoint/auth mode, and probe latency
|
||||
@@ -261,6 +272,7 @@ export const SLASH_COMMANDS = [
|
||||
'/tools',
|
||||
'/model',
|
||||
'/backend',
|
||||
'/runtime',
|
||||
'/research',
|
||||
'/council',
|
||||
'/reset',
|
||||
@@ -293,6 +305,7 @@ export const COMMAND_TOOLTIPS: Record<string, string> = {
|
||||
'/tools': 'Show authoritative runtime tool list for this session',
|
||||
'/model': 'Show or switch model (local, default, fast, complex)',
|
||||
'/backend': 'Show or switch local backend (ollama, llamacpp)',
|
||||
'/runtime': 'Runtime backend mode control (daemon/channel command; not local TUI backend switch)',
|
||||
'/research': 'Delegate a task to the configured research agent',
|
||||
'/council': 'Run the councils pipeline for a task; use "/council preflight" for route/auth checks',
|
||||
'/reset': 'Clear conversation history',
|
||||
|
||||
@@ -563,6 +563,18 @@ export function App({
|
||||
return;
|
||||
}
|
||||
|
||||
case 'runtime': {
|
||||
pushAssistantMessage(
|
||||
'Runtime backend mode command is not available in fullscreen TUI mode.\n'
|
||||
+ 'Use it in daemon/channel sessions:\n'
|
||||
+ '/runtime status\n'
|
||||
+ '/runtime activate pi\n'
|
||||
+ '/runtime deactivate pi\n'
|
||||
+ '/runtime use config',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
case 'login': {
|
||||
const provider = (command.provider ?? '').trim().toLowerCase();
|
||||
if (!provider) {
|
||||
|
||||
@@ -403,6 +403,29 @@ describe('MinimalTui backend command', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('prints guidance when /runtime is invoked in TUI mode', async () => {
|
||||
const mockSession = {
|
||||
id: 'test',
|
||||
getHistory: () => [],
|
||||
addMessage: vi.fn(),
|
||||
clear: vi.fn(),
|
||||
replaceHistory: vi.fn(),
|
||||
};
|
||||
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
||||
try {
|
||||
const tui = new MinimalTui({
|
||||
session: asSession(mockSession),
|
||||
modelClient: asModelClient({}),
|
||||
systemPrompt: 'test',
|
||||
});
|
||||
|
||||
await minimalTuiPrivates(tui).handleCommand({ type: 'runtime', input: 'status' });
|
||||
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('Runtime backend mode command is not available in this TUI mode.'));
|
||||
} finally {
|
||||
logSpy.mockRestore();
|
||||
}
|
||||
});
|
||||
|
||||
it('collects multiline input from /paste and sends as one message', async () => {
|
||||
const mockSession = {
|
||||
id: 'test',
|
||||
|
||||
@@ -524,6 +524,10 @@ export class MinimalTui {
|
||||
await this.handleBackendCommand(command.provider);
|
||||
break;
|
||||
|
||||
case 'runtime':
|
||||
this.handleRuntimeCommand(command.input);
|
||||
break;
|
||||
|
||||
case 'login':
|
||||
await this.handleLoginCommand(command.provider);
|
||||
break;
|
||||
@@ -895,6 +899,16 @@ export class MinimalTui {
|
||||
console.log(`${colors.gray}Switched to backend: ${provider}${colors.reset}\n`);
|
||||
}
|
||||
|
||||
private handleRuntimeCommand(_input?: string): void {
|
||||
console.log(`${colors.gray}Runtime backend mode command is not available in this TUI mode.${colors.reset}`);
|
||||
console.log(`${colors.gray}Use it in daemon/channel sessions:${colors.reset}`);
|
||||
console.log(' /runtime status');
|
||||
console.log(' /runtime activate pi');
|
||||
console.log(' /runtime deactivate pi');
|
||||
console.log(' /runtime use config');
|
||||
console.log('');
|
||||
}
|
||||
|
||||
private async stopBackend(provider: string): Promise<void> {
|
||||
try {
|
||||
const { exec } = await import('child_process');
|
||||
|
||||
Reference in New Issue
Block a user