feat(chat): add /stop cancellation command across gateway and telegram

This commit is contained in:
William Valentin
2026-02-19 09:52:57 -08:00
parent 027f7ad283
commit cd74b1e78a
9 changed files with 203 additions and 11 deletions
+29 -1
View File
@@ -1,6 +1,6 @@
import { describe, it, expect, vi } from 'vitest';
import { createApproveCommand, createApprovalsCommand, createContextCommand, createDenyCommand, createElevateCommand, createModelCommand, createQueueCommand, createResearchCommand, createSkillCommand, createTransferCommand } from './index.js';
import { createApproveCommand, createApprovalsCommand, createContextCommand, createDenyCommand, createElevateCommand, createModelCommand, createQueueCommand, createResearchCommand, createSkillCommand, createStopCommand, createTransferCommand } from './index.js';
describe('builtin /model command', () => {
it('passes through the full argument string', async () => {
@@ -198,6 +198,34 @@ describe('builtin /transfer command', () => {
});
});
describe('builtin /stop command', () => {
it('calls cancelRun service', async () => {
const cmd = createStopCommand();
const cancelRun = vi.fn(() => 'Cancellation requested.');
const result = await cmd.execute([], {
channel: 'test',
senderId: 'user',
sessionId: 's1',
rawInput: '/stop',
services: { cancelRun },
});
expect(cancelRun).toHaveBeenCalledOnce();
expect(result).toEqual({ handled: true, text: 'Cancellation requested.' });
});
it('returns not-available when service is missing', async () => {
const cmd = createStopCommand();
const result = await cmd.execute([], {
channel: 'test',
senderId: 'user',
sessionId: 's1',
rawInput: '/stop',
services: {},
});
expect(result).toEqual({ handled: true, text: 'Stop command is not available in this session.' });
});
});
describe('builtin approval commands', () => {
it('calls getApprovals for /approvals', async () => {
const cmd = createApprovalsCommand();
+18
View File
@@ -144,6 +144,23 @@ export function createResetCommand(): CommandDefinition {
};
}
export function createStopCommand(): CommandDefinition {
return {
name: 'stop',
aliases: ['cancel'],
description: 'Stop the current in-flight run',
execute: async (_args, ctx) => {
if (!ctx.services?.cancelRun) {
return notAvailable('Stop command');
}
return {
handled: true,
text: await ctx.services.cancelRun(),
};
},
};
}
export function createElevateCommand(): CommandDefinition {
return {
name: 'elevate',
@@ -309,6 +326,7 @@ export function registerBuiltinCommands(registry: CommandRegistry): void {
registry.register(createModelCommand());
registry.register(createCompactCommand());
registry.register(createResetCommand());
registry.register(createStopCommand());
registry.register(createElevateCommand());
registry.register(createQueueCommand());
registry.register(createTransferCommand());
+1
View File
@@ -8,6 +8,7 @@ export {
createModelCommand,
createCompactCommand,
createResetCommand,
createStopCommand,
createQueueCommand,
createTransferCommand,
createApprovalsCommand,
+1
View File
@@ -34,6 +34,7 @@ export interface CommandServices {
getQueue?: () => Promise<string> | string;
setQueue?: (input: string) => Promise<string> | string;
resetQueue?: () => Promise<string> | string;
cancelRun?: () => Promise<string> | string;
transferSession?: (target: string) => Promise<string> | string;
getApprovals?: () => Promise<string> | string;
approvePending?: (input: string) => Promise<string> | string;