fix(webchat): route slash commands through agent command fast-path

This commit is contained in:
William Valentin
2026-02-18 16:18:27 -08:00
parent b35a8675b5
commit abd37342fa
2 changed files with 42 additions and 53 deletions
+12 -1
View File
@@ -1,6 +1,6 @@
{
"version": "1.0",
"updated_at": "2026-02-18",
"updated_at": "2026-02-19",
"description": "Tracks the status of all Flynn plans and implementation phases",
"plans": {
"makefile-skills-convenience-targets": {
@@ -5548,6 +5548,17 @@
"docs/plans/state.json"
],
"test_status": "pnpm test:run src/channels/telegram/adapter.test.ts src/config/schema.test.ts src/gateway/handlers/services.test.ts src/cli/doctor.test.ts + pnpm typecheck passing"
},
"webchat-slash-command-agent-fastpath-fix": {
"status": "completed",
"date": "2026-02-19",
"updated": "2026-02-19",
"summary": "Fixed WebChat slash command reliability by routing /reset, /compact, /usage, /status, and /model through agent.send command metadata fast-path instead of mixed local RPC shortcuts. This resolves /model returning unknown and keeps command behavior aligned with backend command registry.",
"files_modified": [
"src/gateway/ui/pages/chat.js",
"docs/plans/state.json"
],
"test_status": "pnpm typecheck + pnpm test:run src/commands/registry.test.ts src/commands/builtin/index.test.ts passing"
}
},
"overall_progress": {
+30 -52
View File
@@ -368,6 +368,20 @@ function showSystemMessage(content) {
scrollToBottom();
}
async function executeAgentSlashCommand(client, command, commandArgs = '') {
const normalizedArgs = (commandArgs ?? '').trim();
const message = normalizedArgs ? `/${command} ${normalizedArgs}` : `/${command}`;
const metadata = {
isCommand: true,
command,
...(normalizedArgs ? { commandArgs: normalizedArgs } : {}),
};
const stream = client.stream('agent.send', { message, metadata });
const done = await stream.result;
return done?.content ?? done?.text ?? '';
}
async function handleSlashCommand(cmd, client) {
switch (cmd.type) {
case 'help': {
@@ -381,7 +395,7 @@ async function handleSlashCommand(cmd, client) {
'| `/compact` | Ask the agent to compact context |',
'| `/usage` | Show token usage stats |',
'| `/status` | Show system health |',
'| `/model` | Show current model info |',
'| `/model [tier|provider]` | Show or set model tier/provider |',
'',
'Type `/` to see autocomplete suggestions.',
];
@@ -391,14 +405,9 @@ async function handleSlashCommand(cmd, client) {
case 'reset': {
try {
// Send reset command via metadata
const stream = client.stream('agent.send', {
message: '/reset',
metadata: { isCommand: true, command: 'reset' },
});
await stream.result;
const result = await executeAgentSlashCommand(client, 'reset');
_elements.messages.innerHTML = '';
showSystemMessage('Session reset.');
showSystemMessage(result || 'Session reset.');
} catch (err) {
showSystemMessage(`Failed to reset: ${err.message}`);
}
@@ -406,33 +415,19 @@ async function handleSlashCommand(cmd, client) {
}
case 'compact': {
// Send as a regular message — the agent will interpret the request
showSystemMessage('Requesting context compaction...');
return false; // Let it pass through as a normal message
try {
const result = await executeAgentSlashCommand(client, 'compact');
showSystemMessage(result || 'Compaction requested.');
} catch (err) {
showSystemMessage(`Failed to compact: ${err.message}`);
}
return true;
}
case 'usage': {
try {
const result = await client.call('system.tokenUsage');
const sessions = result.sessions ?? [];
if (sessions.length === 0) {
showSystemMessage('No usage data available.');
} else {
const lines = ['**Token Usage**', ''];
let totalIn = 0, totalOut = 0, totalCalls = 0;
for (const s of sessions) {
totalIn += s.total?.inputTokens ?? 0;
totalOut += s.total?.outputTokens ?? 0;
totalCalls += s.total?.calls ?? 0;
}
lines.push(`**Input:** ${totalIn.toLocaleString()} tokens`);
lines.push(`**Output:** ${totalOut.toLocaleString()} tokens`);
lines.push(`**API Calls:** ${totalCalls}`);
if (sessions.length > 1) {
lines.push(`**Sessions:** ${sessions.length}`);
}
showSystemMessage(lines.join('\n'));
}
const result = await executeAgentSlashCommand(client, 'usage');
showSystemMessage(result || 'No usage data available.');
} catch (err) {
showSystemMessage(`Failed to fetch usage: ${err.message}`);
}
@@ -441,24 +436,8 @@ async function handleSlashCommand(cmd, client) {
case 'status': {
try {
const result = await client.call('system.health');
const lines = [
'**System Status**',
'',
`**Uptime:** ${result.uptime ?? 'unknown'}`,
`**Status:** ${result.status ?? 'unknown'}`,
];
if (result.channels) {
lines.push('', '**Channels:**');
for (const ch of result.channels) {
const dot = ch.status === 'connected' ? '\\*' : '-';
lines.push(` ${dot} ${ch.name}: ${ch.status}`);
}
}
if (result.model) {
lines.push('', `**Model:** ${result.model}`);
}
showSystemMessage(lines.join('\n'));
const result = await executeAgentSlashCommand(client, 'status');
showSystemMessage(result || 'Status unavailable.');
} catch (err) {
showSystemMessage(`Failed to fetch status: ${err.message}`);
}
@@ -467,9 +446,8 @@ async function handleSlashCommand(cmd, client) {
case 'model': {
try {
const result = await client.call('system.health');
const model = result.model ?? result.config?.model ?? 'unknown';
showSystemMessage(`**Current Model:** ${model}`);
const result = await executeAgentSlashCommand(client, 'model', cmd.args ?? '');
showSystemMessage(result || 'Model info unavailable.');
} catch (err) {
showSystemMessage(`Failed to fetch model info: ${err.message}`);
}