File size: 3,513 Bytes
f0634fb | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | import { afterEach, describe, expect, it } from 'vitest';
import { IEventBus } from '#/app/event/eventBus';
import { IPluginService } from '#/app/plugin/plugin';
import type { PluginCommandDef } from '#/app/plugin/types';
import { ErrorCodes } from '#/errors';
import { ISessionMetadata } from '#/session/sessionMetadata/sessionMetadata';
import {
IAgentPluginCommandService,
PluginCommandActivated,
} from '#/agent/pluginCommand/pluginCommand';
import { appService, createTestAgent, type TestAgentContext } from '../../harness';
const DEPLOY_COMMAND: PluginCommandDef = {
pluginId: 'demo',
name: 'deploy',
description: 'Deploy',
body: 'Deploy body',
path: '/plugins/demo/deploy.md',
};
function pluginServiceStub(commands: readonly PluginCommandDef[]): IPluginService {
return {
_serviceBrand: undefined,
onDidReload: () => ({ dispose: () => {} }),
onDidMutate: () => ({ dispose: () => {} }),
listPlugins: async () => [],
installPlugin: async () => ({ id: '' }) as never,
setPluginEnabled: async () => {},
setPluginMcpServerEnabled: async () => {},
removePlugin: async () => {},
reloadPlugins: async () => ({ added: [], removed: [], errors: [] }),
getPluginInfo: async () => {
throw new Error('getPluginInfo is not used by these tests');
},
listPluginCommands: async () => commands,
checkUpdates: async () => [],
pluginSkillRoots: async () => [],
pluginAgentRoots: async () => [],
enabledSessionStarts: async () => [],
enabledSystemPrompts: async () => [],
enabledMcpServers: async () => ({}),
mcpServerEntries: async () => [],
enabledHooks: async () => [],
hasLoadedSnapshot: () => true,
};
}
describe('AgentPluginCommandService', () => {
let ctx: TestAgentContext;
afterEach(async () => {
try {
await ctx.expectResumeMatches();
} finally {
await ctx.dispose();
}
});
function agentWithDeployCommand(): TestAgentContext {
return createTestAgent(
appService(IPluginService, pluginServiceStub([DEPLOY_COMMAND])),
);
}
it('publishes the activation event, enqueues the expanded body, and updates metadata', async () => {
ctx = agentWithDeployCommand();
ctx.mockNextResponse({ type: 'text', text: 'deployed' });
const events: PluginCommandActivated[] = [];
const sub = ctx
.get(IEventBus)
.subscribe(PluginCommandActivated, (event) => events.push(event));
await ctx
.get(IAgentPluginCommandService)
.activate({ pluginId: 'demo', commandName: 'deploy', args: 'prod' });
sub.dispose();
expect(events).toHaveLength(1);
expect(events[0]).toMatchObject({
type: 'plugin_command.activated',
pluginId: 'demo',
commandName: 'deploy',
commandArgs: 'prod',
trigger: 'user-slash',
});
await ctx.untilTurnEnd();
const llmInput = JSON.stringify(ctx.llmInputs());
expect(llmInput).toContain('Deploy body');
expect(llmInput).toContain('ARGUMENTS: prod');
const metadata = await ctx.get(ISessionMetadata).read();
expect(metadata.title).toBe('/demo:deploy prod');
expect(metadata.lastPrompt).toBe('/demo:deploy prod');
});
it('rejects an unknown command with request.invalid', async () => {
ctx = agentWithDeployCommand();
await expect(
ctx
.get(IAgentPluginCommandService)
.activate({ pluginId: 'demo', commandName: 'missing' }),
).rejects.toMatchObject({ code: ErrorCodes.REQUEST_INVALID });
});
});
|