


















@@ -110,6 +110,51 @@ describe("hooks mapping", () => {
110110});
111111}
112112113+async function applyGmailTransformSessionKey(params: {
114+tempPrefix: string;
115+transformLines: string[];
116+payload?: Record<string, unknown>;
117+sessionKey?: string;
118+}) {
119+const configDir = fs.mkdtempSync(path.join(os.tmpdir(), params.tempPrefix));
120+const transformsRoot = path.join(configDir, "hooks", "transforms");
121+fs.mkdirSync(transformsRoot, { recursive: true });
122+fs.writeFileSync(path.join(transformsRoot, "transform.mjs"), params.transformLines.join("\n"));
123+124+const mappings = resolveHookMappings(
125+{
126+mappings: [
127+{
128+match: { path: "gmail" },
129+action: "agent",
130+messageTemplate: "Subject: {{messages[0].subject}}",
131+ ...(params.sessionKey ? { sessionKey: params.sessionKey } : {}),
132+transform: { module: "transform.mjs" },
133+},
134+],
135+},
136+{ configDir },
137+);
138+139+return applyHookMappings(mappings, {
140+payload: params.payload ?? gmailPayload,
141+headers: {},
142+url: baseUrl,
143+path: "gmail",
144+});
145+}
146+147+function expectAgentSessionKey(
148+result: Awaited<ReturnType<typeof applyHookMappings>>,
149+params: { sessionKey: string; sessionKeySource?: "static" | "templated" },
150+) {
151+expect(result?.ok).toBe(true);
152+if (result?.ok && result.action?.kind === "agent") {
153+expect(result.action.sessionKey).toBe(params.sessionKey);
154+expect(result.action.sessionKeySource).toBe(params.sessionKeySource);
155+}
156+}
157+113158it("resolves gmail preset", () => {
114159const mappings = resolveHookMappings({ presets: ["gmail"] });
115160expect(mappings.length).toBeGreaterThan(0);
@@ -221,181 +266,76 @@ describe("hooks mapping", () => {
221266});
222267223268it("treats transform-provided session keys as templated by default", async () => {
224-const configDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-config-sessionkey-xform-"));
225-const transformsRoot = path.join(configDir, "hooks", "transforms");
226-fs.mkdirSync(transformsRoot, { recursive: true });
227-fs.writeFileSync(
228-path.join(transformsRoot, "transform.mjs"),
229-[
269+const result = await applyGmailTransformSessionKey({
270+tempPrefix: "openclaw-config-sessionkey-xform-",
271+payload: { subject: "external" },
272+sessionKey: "hook:gmail:static",
273+transformLines: [
230274"export default ({ payload }) => ({",
231275' kind: "agent",',
232276' message: "Transformed",',
233277" sessionKey: `hook:gmail:${payload.subject}`,",
234278"});",
235-].join("\n"),
236-);
237-238-const mappings = resolveHookMappings(
239-{
240-mappings: [
241-{
242-match: { path: "gmail" },
243-action: "agent",
244-messageTemplate: "Subject: {{messages[0].subject}}",
245-sessionKey: "hook:gmail:static",
246-transform: { module: "transform.mjs" },
247-},
248-],
249-},
250-{ configDir },
251-);
252-253-const result = await applyHookMappings(mappings, {
254-payload: { subject: "external" },
255-headers: {},
256-url: baseUrl,
257-path: "gmail",
279+],
258280});
259281260-expect(result?.ok).toBe(true);
261-if (result?.ok && result.action?.kind === "agent") {
262-expect(result.action.sessionKey).toBe("hook:gmail:external");
263-expect(result.action.sessionKeySource).toBe("templated");
264-}
282+expectAgentSessionKey(result, {
283+sessionKey: "hook:gmail:external",
284+sessionKeySource: "templated",
285+});
265286});
266287267288it("uses transform-provided static session key source metadata", async () => {
268-const configDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-config-sessionkey-static-"));
269-const transformsRoot = path.join(configDir, "hooks", "transforms");
270-fs.mkdirSync(transformsRoot, { recursive: true });
271-fs.writeFileSync(
272-path.join(transformsRoot, "transform.mjs"),
273-[
289+const result = await applyGmailTransformSessionKey({
290+tempPrefix: "openclaw-config-sessionkey-static-",
291+sessionKey: "hook:gmail:{{messages[0].subject}}",
292+transformLines: [
274293"export default () => ({",
275294' kind: "agent",',
276295' message: "Transformed",',
277296' sessionKey: "hook:gmail:fixed",',
278297' sessionKeySource: "static",',
279298"});",
280-].join("\n"),
281-);
282-283-const mappings = resolveHookMappings(
284-{
285-mappings: [
286-{
287-match: { path: "gmail" },
288-action: "agent",
289-messageTemplate: "Subject: {{messages[0].subject}}",
290-sessionKey: "hook:gmail:{{messages[0].subject}}",
291-transform: { module: "transform.mjs" },
292-},
293-],
294-},
295-{ configDir },
296-);
297-298-const result = await applyHookMappings(mappings, {
299-payload: gmailPayload,
300-headers: {},
301-url: baseUrl,
302-path: "gmail",
299+],
303300});
304301305-expect(result?.ok).toBe(true);
306-if (result?.ok && result.action?.kind === "agent") {
307-expect(result.action.sessionKey).toBe("hook:gmail:fixed");
308-expect(result.action.sessionKeySource).toBe("static");
309-}
302+expectAgentSessionKey(result, { sessionKey: "hook:gmail:fixed", sessionKeySource: "static" });
310303});
311304312305it("treats empty transform session keys as absent for source tracking", async () => {
313-const configDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-config-sessionkey-empty-"));
314-const transformsRoot = path.join(configDir, "hooks", "transforms");
315-fs.mkdirSync(transformsRoot, { recursive: true });
316-fs.writeFileSync(
317-path.join(transformsRoot, "transform.mjs"),
318-[
306+const result = await applyGmailTransformSessionKey({
307+tempPrefix: "openclaw-config-sessionkey-empty-",
308+sessionKey: "hook:gmail:{{messages[0].subject}}",
309+transformLines: [
319310"export default () => ({",
320311' kind: "agent",',
321312' message: "Transformed",',
322313' sessionKey: "",',
323314' sessionKeySource: "templated",',
324315"});",
325-].join("\n"),
326-);
327-328-const mappings = resolveHookMappings(
329-{
330-mappings: [
331-{
332-match: { path: "gmail" },
333-action: "agent",
334-messageTemplate: "Subject: {{messages[0].subject}}",
335-sessionKey: "hook:gmail:{{messages[0].subject}}",
336-transform: { module: "transform.mjs" },
337-},
338-],
339-},
340-{ configDir },
341-);
342-343-const result = await applyHookMappings(mappings, {
344-payload: gmailPayload,
345-headers: {},
346-url: baseUrl,
347-path: "gmail",
316+],
348317});
349318350-expect(result?.ok).toBe(true);
351-if (result?.ok && result.action?.kind === "agent") {
352-expect(result.action.sessionKey).toBe("");
353-expect(result.action.sessionKeySource).toBeUndefined();
354-}
319+expectAgentSessionKey(result, { sessionKey: "" });
355320});
356321357322it("defaults invalid transform session key source metadata to templated", async () => {
358-const configDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-config-sessionkey-invalid-"));
359-const transformsRoot = path.join(configDir, "hooks", "transforms");
360-fs.mkdirSync(transformsRoot, { recursive: true });
361-fs.writeFileSync(
362-path.join(transformsRoot, "transform.mjs"),
363-[
323+const result = await applyGmailTransformSessionKey({
324+tempPrefix: "openclaw-config-sessionkey-invalid-",
325+transformLines: [
364326"export default () => ({",
365327' kind: "agent",',
366328' message: "Transformed",',
367329' sessionKey: "hook:gmail:from-transform",',
368330' sessionKeySource: "bogus",',
369331"});",
370-].join("\n"),
371-);
372-373-const mappings = resolveHookMappings(
374-{
375-mappings: [
376-{
377-match: { path: "gmail" },
378-action: "agent",
379-messageTemplate: "Subject: {{messages[0].subject}}",
380-transform: { module: "transform.mjs" },
381-},
382-],
383-},
384-{ configDir },
385-);
386-387-const result = await applyHookMappings(mappings, {
388-payload: gmailPayload,
389-headers: {},
390-url: baseUrl,
391-path: "gmail",
332+],
392333});
393334394-expect(result?.ok).toBe(true);
395-if (result?.ok && result.action?.kind === "agent") {
396-expect(result.action.sessionKey).toBe("hook:gmail:from-transform");
397-expect(result.action.sessionKeySource).toBe("templated");
398-}
335+expectAgentSessionKey(result, {
336+sessionKey: "hook:gmail:from-transform",
337+sessionKeySource: "templated",
338+});
399339});
400340401341it("rejects transform module traversal outside transformsDir", () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。