feat(gateway): support full /model switching in webchat sessions

This commit is contained in:
William Valentin
2026-02-18 16:55:38 -08:00
parent abd37342fa
commit a13aa3113e
4 changed files with 206 additions and 11 deletions
+76
View File
@@ -143,6 +143,82 @@ describe('createAgentHandlers command fast-path', () => {
expect(((sent[0] as GatewayEvent).data as { content: string }).content).toContain('Switched to model tier: fast');
});
it('handles /model <tier> <provider/model> in gateway sessions', async () => {
const sent: OutboundMessage[] = [];
const send = vi.fn((msg: OutboundMessage) => sent.push(msg));
const modelRouter = {
setClient: vi.fn(),
setTierStrict: vi.fn(),
};
const handlersWithRouter = createAgentHandlers({
sessionBridge: sessionBridge as unknown as AgentHandlerDeps['sessionBridge'],
laneQueue: new LaneQueue(),
sessionManager: sessionManager as unknown as AgentHandlerDeps['sessionManager'],
commandRegistry,
modelRouter: modelRouter as unknown as AgentHandlerDeps['modelRouter'],
runtimeConfig: {
models: {
default: { provider: 'anthropic', model: 'claude-sonnet-4' },
fallback_chain: ['anthropic'],
},
} as unknown as AgentHandlerDeps['runtimeConfig'],
});
const req: GatewayRequest = {
id: 9,
method: 'agent.send',
params: {
message: '/model default github/gpt-5-mini',
connectionId: 'conn-1',
metadata: { isCommand: true, command: 'model', commandArgs: 'default github/gpt-5-mini' },
},
};
await handlersWithRouter['agent.send'](req, send);
expect(modelRouter.setClient).toHaveBeenCalledWith('default', expect.anything(), 'github/gpt-5-mini');
expect(modelRouter.setTierStrict).toHaveBeenCalledWith('default', true);
expect(mockAgent.setModelTier).toHaveBeenCalledWith('default');
expect(((sent[0] as GatewayEvent).data as { content: string }).content).toContain('Set default to: github/gpt-5-mini');
});
it('handles /model <tier> reset in gateway sessions', async () => {
const sent: OutboundMessage[] = [];
const send = vi.fn((msg: OutboundMessage) => sent.push(msg));
const modelRouter = {
setClient: vi.fn(),
setTierStrict: vi.fn(),
};
const handlersWithRouter = createAgentHandlers({
sessionBridge: sessionBridge as unknown as AgentHandlerDeps['sessionBridge'],
laneQueue: new LaneQueue(),
sessionManager: sessionManager as unknown as AgentHandlerDeps['sessionManager'],
commandRegistry,
modelRouter: modelRouter as unknown as AgentHandlerDeps['modelRouter'],
runtimeConfig: {
models: {
default: { provider: 'anthropic', model: 'claude-sonnet-4' },
fallback_chain: ['anthropic'],
},
} as unknown as AgentHandlerDeps['runtimeConfig'],
});
const req: GatewayRequest = {
id: 10,
method: 'agent.send',
params: {
message: '/model default reset',
connectionId: 'conn-1',
metadata: { isCommand: true, command: 'model', commandArgs: 'default reset' },
},
};
await handlersWithRouter['agent.send'](req, send);
expect(modelRouter.setClient).toHaveBeenCalledWith('default', expect.anything(), 'anthropic/claude-sonnet-4');
expect(modelRouter.setTierStrict).toHaveBeenCalledWith('default', false);
expect(mockAgent.setModelTier).toHaveBeenCalledWith('default');
expect(((sent[0] as GatewayEvent).data as { content: string }).content).toContain('Reset default to: anthropic/claude-sonnet-4');
});
it('falls through to agent.process for unknown commands', async () => {
const sent: OutboundMessage[] = [];
const send = vi.fn((msg: OutboundMessage) => sent.push(msg));