Unify TUI runtime commands with gateway and harden gateway restart

This commit is contained in:
William Valentin
2026-02-24 13:14:53 -08:00
parent db2f697741
commit 37be391a40
24 changed files with 1253 additions and 120 deletions
+31 -78
View File
@@ -18,6 +18,11 @@ import { ToolRegistry, ToolExecutor } from '../tools/index.js';
import { SessionManager } from '../session/index.js';
import { AgentConfigRegistry, AgentRouter } from '../agents/index.js';
import type { CommandRegistry } from '../commands/index.js';
import {
executeRuntimeBackendModeCommand,
formatRuntimeBackendStatusLine,
type RuntimeBackendMode,
} from '../commands/index.js';
import type { ComponentRegistry } from '../intents/index.js';
import type { RoutingPolicy } from '../routing/index.js';
import type { HookEngine } from '../hooks/index.js';
@@ -31,7 +36,7 @@ import { dirname, resolve } from 'path';
import { loadCouncilScaffoldSafe } from '../councils/scaffold.js';
import { buildCouncilPreflightReport, shouldRunCouncilPreflight } from '../councils/preflight.js';
export type BackendRuntimeMode = 'config_default' | 'force_native' | 'force_pi_embedded';
export type BackendRuntimeMode = RuntimeBackendMode;
function buildProviderConfigMap(config: Config): Partial<Record<ModelProvider, ModelConfig>> {
const providerConfigs: Partial<Record<ModelProvider, ModelConfig>> = {};
@@ -393,17 +398,29 @@ export function createMessageRouter(deps: {
return requestedBackend;
}
function formatBackendStatusLine(activeTier: string): string {
const mode = getBackendMode();
const configuredDefault = getConfiguredOrFallbackDefaultBackend();
const effectiveDefault = resolveRoutableBackend(getEffectiveDefaultBackend());
const availableExternal = Object.keys(deps.externalBackends ?? {}).sort().join(', ') || 'none';
return [
`Flynn is running. Active model tier: ${activeTier}. Backend: ${effectiveDefault}`,
`Backend mode: ${mode}`,
`Configured default: ${configuredDefault}`,
`Available external backends: ${availableExternal}`,
].join('\n');
function listAvailableExternalBackends(): string[] {
return Object.keys(deps.externalBackends ?? {});
}
function formatBackendStatus(activeTier: string): string {
return formatRuntimeBackendStatusLine({
getActiveTier: () => activeTier,
getBackendMode,
getConfiguredDefaultBackend: getConfiguredOrFallbackDefaultBackend,
getEffectiveDefaultBackend: () => resolveRoutableBackend(getEffectiveDefaultBackend()),
getAvailableExternalBackends: listAvailableExternalBackends,
});
}
function executeBackendCommand(inputRaw: string, activeTier: string): string {
return executeRuntimeBackendModeCommand(inputRaw, {
getActiveTier: () => activeTier,
getBackendMode,
setBackendMode: deps.setBackendMode,
getConfiguredDefaultBackend: getConfiguredOrFallbackDefaultBackend,
getEffectiveDefaultBackend: () => resolveRoutableBackend(getEffectiveDefaultBackend()),
getAvailableExternalBackends: listAvailableExternalBackends,
});
}
async function maybeBuildTtsAttachment(responseText: string, channel: string) {
@@ -823,7 +840,7 @@ export function createMessageRouter(deps: {
rawInput: commandInput,
services: {
getStatus: () => {
return formatBackendStatusLine(agent.getModelTier());
return formatBackendStatus(agent.getModelTier());
},
getTools: () => {
const names = new Set(deps.toolRegistry.list().map((tool: Tool) => tool.name));
@@ -1203,71 +1220,7 @@ export function createMessageRouter(deps: {
return `Session transferred to ${destinationLabel}`;
},
backendCommand: (inputRaw: string) => {
let normalized = inputRaw.trim().toLowerCase();
// Accept both subcommand-only input ("status") and accidental full-command
// input ("/runtime status", "runtime status", "/backend status").
normalized = normalized.replace(/^(?:\/)?(?:runtime|backend)\b/, '').trim();
normalized = normalized.replace(/^\//, '').trim();
if (!normalized || normalized === 'status' || normalized === 'show') {
return formatBackendStatusLine(agent.getModelTier());
}
if (!deps.setBackendMode) {
return 'Backend mode control is not available in this runtime.';
}
if (
normalized === 'activate pi'
|| normalized === 'activate pi_embedded'
|| normalized === 'activate pi-embedded'
) {
deps.setBackendMode('force_pi_embedded');
return [
'Pi embedded backend activated globally.',
formatBackendStatusLine(agent.getModelTier()),
].join('\n\n');
}
if (
normalized === 'deactivate pi'
|| normalized === 'deactivate pi_embedded'
|| normalized === 'deactivate pi-embedded'
) {
deps.setBackendMode('force_native');
return [
'Pi embedded backend deactivated globally. Native is now forced for Pi-routed turns.',
formatBackendStatusLine(agent.getModelTier()),
].join('\n\n');
}
if (
normalized === 'use config'
|| normalized === 'reset'
|| normalized === 'auto'
|| normalized === 'config'
) {
deps.setBackendMode('config_default');
return [
'Backend mode reset to config default.',
formatBackendStatusLine(agent.getModelTier()),
].join('\n\n');
}
return [
'Usage:',
'/runtime status',
'/runtime activate pi',
'/runtime deactivate pi',
'/runtime use config',
'',
'Alias:',
'/backend status',
'/backend activate pi',
'/backend deactivate pi',
'/backend use config',
].join('\n');
},
backendCommand: (inputRaw: string) => executeBackendCommand(inputRaw, agent.getModelTier()),
getApprovals: () => {
if (!deps.hookEngine) {