mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-25 02:32:47 +00:00
fix(claude): skip user and project hooks for internal agents (#5750)
Branch-name, commit message, and pull request text generation run as internal Claude agents in the workspace, so a project Stop hook that pushes HEAD published the placeholder branch before the rename. Closes #5742 Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { describe, test, expect, beforeEach, afterEach, beforeAll, afterAll } from "vitest";
|
||||
import { createServer } from "http";
|
||||
import { mkdtempSync, rmSync } from "fs";
|
||||
import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "fs";
|
||||
import { tmpdir } from "os";
|
||||
import path from "path";
|
||||
import { randomUUID } from "crypto";
|
||||
@@ -250,4 +250,39 @@ describe("getStructuredAgentResponse (e2e)", () => {
|
||||
|
||||
expect(result.message.trim().toLowerCase()).toBe("hello");
|
||||
}, 180000);
|
||||
|
||||
test("does not run the project's Claude hooks for an internal Claude agent", async (context) => {
|
||||
if (!canRunClaude) {
|
||||
context.skip();
|
||||
}
|
||||
const hookMarker = path.join(cwd, "stop-hook-ran");
|
||||
mkdirSync(path.join(cwd, ".claude"));
|
||||
writeFileSync(
|
||||
path.join(cwd, ".claude", "settings.json"),
|
||||
JSON.stringify({
|
||||
hooks: {
|
||||
Stop: [{ hooks: [{ type: "command", command: `touch '${hookMarker}'` }] }],
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const result = await generateStructuredAgentResponse({
|
||||
manager,
|
||||
agentConfig: {
|
||||
provider: "claude",
|
||||
model: CLAUDE_TEST_MODEL,
|
||||
cwd,
|
||||
title: "Branch name generator",
|
||||
internal: true,
|
||||
},
|
||||
prompt:
|
||||
'Respond with exactly this JSON (no markdown, no extra keys, no extra text): {"message":"hello"}',
|
||||
schema: z.object({ message: z.string() }),
|
||||
maxRetries: 2,
|
||||
persistSession: false,
|
||||
});
|
||||
|
||||
expect(result.message.trim().toLowerCase()).toBe("hello");
|
||||
expect(existsSync(hookMarker)).toBe(false);
|
||||
}, 180000);
|
||||
});
|
||||
|
||||
@@ -1067,6 +1067,35 @@ describe("ClaudeAgentSession features", () => {
|
||||
await session.close();
|
||||
});
|
||||
|
||||
test("disables Claude hooks for internal agents only", async () => {
|
||||
const { queryFactory } = createQueryMock();
|
||||
const client = new ClaudeAgentClient({
|
||||
logger,
|
||||
queryFactory,
|
||||
resolveBinary: async () => "/test/claude/bin",
|
||||
});
|
||||
const internalSession = await client.createSession({
|
||||
provider: "claude",
|
||||
cwd: process.cwd(),
|
||||
internal: true,
|
||||
});
|
||||
const userSession = await client.createSession({
|
||||
provider: "claude",
|
||||
cwd: process.cwd(),
|
||||
});
|
||||
|
||||
await internalSession.startTurn("hello");
|
||||
await userSession.startTurn("hello");
|
||||
|
||||
expect(queryFactory.mock.calls[0]?.[0].options.settings).toMatchObject({
|
||||
disableAllHooks: true,
|
||||
});
|
||||
expect(queryFactory.mock.calls[1]?.[0].options.settings).toBeUndefined();
|
||||
|
||||
await internalSession.close();
|
||||
await userSession.close();
|
||||
});
|
||||
|
||||
test("turns Claude thinking off without retaining an effort level", async () => {
|
||||
const { queryFactory, launches } = createQueryMock();
|
||||
const client = new ClaudeAgentClient({
|
||||
|
||||
@@ -3363,13 +3363,17 @@ class ClaudeAgentSession implements AgentSession {
|
||||
input: { ultracode: boolean },
|
||||
): Pick<ClaudeOptions, "settings"> | Record<string, never> {
|
||||
const fastMode = this.resolveFastModeSetting();
|
||||
if (fastMode === null && !input.ultracode) {
|
||||
// Internal agents do daemon work such as naming a branch, so the user's and
|
||||
// project's hooks must not run for them.
|
||||
const disableAllHooks = this.config.internal === true;
|
||||
if (fastMode === null && !input.ultracode && !disableAllHooks) {
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
settings: mergeClaudeSettings(providerOptions.settings, {
|
||||
...(fastMode === null ? {} : { fastMode }),
|
||||
...(input.ultracode ? { ultracode: true } : {}),
|
||||
...(disableAllHooks ? { disableAllHooks: true } : {}),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user