fix(exec): add approval race changelog and regressions

This commit is contained in:
Peter Steinberger
2026-02-24 03:22:05 +00:00
parent 6f0dd61795
commit 7b2b86c60a
3 changed files with 112 additions and 0 deletions

View File

@@ -102,6 +102,55 @@ describe("requestExecApprovalDecision", () => {
).resolves.toBeNull();
});
it("uses registration response id when waiting for decision", async () => {
vi.mocked(callGatewayTool)
.mockResolvedValueOnce({
status: "accepted",
id: "server-assigned-id",
expiresAtMs: DEFAULT_APPROVAL_TIMEOUT_MS,
})
.mockResolvedValueOnce({ decision: "allow-once" });
await expect(
requestExecApprovalDecision({
id: "client-id",
command: "echo hi",
cwd: "/tmp",
host: "gateway",
security: "allowlist",
ask: "on-miss",
}),
).resolves.toBe("allow-once");
expect(callGatewayTool).toHaveBeenNthCalledWith(
2,
"exec.approval.waitDecision",
{ timeoutMs: DEFAULT_APPROVAL_REQUEST_TIMEOUT_MS },
{ id: "server-assigned-id" },
);
});
it("treats expired-or-missing waitDecision as null decision", async () => {
vi.mocked(callGatewayTool)
.mockResolvedValueOnce({
status: "accepted",
id: "approval-id",
expiresAtMs: DEFAULT_APPROVAL_TIMEOUT_MS,
})
.mockRejectedValueOnce(new Error("approval expired or not found"));
await expect(
requestExecApprovalDecision({
id: "approval-id",
command: "echo hi",
cwd: "/tmp",
host: "gateway",
security: "allowlist",
ask: "on-miss",
}),
).resolves.toBeNull();
});
it("returns final decision directly when gateway already replies with decision", async () => {
vi.mocked(callGatewayTool).mockResolvedValue({ decision: "deny", id: "approval-id" });