Address review feedback: use effort params, fix model names, add safety checks

This commit is contained in:
pullfrog
2026-01-12 14:12:16 -08:00
committed by Colin McDonnell
parent 89e93d3398
commit c6572f0987
6 changed files with 1291 additions and 1315 deletions
+10 -15
View File
@@ -5,12 +5,12 @@ import { log } from "../utils/cli.ts";
import { addInstructions } from "./instructions.ts"; import { addInstructions } from "./instructions.ts";
import { agent, createAgentEnv, installFromNpmTarball } from "./shared.ts"; import { agent, createAgentEnv, installFromNpmTarball } from "./shared.ts";
// model configuration based on effort level // effort levels for Claude Code
// uses model family aliases that auto-resolve to latest version // Claude Code automatically selects the best model based on effort
const claudeModels: Record<Effort, { model: string; thinking: boolean }> = { const claudeEffortLevels: Record<Effort, "low" | "medium" | "high"> = {
nothink: { model: "claude-haiku-4-5", thinking: false }, nothink: "low",
think: { model: "claude-sonnet-4-5", thinking: true }, think: "medium",
max: { model: "claude-opus-4-5", thinking: true }, max: "high",
} as const; } as const;
export const claude = agent({ export const claude = agent({
@@ -30,9 +30,9 @@ export const claude = agent({
const prompt = addInstructions({ payload, repo }); const prompt = addInstructions({ payload, repo });
log.group("Full prompt", () => log.info(prompt)); log.group("Full prompt", () => log.info(prompt));
// get model configuration based on effort // get effort level - Claude Code automatically selects the best model
const modelConfig = claudeModels[effort]; const effortLevel = claudeEffortLevels[effort];
log.info(`Using model: ${modelConfig.model}, thinking: ${modelConfig.thinking}`); log.info(`Using effort: ${effortLevel}`);
// SECURITY: For PUBLIC repos, Claude Code spawns subprocesses with full process.env, leaking API keys. // SECURITY: For PUBLIC repos, Claude Code spawns subprocesses with full process.env, leaking API keys.
// disable native Bash; agents use MCP bash tool which filters secrets. // disable native Bash; agents use MCP bash tool which filters secrets.
@@ -62,15 +62,10 @@ export const claude = agent({
const queryOptions: Options = { const queryOptions: Options = {
...sandboxOptions, ...sandboxOptions,
mcpServers, mcpServers,
model: modelConfig.model, effort: effortLevel,
pathToClaudeCodeExecutable: cliPath, pathToClaudeCodeExecutable: cliPath,
env: createAgentEnv({ ANTHROPIC_API_KEY: apiKey }), env: createAgentEnv({ ANTHROPIC_API_KEY: apiKey }),
}; };
// only set maxThinkingTokens when we want to disable thinking (0)
// omit the property to use default when thinking is enabled
if (!modelConfig.thinking) {
queryOptions.maxThinkingTokens = 0;
}
const queryInstance = query({ const queryInstance = query({
prompt, prompt,
+33 -25
View File
@@ -7,12 +7,12 @@ import { log } from "../utils/cli.ts";
import { addInstructions } from "./instructions.ts"; import { addInstructions } from "./instructions.ts";
import { agent, installFromNpmTarball, setupProcessAgentEnv } from "./shared.ts"; import { agent, installFromNpmTarball, setupProcessAgentEnv } from "./shared.ts";
// model configuration based on effort level // reasoning effort configuration based on effort level
// uses model family aliases that auto-resolve to latest version // uses model_reasoning_effort parameter for o1 models
const codexModels: Record<Effort, string> = { const codexReasoningEffort: Record<Effort, string | undefined> = {
nothink: "gpt-4o-mini", nothink: "low",
think: "gpt-5.2-instant", think: undefined, // use default
max: "gpt-5.2-thinking", max: "xhigh",
} as const; } as const;
interface WriteCodexConfigParams { interface WriteCodexConfigParams {
@@ -88,9 +88,13 @@ export const codex = agent({
CODEX_HOME: codexDir, // point Codex to our config directory CODEX_HOME: codexDir, // point Codex to our config directory
}); });
// get model based on effort level // get reasoning effort based on effort level
const model = codexModels[effort]; const modelReasoningEffort = codexReasoningEffort[effort];
log.info(`Using model: ${model}`); if (modelReasoningEffort) {
log.info(`Using model_reasoning_effort: ${modelReasoningEffort}`);
} else {
log.info(`Using default reasoning effort`);
}
// Configure Codex // Configure Codex
const codexOptions: CodexOptions = { const codexOptions: CodexOptions = {
@@ -103,22 +107,26 @@ export const codex = agent({
} }
const codex = new Codex(codexOptions); const codex = new Codex(codexOptions);
const thread = codex.startThread(
payload.sandbox // Build thread options with optional model_reasoning_effort
? { const baseThreadOptions = payload.sandbox
model, ? {
approvalPolicy: "never", approvalPolicy: "never" as const,
sandboxMode: "read-only", sandboxMode: "read-only" as const,
networkAccessEnabled: false, networkAccessEnabled: false,
} }
: { : {
model, approvalPolicy: "never" as const,
approvalPolicy: "never", // use danger-full-access to allow git operations (workspace-write blocks .git directory writes)
// use danger-full-access to allow git operations (workspace-write blocks .git directory writes) sandboxMode: "danger-full-access" as const,
sandboxMode: "danger-full-access", networkAccessEnabled: true,
networkAccessEnabled: true, };
}
); const threadOptions = modelReasoningEffort
? { ...baseThreadOptions, model_reasoning_effort: modelReasoningEffort }
: baseThreadOptions;
const thread = codex.startThread(threadOptions);
try { try {
const streamedTurn = await thread.runStreamed(addInstructions({ payload, repo })); const streamedTurn = await thread.runStreamed(addInstructions({ payload, repo }));
+4 -4
View File
@@ -12,11 +12,11 @@ import {
} from "./shared.ts"; } from "./shared.ts";
// model configuration based on effort level // model configuration based on effort level
// uses model family aliases that auto-resolve to latest version // auto for default, flash for fast/low-cost, pro for max capability
const geminiModels = { const geminiModels = {
nothink: "gemini-2.5-pro", nothink: "gemini-2.5-flash",
think: "gemini-3-pro", think: "gemini-2.5-pro", // auto selection
max: "gemini-3-pro", max: "gemini-2.5-pro",
} as const; } as const;
// gemini cli event types inferred from stream-json output (NDJSON format) // gemini cli event types inferred from stream-json output (NDJSON format)
+1 -1
View File
@@ -68,7 +68,7 @@ export const opencode = agent({
// add API keys from apiKeys object // add API keys from apiKeys object
for (const [key, value] of Object.entries(apiKeys || {})) { for (const [key, value] of Object.entries(apiKeys || {})) {
env[key] = value; env[key.toUpperCase()] = value;
// also set GOOGLE_GENERATIVE_AI_API_KEY for Google provider compatibility // also set GOOGLE_GENERATIVE_AI_API_KEY for Google provider compatibility
if (key === "GEMINI_API_KEY") { if (key === "GEMINI_API_KEY") {
env.GOOGLE_GENERATIVE_AI_API_KEY = value; env.GOOGLE_GENERATIVE_AI_API_KEY = value;
+1241 -1268
View File
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -401,10 +401,10 @@ function parsePayload(inputs: Inputs): Payload {
if (!("~pullfrog" in parsedPrompt)) { if (!("~pullfrog" in parsedPrompt)) {
throw new Error(); throw new Error();
} }
// internal invocation: use effort from payload, fallback to input // internal invocation: use effort from payload, fallback to input, default to "think"
return { return {
...parsedPrompt, ...parsedPrompt,
effort: parsedPrompt.effort ?? inputs.effort, effort: parsedPrompt.effort ?? inputs.effort ?? "think",
} as Payload; } as Payload;
} catch { } catch {
// external invocation: use effort from input // external invocation: use effort from input