Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions sdks/agent-sdk/src/demo/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down
45 changes: 45 additions & 0 deletions sdks/agent-sdk/src/middleware/CommandRouter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 "/"');
});
});
});
26 changes: 19 additions & 7 deletions sdks/agent-sdk/src/middleware/CommandRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,19 +50,26 @@ export class CommandRouter<ContentTypes = unknown> {
return Array.from(this.#commandMap.keys());
}

command(command: string, handler: AgentMessageHandler<SupportedType>): this;
command(
command: string,
command: string | string[],
handler: AgentMessageHandler<SupportedType>,
): this;
command(
command: string | string[],
description: string,
handler: AgentMessageHandler<SupportedType>,
): this;
command(
command: string,
command: string | string[],
handlerOrDescription: AgentMessageHandler<SupportedType> | string,
handler?: AgentMessageHandler<SupportedType>,
): 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<SupportedType>;
Expand All @@ -80,10 +87,15 @@ export class CommandRouter<ContentTypes = unknown> {
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;
}

Expand Down