diff --git a/sdks/agent-sdk/src/demo/main.ts b/sdks/agent-sdk/src/demo/main.ts index fbc3e6d20..2e4b38ba1 100644 --- a/sdks/agent-sdk/src/demo/main.ts +++ b/sdks/agent-sdk/src/demo/main.ts @@ -33,9 +33,13 @@ const performanceMonitor = new PerformanceMonitor({ }); const commandRouter = new CommandRouter({ helpCommand: "/help" }); -commandRouter.command("/version", "Show Agent SDK version", async (ctx) => { - await ctx.conversation.sendText(`v${process.env.npm_package_version}`); -}); +commandRouter.command( + ["/v", "/version"], + "Show Agent SDK version", + async (ctx) => { + await ctx.conversation.sendText(`v${process.env.npm_package_version}`); + }, +); commandRouter.command("/test-actions", async (ctx) => { await ctx.conversation.sendActions({ diff --git a/sdks/agent-sdk/src/middleware/CommandRouter.test.ts b/sdks/agent-sdk/src/middleware/CommandRouter.test.ts index 960531cec..968e88b6d 100644 --- a/sdks/agent-sdk/src/middleware/CommandRouter.test.ts +++ b/sdks/agent-sdk/src/middleware/CommandRouter.test.ts @@ -165,4 +165,49 @@ describe("CommandRouter", () => { expect(router.commandList).not.toContain("/help"); }); }); + + describe("multiple command aliases", () => { + it("should register the same handler for multiple command strings", async () => { + const router = new CommandRouter(); + const handler = vi.fn(); + router.command(["/v", "/version"], handler); + + agent.use(router.middleware()); + await agent.start(); + + const otherClient = await createClient(); + const dm = await otherClient.conversations.createDm(client.inboxId); + + await dm.sendText("/v"); + await vi.waitFor(() => { + expect(handler).toHaveBeenCalledTimes(1); + }); + + await dm.sendText("/version"); + await vi.waitFor(() => { + expect(handler).toHaveBeenCalledTimes(2); + }); + }); + + it("should include all aliases in the command list", () => { + const router = new CommandRouter(); + router.command(["/v", "/version"], vi.fn()); + expect(router.commandList).toContain("/v"); + expect(router.commandList).toContain("/version"); + }); + + it("should accept a description with multiple command strings", () => { + const router = new CommandRouter(); + router.command(["/v", "/version"], "Show version", vi.fn()); + expect(router.commandList).toContain("/v"); + expect(router.commandList).toContain("/version"); + }); + + it("should throw if any command in the array does not start with /", () => { + const router = new CommandRouter(); + expect(() => { + router.command(["/valid", "invalid"], vi.fn()); + }).toThrow('Command must start with "/"'); + }); + }); }); diff --git a/sdks/agent-sdk/src/middleware/CommandRouter.ts b/sdks/agent-sdk/src/middleware/CommandRouter.ts index 067471646..baf9143c1 100644 --- a/sdks/agent-sdk/src/middleware/CommandRouter.ts +++ b/sdks/agent-sdk/src/middleware/CommandRouter.ts @@ -50,19 +50,26 @@ export class CommandRouter { return Array.from(this.#commandMap.keys()); } - command(command: string, handler: AgentMessageHandler): this; command( - command: string, + command: string | string[], + handler: AgentMessageHandler, + ): this; + command( + command: string | string[], description: string, handler: AgentMessageHandler, ): this; command( - command: string, + command: string | string[], handlerOrDescription: AgentMessageHandler | string, handler?: AgentMessageHandler, ): this { - if (!command.startsWith("/")) { - throw new Error('Command must start with "/"'); + const commands = Array.isArray(command) ? command : [command]; + + for (const cmd of commands) { + if (!cmd.startsWith("/")) { + throw new Error('Command must start with "/"'); + } } let resolvedHandler: AgentMessageHandler; @@ -80,10 +87,15 @@ export class CommandRouter { resolvedHandler = handler; } - this.#commandMap.set(command.toLowerCase(), { + const entry: CommandEntry = { handler: resolvedHandler, description, - }); + }; + + for (const cmd of commands) { + this.#commandMap.set(cmd.toLowerCase(), entry); + } + return this; }