Files
openclaw/src/plugins/hook-resolve-exec-env.test.ts
兰之 10d10faa25 feat(plugin-sdk): add resolve_exec_env hook
Summary:
- Add the plugin SDK `resolve_exec_env` hook for bounded exec environment contributions.
- Wire resolved exec env through exec preparation/final execution without exposing plugin env values to generic tool hooks.
- Cover lazy exec loading, host and command rewrites, node/gateway execution, filtering, and EXEC shell snapshot cache behavior.

Verification:
- `pnpm changed:lanes --json`
- `node scripts/run-vitest.mjs src/agents/bash-tools.exec.resolve-env-hook.test.ts src/agents/agent-tool-definition-adapter.test.ts src/agents/agent-tool-definition-adapter.after-tool-call.test.ts src/agents/shell-snapshot.test.ts src/plugins/hook-resolve-exec-env.test.ts`
- `pnpm check:test-types`
- `pnpm lint src/agents/bash-tools.exec.ts src/agents/bash-tools.exec.resolve-env-hook.test.ts`
- `.agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main`
- PR CI clean on 1bbad8d071: https://github.com/openclaw/openclaw/actions/runs/26817910293

Co-authored-by: Lanzhi <lizhan3@xiaomi.com>
2026-06-02 08:00:42 -04:00

126 lines
3.7 KiB
TypeScript

import { afterEach, describe, expect, it, vi } from "vitest";
import { createHookRunner } from "./hooks.js";
import { addStaticTestHooks, addTestHook } from "./hooks.test-helpers.js";
import { createEmptyPluginRegistry } from "./registry.js";
import type { PluginHookResolveExecEnvContext } from "./types.js";
const ctx: PluginHookResolveExecEnvContext = {
agentId: "agent-1",
sessionKey: "agent:agent-1:main",
messageProvider: "telegram",
channelId: "chat-1",
};
describe("resolve_exec_env hook", () => {
afterEach(() => {
vi.useRealTimers();
});
it("returns an empty env when no handlers are registered", async () => {
const runner = createHookRunner(createEmptyPluginRegistry());
await expect(
runner.runResolveExecEnv(
{ sessionKey: ctx.sessionKey, toolName: "exec", host: "gateway" },
ctx,
),
).resolves.toEqual({});
});
it("merges env vars from multiple plugins in priority order", async () => {
const registry = createEmptyPluginRegistry();
addStaticTestHooks<Record<string, string>>(registry, {
hookName: "resolve_exec_env",
hooks: [
{
pluginId: "first",
priority: 100,
result: { SHARED: "first", FIRST_ONLY: "1" },
},
{
pluginId: "second",
priority: 50,
result: { SHARED: "second", SECOND_ONLY: "2" },
},
],
});
const runner = createHookRunner(registry);
await expect(
runner.runResolveExecEnv(
{ sessionKey: ctx.sessionKey, toolName: "exec", host: "gateway" },
ctx,
),
).resolves.toEqual({
FIRST_ONLY: "1",
SECOND_ONLY: "2",
SHARED: "second",
});
});
it("isolates handler errors so other plugins can still contribute env", async () => {
const registry = createEmptyPluginRegistry();
const logger = { error: vi.fn(), warn: vi.fn(), debug: vi.fn() };
addTestHook({
registry,
pluginId: "crasher",
hookName: "resolve_exec_env",
handler: async () => {
throw new Error("plugin failed");
},
priority: 100,
});
addTestHook({
registry,
pluginId: "healthy",
hookName: "resolve_exec_env",
handler: async () => ({ HEALTHY_ENV: "ok" }),
priority: 50,
});
const runner = createHookRunner(registry, { logger });
await expect(
runner.runResolveExecEnv(
{ sessionKey: ctx.sessionKey, toolName: "exec", host: "gateway" },
ctx,
),
).resolves.toEqual({ HEALTHY_ENV: "ok" });
expect(logger.error).toHaveBeenCalledWith(
expect.stringContaining("resolve_exec_env handler from crasher failed"),
);
});
it("skips handlers that exceed the default timeout", async () => {
vi.useFakeTimers();
const logger = { error: vi.fn(), warn: vi.fn(), debug: vi.fn() };
const registry = createEmptyPluginRegistry();
addTestHook({
registry,
pluginId: "hanging",
hookName: "resolve_exec_env",
handler: () => new Promise<never>(() => {}),
priority: 100,
});
addTestHook({
registry,
pluginId: "healthy",
hookName: "resolve_exec_env",
handler: async () => ({ HEALTHY_ENV: "ok" }),
priority: 50,
});
const runner = createHookRunner(registry, { logger });
const resultPromise = runner.runResolveExecEnv(
{ sessionKey: ctx.sessionKey, toolName: "exec", host: "gateway" },
ctx,
);
await vi.advanceTimersByTimeAsync(15_000);
await expect(resultPromise).resolves.toEqual({ HEALTHY_ENV: "ok" });
expect(logger.error).toHaveBeenCalledWith(
"[hooks] resolve_exec_env handler from hanging failed: timed out after 15000ms",
);
});
});