From b20e2c9c553bce08a5d57b5608001398c7af8c39 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Mon, 18 May 2026 13:04:10 +0900 Subject: [PATCH] fix: refactor 5 additional aliased output.args mutations + strengthen audit test Address Oracle review feedback: refactor 4 aliased mutations via argsObject in plugin/tool-execute-before.ts and 1 via toolOutput in atlas/tool-execute-before.ts. Strengthen audit test regex to catch Output.args mutations regardless of the variable name prefix (toolOutput, argsObject aliases). Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- src/hooks/atlas/tool-execute-before.ts | 3 ++- src/plugin/tool-execute-before.ts | 29 +++++++++++----------- src/shared/replace-tool-args.audit.test.ts | 6 +++-- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/hooks/atlas/tool-execute-before.ts b/src/hooks/atlas/tool-execute-before.ts index f4e03ad65..d9ede7484 100644 --- a/src/hooks/atlas/tool-execute-before.ts +++ b/src/hooks/atlas/tool-execute-before.ts @@ -1,4 +1,5 @@ import { log } from "../../shared/logger" +import { replaceToolArgs } from "../../shared/replace-tool-args" import { SYSTEM_DIRECTIVE_PREFIX } from "../../shared/system-directive" import { isCallerOrchestrator } from "../../shared/session-utils" import type { PluginInput } from "@opencode-ai/plugin" @@ -179,7 +180,7 @@ export function createToolExecuteBeforeHandler(input: { const prompt = toolOutput.args.prompt as string | undefined if (prompt && !prompt.includes(SYSTEM_DIRECTIVE_PREFIX)) { - toolOutput.args.prompt = `${SINGLE_TASK_DIRECTIVE}\n` + prompt + replaceToolArgs(toolOutput, { prompt: `${SINGLE_TASK_DIRECTIVE}\n` + prompt }) log(`[${HOOK_NAME}] Injected single-task directive to task`, { sessionID: toolInput.sessionID, }) diff --git a/src/plugin/tool-execute-before.ts b/src/plugin/tool-execute-before.ts index ce1129ccc..508f40dde 100644 --- a/src/plugin/tool-execute-before.ts +++ b/src/plugin/tool-execute-before.ts @@ -100,21 +100,20 @@ export function createToolExecuteBeforeHandler(args: { } if (input.tool === "task") { - const argsObject = output.args - const category = typeof argsObject.category === "string" ? argsObject.category : undefined - const subagentType = typeof argsObject.subagent_type === "string" ? argsObject.subagent_type : undefined - const taskId = typeof argsObject.task_id === "string" ? argsObject.task_id : undefined + const category = typeof output.args.category === "string" ? output.args.category : undefined + const subagentType = typeof output.args.subagent_type === "string" ? output.args.subagent_type : undefined + const taskId = typeof output.args.task_id === "string" ? output.args.task_id : undefined if (category) { - argsObject.subagent_type = "sisyphus-junior" + replaceToolArgs(output, { subagent_type: "sisyphus-junior" }) } else if (!subagentType && taskId) { const resolvedAgent = await resolveSessionAgent(ctx.client, taskId) - argsObject.subagent_type = resolvedAgent ?? "continue" + replaceToolArgs(output, { subagent_type: resolvedAgent ?? "continue" }) } const normalizedSubagentType = - typeof argsObject.subagent_type === "string" ? stripInvisibleAgentCharacters(argsObject.subagent_type) : undefined - const prompt = typeof argsObject.prompt === "string" ? argsObject.prompt : "" + typeof output.args.subagent_type === "string" ? stripInvisibleAgentCharacters(output.args.subagent_type) : undefined + const prompt = typeof output.args.prompt === "string" ? output.args.prompt : "" const loopState = typeof ctx.directory === "string" ? readState(ctx.directory) : null const shouldInjectOracleVerification = normalizedSubagentType === "oracle" @@ -136,12 +135,14 @@ export function createToolExecuteBeforeHandler(args: { verification_attempt_id: verificationAttemptId, verification_session_id: undefined, }) - argsObject.run_in_background = false - argsObject.prompt = buildUltraworkOracleVerificationPrompt( - prompt, - loopState.prompt, - verificationAttemptId, - ) + replaceToolArgs(output, { + run_in_background: false, + prompt: buildUltraworkOracleVerificationPrompt( + prompt, + loopState.prompt, + verificationAttemptId, + ), + }) } } diff --git a/src/shared/replace-tool-args.audit.test.ts b/src/shared/replace-tool-args.audit.test.ts index 4c14faf34..1c85ee6d9 100644 --- a/src/shared/replace-tool-args.audit.test.ts +++ b/src/shared/replace-tool-args.audit.test.ts @@ -23,8 +23,10 @@ async function collectTsFiles(dir: string): Promise { const HELPER_FILE = "shared/replace-tool-args.ts" -const DIRECT_MUTATION_PATTERN = /output\.args\.\w+\s*=[^=]/g -const OBJECT_ASSIGN_PATTERN = /Object\.assign\(\s*output\.args/g +// Matches direct mutations like `output.args.foo =` or `toolOutput.args.foo =` +// but excludes comparisons (===, !==, ==) +const DIRECT_MUTATION_PATTERN = /\w*[Oo]utput\.args\.\w+\s*=[^=]/g +const OBJECT_ASSIGN_PATTERN = /Object\.assign\(\s*\w*[Oo]utput\.args/g describe("replace-tool-args audit", () => { it("#given src/**/*.ts files #when scanning for direct output.args mutation #then no matches found outside the helper", async () => {