fix(gateway): include export name in hook transform cache key (#13855)

Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: a9eea919b8
Co-authored-by: mcaxtr <7562095+mcaxtr@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky
This commit is contained in:
Marcus Castro
2026-02-20 14:44:51 -03:00
committed by GitHub
parent 618b36f07a
commit c8ee33c162
3 changed files with 70 additions and 2 deletions

View File

@@ -294,6 +294,72 @@ describe("hooks mapping", () => {
}
});
it("caches transform functions by module path and export name", async () => {
const configDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-hooks-export-"));
const transformsRoot = path.join(configDir, "hooks", "transforms");
fs.mkdirSync(transformsRoot, { recursive: true });
const modPath = path.join(transformsRoot, "multi-export.mjs");
fs.writeFileSync(
modPath,
[
'export function transformA() { return { kind: "wake", text: "from-A" }; }',
'export function transformB() { return { kind: "wake", text: "from-B" }; }',
].join("\n"),
);
const mappingsA = resolveHookMappings(
{
mappings: [
{
match: { path: "testA" },
action: "agent",
messageTemplate: "unused",
transform: { module: "multi-export.mjs", export: "transformA" },
},
],
},
{ configDir },
);
const mappingsB = resolveHookMappings(
{
mappings: [
{
match: { path: "testB" },
action: "agent",
messageTemplate: "unused",
transform: { module: "multi-export.mjs", export: "transformB" },
},
],
},
{ configDir },
);
const resultA = await applyHookMappings(mappingsA, {
payload: {},
headers: {},
url: new URL("http://127.0.0.1:18789/hooks/testA"),
path: "testA",
});
const resultB = await applyHookMappings(mappingsB, {
payload: {},
headers: {},
url: new URL("http://127.0.0.1:18789/hooks/testB"),
path: "testB",
});
expect(resultA?.ok).toBe(true);
if (resultA?.ok && resultA.action?.kind === "wake") {
expect(resultA.action.text).toBe("from-A");
}
expect(resultB?.ok).toBe(true);
if (resultB?.ok && resultB.action?.kind === "wake") {
expect(resultB.action.text).toBe("from-B");
}
});
it("rejects missing message", async () => {
const mappings = resolveHookMappings({
mappings: [{ match: { path: "noop" }, action: "agent" }],