// Selfhost-only: an execution that triggers an approval gate pauses, then // resumes successfully after `resume` is called with action "accept". // // Mechanism: create a `require_approval` policy scoped to the built-in tool // `executor.coreTools.policies.list` via the typed HTTP API, then execute code // over MCP that calls that tool. The engine hits the `enforceApproval` path // and returns a paused result with an `executionId`; `session.approvePaused()` // resumes it. The policy removal is an `ensuring` finalizer — a leaked // require_approval gate on a built-in tool would pause unrelated scenarios on // the shared selfhost instance. import { expect } from "@effect/vitest"; import { Effect } from "effect"; import { composePluginApi } from "@executor-js/api/server"; import { scenario } from "../src/scenario"; import { Api, Mcp, Target } from "../src/services"; const coreApi = composePluginApi([] as const); const APPROVAL_TARGET_TOOL = "executor.coreTools.policies.list"; const EXECUTE_CODE = ` const result = await tools.executor.coreTools.policies.list({}); return JSON.stringify(result); `; scenario( "MCP · a paused execution resumes after human approval", {}, Effect.gen(function* () { const target = yield* Target; const apiSurface = yield* Api; const mcp = yield* Mcp; const identity = yield* target.newIdentity(); const client = yield* apiSurface.client(coreApi, identity); const policy = yield* client.policies.create({ payload: { owner: "org", pattern: APPROVAL_TARGET_TOOL, action: "require_approval" }, }); yield* Effect.gen(function* () { const session = mcp.session(identity); // Warm up the MCP session before the gated call so the OAuth handshake // does not race with the policy window. const tools = yield* session.listTools(); expect(tools).toContain("execute"); const paused = yield* session.call("execute", { code: EXECUTE_CODE }); expect(paused.text, "execution paused rather than completing").toContain("Execution paused"); expect(paused.text, "paused result carries the executionId").toContain("executionId:"); const resumed = yield* session.approvePaused(paused.text); expect(resumed.ok, "resumed execution completed without error").toBe(true); expect(resumed.text, "the sandbox returned the gated tool's result").toContain( APPROVAL_TARGET_TOOL, ); }).pipe( // Always remove the gate, even when the test fails or times out. Effect.ensuring( client.policies .remove({ params: { policyId: policy.id }, payload: { owner: "org" } }) .pipe(Effect.ignore), ), ); }), );