Skip to content

Commit 22449c2

Browse files
committed
feat: research background agent hardening
1 parent 4f5ee10 commit 22449c2

4 files changed

Lines changed: 49 additions & 4 deletions

File tree

packages/agent/src/adapters/claude/tools.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ export const AGENT_TOOLS: Set<string> = new Set([
3333
"Skill",
3434
]);
3535

36+
export const RESEARCH_BACKGROUND_TOOLS: string[] = [
37+
...READ_TOOLS,
38+
...SEARCH_TOOLS,
39+
...AGENT_TOOLS,
40+
"EnterPlanMode",
41+
"ExitPlanMode",
42+
];
43+
3644
const BASE_ALLOWED_TOOLS = [
3745
...READ_TOOLS,
3846
...SEARCH_TOOLS,

packages/agent/src/server/agent-server.ts

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
type InProcessAcpConnection,
1313
} from "../adapters/acp-connection";
1414
import { selectRecentTurns } from "../adapters/claude/session/jsonl-hydration";
15+
import { RESEARCH_BACKGROUND_TOOLS } from "../adapters/claude/tools";
1516
import { PostHogAPIClient } from "../posthog-api";
1617
import {
1718
type ConversationTurn,
@@ -633,18 +634,24 @@ export class AgentServer {
633634
this.detectedPrUrl = prUrl;
634635
}
635636

637+
const claudeCodeOptions: Record<string, unknown> = {};
638+
if (this.config.claudeCode?.plugins?.length) {
639+
claudeCodeOptions.plugins = this.config.claudeCode.plugins;
640+
}
641+
if (this.config.toolsPreset === "research_background_agent") {
642+
claudeCodeOptions.tools = RESEARCH_BACKGROUND_TOOLS;
643+
}
644+
636645
const sessionResponse = await clientConnection.newSession({
637646
cwd: this.config.repositoryPath ?? "/tmp/workspace",
638647
mcpServers: this.config.mcpServers ?? [],
639648
_meta: {
640649
sessionId: payload.run_id,
641650
taskRunId: payload.run_id,
642651
systemPrompt: this.buildSessionSystemPrompt(prUrl),
643-
...(this.config.claudeCode?.plugins?.length && {
652+
...(Object.keys(claudeCodeOptions).length > 0 && {
644653
claudeCode: {
645-
options: {
646-
plugins: this.config.claudeCode.plugins,
647-
},
654+
options: claudeCodeOptions,
648655
},
649656
}),
650657
},
@@ -1095,6 +1102,27 @@ Important:
10951102
options: params.options,
10961103
});
10971104

1105+
// Defense-in-depth: deny tools not in the restricted allowlist
1106+
if (this.config.toolsPreset === "research_background_agent") {
1107+
const meta = params.toolCall?._meta as
1108+
| Record<string, unknown>
1109+
| undefined;
1110+
const toolName =
1111+
(meta?.codeToolKind as string) ?? (meta?.toolName as string);
1112+
if (toolName && !RESEARCH_BACKGROUND_TOOLS.includes(toolName)) {
1113+
this.logger.warn(
1114+
"Denied restricted tool in research_background_agent mode",
1115+
{ toolName },
1116+
);
1117+
return {
1118+
outcome: { outcome: "cancelled" as const },
1119+
_meta: {
1120+
message: `Tool "${toolName}" is not available in research mode. You can only use read, search, and planning tools.`,
1121+
},
1122+
};
1123+
}
1124+
}
1125+
10981126
const allowOption = params.options.find(
10991127
(o) => o.kind === "allow_once" || o.kind === "allow_always",
11001128
);

packages/agent/src/server/bin.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ program
7979
"--claudeCodeConfig <json>",
8080
"Claude Code config as JSON (systemPrompt, systemPromptAppend, plugins)",
8181
)
82+
.option(
83+
"--toolsPreset <preset>",
84+
"Tools preset: default or research_background_agent",
85+
"default",
86+
)
8287
.action(async (options) => {
8388
const envResult = envSchema.safeParse(process.env);
8489

@@ -118,6 +123,7 @@ program
118123
mcpServers,
119124
baseBranch: options.baseBranch,
120125
claudeCode,
126+
toolsPreset: options.toolsPreset,
121127
});
122128

123129
process.on("SIGINT", async () => {

packages/agent/src/server/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import type { AgentMode } from "../types";
22
import type { RemoteMcpServer } from "./schemas";
33

4+
export type ToolsPreset = "default" | "research_background_agent";
5+
46
export interface ClaudeCodeConfig {
57
systemPrompt?:
68
| string
@@ -22,4 +24,5 @@ export interface AgentServerConfig {
2224
mcpServers?: RemoteMcpServer[];
2325
baseBranch?: string;
2426
claudeCode?: ClaudeCodeConfig;
27+
toolsPreset?: ToolsPreset;
2528
}

0 commit comments

Comments
 (0)