mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-28 20:22:45 +00:00
Revert "fix(server): honor Claude bypass permission mode (#3732)"
This reverts commit f998ee1691.
This commit is contained in:
@@ -1,83 +0,0 @@
|
||||
import {
|
||||
existsSync,
|
||||
mkdirSync,
|
||||
mkdtempSync,
|
||||
readFileSync,
|
||||
realpathSync,
|
||||
writeFileSync,
|
||||
} from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import { expect, test } from "../support/fixtures";
|
||||
import { submitMessage } from "../support/helpers/composer";
|
||||
import { cleanupRewindFlow, type AgentHandle, launchAgent } from "../support/helpers/rewind-flow";
|
||||
|
||||
const FILE_NAME = "BYPASS.md";
|
||||
const FILE_CONTENT = "PASEO_BYPASS_PLAYWRIGHT_OK\n";
|
||||
const COMPLETION = "BYPASS_WRITE_COMPLETE";
|
||||
|
||||
function configureClaudeToAskForFileTools(handle: AgentHandle): void {
|
||||
const settingsDirectory = path.join(handle.cwd, ".claude");
|
||||
mkdirSync(settingsDirectory);
|
||||
writeFileSync(
|
||||
path.join(settingsDirectory, "settings.local.json"),
|
||||
`${JSON.stringify({ permissions: { ask: ["Bash", "Write", "Edit"] } }, null, 2)}\n`,
|
||||
);
|
||||
}
|
||||
|
||||
async function switchClaudeToBypassPermissions(handle: AgentHandle): Promise<void> {
|
||||
const modeControl = handle.page.getByRole("button", { name: /^Select agent mode \(/ });
|
||||
await expect(modeControl).toHaveAccessibleName("Select agent mode (Always ask)");
|
||||
await modeControl.click();
|
||||
|
||||
const modePicker = handle.page.getByTestId("combobox-desktop-container").last();
|
||||
await expect(modePicker).toBeVisible();
|
||||
await modePicker.getByText("Bypass", { exact: true }).click();
|
||||
await expect(modeControl).toHaveAccessibleName("Select agent mode (Bypass)");
|
||||
}
|
||||
|
||||
async function askClaudeToWriteWithoutApproval(handle: AgentHandle): Promise<void> {
|
||||
await submitMessage(
|
||||
handle.page,
|
||||
`Call the Write tool exactly once to create ${FILE_NAME} with exactly ${JSON.stringify(
|
||||
FILE_CONTENT,
|
||||
)}. Do not use Bash or another tool. After Write succeeds, reply exactly ${COMPLETION}.`,
|
||||
);
|
||||
}
|
||||
|
||||
async function expectBypassWriteToComplete(handle: AgentHandle): Promise<void> {
|
||||
await expect(
|
||||
handle.page.getByTestId("assistant-message").filter({ hasText: COMPLETION }).last(),
|
||||
).toBeVisible({ timeout: 120_000 });
|
||||
await expect(handle.page.getByTestId("permission-request-question")).toHaveCount(0);
|
||||
await expect.poll(() => existsSync(path.join(handle.cwd, FILE_NAME))).toBe(true);
|
||||
await expect
|
||||
.poll(() => readFileSync(path.join(handle.cwd, FILE_NAME), "utf8"))
|
||||
.toBe(FILE_CONTENT);
|
||||
}
|
||||
|
||||
test.describe("real Claude bypass permissions", () => {
|
||||
test.setTimeout(240_000);
|
||||
|
||||
test("honors Bypass when project policy asks to approve Write", async ({ page }, testInfo) => {
|
||||
const cwd = realpathSync(mkdtempSync(path.join(tmpdir(), "paseo-claude-bypass-")));
|
||||
let handle: AgentHandle | undefined;
|
||||
|
||||
try {
|
||||
handle = await launchAgent({
|
||||
page,
|
||||
provider: "claude",
|
||||
cwd,
|
||||
mode: "full-access",
|
||||
providerConfig: { modeId: "default" },
|
||||
});
|
||||
configureClaudeToAskForFileTools(handle);
|
||||
await switchClaudeToBypassPermissions(handle);
|
||||
await askClaudeToWriteWithoutApproval(handle);
|
||||
await expectBypassWriteToComplete(handle);
|
||||
await page.screenshot({ path: testInfo.outputPath("claude-bypass-write-complete.png") });
|
||||
} finally {
|
||||
await cleanupRewindFlow({ handle, cwd });
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -4571,23 +4571,9 @@ class ClaudeAgentSession implements AgentSession {
|
||||
input,
|
||||
options,
|
||||
): Promise<PermissionResult> => {
|
||||
const requestId = `permission-${randomUUID()}`;
|
||||
const kind = resolvePermissionKind(toolName, input);
|
||||
const requestInput = normalizeClaudeAskUserQuestionRequestInput(toolName, input);
|
||||
|
||||
// Supplying canUseTool gives Paseo a host UI for interactive modes, but the SDK can
|
||||
// still call it for ordinary tools while bypassPermissions is active. Honor the mode
|
||||
// contract here so a Bypass session never turns Write/Bash into a blocking UI card.
|
||||
// A human steer keeps precedence: tools from the superseded turn must be denied until
|
||||
// Claude consumes the steer instead of being allowed to race it.
|
||||
if (
|
||||
kind === "tool" &&
|
||||
this.currentMode === "bypassPermissions" &&
|
||||
this.permissionClearingSteerUuids.size === 0
|
||||
) {
|
||||
return { behavior: "allow", updatedInput: input };
|
||||
}
|
||||
|
||||
const requestId = `permission-${randomUUID()}`;
|
||||
const metadata: AgentMetadata = {};
|
||||
if (options.toolUseID) {
|
||||
metadata.toolUseId = options.toolUseID;
|
||||
|
||||
Reference in New Issue
Block a user