diff --git a/src/config/types.hooks.ts b/src/config/types.hooks.ts index 7ca74605a..d748cc453 100644 --- a/src/config/types.hooks.ts +++ b/src/config/types.hooks.ts @@ -36,6 +36,7 @@ export type HookMappingConfig = { thinking?: string; timeoutSeconds?: number; transform?: HookMappingTransform; + agentId?: string; }; export type HooksGmailTailscaleMode = "off" | "serve" | "funnel"; diff --git a/src/gateway/hooks-mapping.ts b/src/gateway/hooks-mapping.ts index 2ebf9b136..df9a37037 100644 --- a/src/gateway/hooks-mapping.ts +++ b/src/gateway/hooks-mapping.ts @@ -22,6 +22,7 @@ export type HookMappingResolved = { thinking?: string; timeoutSeconds?: number; transform?: HookMappingTransformResolved; + agentId?: string; }; export type HookMappingTransformResolved = { @@ -55,6 +56,7 @@ export type HookAction = model?: string; thinking?: string; timeoutSeconds?: number; + agentId?: string; }; export type HookMappingResult = diff --git a/src/gateway/hooks.ts b/src/gateway/hooks.ts index 1fc6d52f4..00027ec33 100644 --- a/src/gateway/hooks.ts +++ b/src/gateway/hooks.ts @@ -137,6 +137,7 @@ export type HookAgentPayload = { model?: string; thinking?: string; timeoutSeconds?: number; + agentId?: string; }; const listHookChannelValues = () => ["last", ...listChannelPlugins().map((plugin) => plugin.id)]; @@ -195,7 +196,9 @@ export function normalizeAgentPayload( const timeoutSeconds = typeof timeoutRaw === "number" && Number.isFinite(timeoutRaw) && timeoutRaw > 0 ? Math.floor(timeoutRaw) - : undefined; + : un + const agentIdRaw = payload.agentId; + const agentId = typeof agentIdRaw === "string" && agentIdRaw.trim() ? agentIdRaw.trim() : undefined;defined; return { ok: true, value: { @@ -209,6 +212,7 @@ export function normalizeAgentPayload( model, thinking, timeoutSeconds, + agentId, }, }; } diff --git a/test/hooks.agentId.test.ts b/test/hooks.agentId.test.ts new file mode 100644 index 000000000..7684a9b08 --- /dev/null +++ b/test/hooks.agentId.test.ts @@ -0,0 +1,68 @@ +import { describe, it, expect } from "vitest"; +import { normalizeAgentPayload } from "../src/gateway/hooks.js"; + +describe("normalizeAgentPayload agentId support", () => { + it("should accept agentId as a string", () => { + const payload = { + message: "test message", + channel: "last", + agentId: "agent-123", + }; + const result = normalizeAgentPayload(payload); + expect(result.ok).toBe(true); + if (result.ok) { + expect(result.value.agentId).toBe("agent-123"); + } + }); + + it("should trim agentId whitespace", () => { + const payload = { + message: "test message", + channel: "last", + agentId: " agent-456 ", + }; + const result = normalizeAgentPayload(payload); + expect(result.ok).toBe(true); + if (result.ok) { + expect(result.value.agentId).toBe("agent-456"); + } + }); + + it("should handle missing agentId as undefined", () => { + const payload = { + message: "test message", + channel: "last", + }; + const result = normalizeAgentPayload(payload); + expect(result.ok).toBe(true); + if (result.ok) { + expect(result.value.agentId).toBeUndefined(); + } + }); + + it("should ignore empty agentId strings", () => { + const payload = { + message: "test message", + channel: "last", + agentId: " ", + }; + const result = normalizeAgentPayload(payload); + expect(result.ok).toBe(true); + if (result.ok) { + expect(result.value.agentId).toBeUndefined(); + } + }); + + it("should handle non-string agentId as undefined", () => { + const payload = { + message: "test message", + channel: "last", + agentId: 123, + } as any; + const result = normalizeAgentPayload(payload); + expect(result.ok).toBe(true); + if (result.ok) { + expect(result.value.agentId).toBeUndefined(); + } + }); +});