Merge pull request #4133 from code-yeongyu/fix/3816-frozen-output-args

fix: replace direct output.args mutation with replaceToolArgs helper (fixes #3816)
This commit is contained in:
YeonGyu-Kim
2026-05-18 13:09:30 +09:00
committed by GitHub
13 changed files with 262 additions and 28 deletions
+2 -1
View File
@@ -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 = `<system-reminder>${SINGLE_TASK_DIRECTIVE}</system-reminder>\n` + prompt
replaceToolArgs(toolOutput, { prompt: `<system-reminder>${SINGLE_TASK_DIRECTIVE}</system-reminder>\n` + prompt })
log(`[${HOOK_NAME}] Injected single-task directive to task`, {
sessionID: toolInput.sessionID,
})
@@ -8,7 +8,7 @@ import {
import { appendTranscriptEntry } from "../transcript"
import { cacheToolInput } from "../tool-input-cache"
import type { PluginConfig } from "../types"
import { isHookDisabled, log } from "../../../shared"
import { isHookDisabled, log, replaceToolArgs } from "../../../shared"
export function createToolExecuteBeforeHandler(ctx: PluginInput, config: PluginConfig) {
return async (
@@ -39,7 +39,7 @@ export function createToolExecuteBeforeHandler(ctx: PluginInput, config: PluginC
)
}
output.args.todos = parsed
replaceToolArgs(output, { todos: parsed })
log("todowrite: parsed todos string to array", { sessionID: input.sessionID })
}
@@ -87,7 +87,7 @@ export function createToolExecuteBeforeHandler(ctx: PluginInput, config: PluginC
}
if (result.modifiedInput) {
Object.assign(output.args, result.modifiedInput)
replaceToolArgs(output, result.modifiedInput as Record<string, unknown>)
}
}
}
+2 -1
View File
@@ -1,6 +1,7 @@
import type { PluginInput } from "@opencode-ai/plugin"
import { resolveSessionEventID } from "../../shared/event-session-id"
import { log } from "../../shared/logger"
import { replaceToolArgs } from "../../shared/replace-tool-args"
interface TodoSnapshot {
id?: string
@@ -233,7 +234,7 @@ export function createCompactionTodoPreserverHook(
return
}
output.args.todos = snapshot
replaceToolArgs(output, { todos: snapshot })
log(`[${HOOK_NAME}] Replaced late Atlas bootstrap todowrite with restored snapshot`, {
sessionID: input.sessionID,
count: snapshot.length,
@@ -1,6 +1,6 @@
import type { PluginInput } from "@opencode-ai/plugin"
import { HOOK_NAME, NON_INTERACTIVE_ENV, SHELL_COMMAND_PATTERNS } from "./constants"
import { log, buildEnvPrefix } from "../../shared"
import { log, buildEnvPrefix, replaceToolArgs } from "../../shared"
import { detectShellType, type ShellType } from "../../shared/shell-env"
export * from "./constants"
@@ -97,7 +97,7 @@ export function createNonInteractiveEnvHook(_ctx: PluginInput) {
return
}
output.args.command = `${envPrefix} ${command}`
replaceToolArgs(output, { command: `${envPrefix} ${command}` })
log(`[${HOOK_NAME}] Prepended non-interactive env vars to git command`, {
sessionID: input.sessionID,
+2 -1
View File
@@ -1,6 +1,7 @@
import type { PluginInput } from "@opencode-ai/plugin"
import { HOOK_NAME, BLOCKED_TOOLS, PLANNING_CONSULT_WARNING, PROMETHEUS_WORKFLOW_REMINDER } from "./constants"
import { log } from "../../shared/logger"
import { replaceToolArgs } from "../../shared/replace-tool-args"
import { SYSTEM_DIRECTIVE_PREFIX } from "../../shared/system-directive"
import { getAgentDisplayName } from "../../shared/agent-display-names"
import { getAgentFromSession } from "./agent-resolution"
@@ -27,7 +28,7 @@ export function createPrometheusMdOnlyHook(ctx: PluginInput) {
if (TASK_TOOLS.includes(toolName)) {
const prompt = output.args.prompt as string | undefined
if (prompt && !prompt.includes(SYSTEM_DIRECTIVE_PREFIX)) {
output.args.prompt = PLANNING_CONSULT_WARNING + prompt
replaceToolArgs(output, { prompt: PLANNING_CONSULT_WARNING + prompt })
log(`[${HOOK_NAME}] Injected planning warning to ${toolName}`, {
sessionID: input.sessionID,
tool: toolName,
+3 -1
View File
@@ -1,3 +1,5 @@
import { replaceToolArgs } from "../../shared/replace-tool-args"
const MAX_LABEL_LENGTH = 30;
interface QuestionOption {
@@ -56,7 +58,7 @@ export function createQuestionLabelTruncatorHook() {
if (toolName === "askuserquestion" || toolName === "ask_user_question") {
if (hasQuestions(output.args)) {
const truncatedArgs = truncateQuestionLabels(output.args);
Object.assign(output.args, truncatedArgs);
replaceToolArgs(output, { questions: truncatedArgs.questions });
}
}
},
+2 -1
View File
@@ -3,6 +3,7 @@ import type { PluginInput } from "@opencode-ai/plugin"
import { isCallerOrchestrator } from "../../shared/session-utils"
import { SYSTEM_DIRECTIVE_PREFIX } from "../../shared/system-directive"
import { log } from "../../shared/logger"
import { replaceToolArgs } from "../../shared/replace-tool-args"
import { HOOK_NAME, NOTEPAD_DIRECTIVE } from "./constants"
export function createSisyphusJuniorNotepadHook(ctx: PluginInput) {
@@ -33,7 +34,7 @@ export function createSisyphusJuniorNotepadHook(ctx: PluginInput) {
}
// 5. Prepend directive
output.args.prompt = NOTEPAD_DIRECTIVE + prompt
replaceToolArgs(output, { prompt: NOTEPAD_DIRECTIVE + prompt })
// 6. Log injection
log(`[${HOOK_NAME}] Injected notepad directive to task`, {
+2 -2
View File
@@ -1,5 +1,5 @@
import type { PluginInput } from "@opencode-ai/plugin"
import { log } from "../../shared"
import { log, replaceToolArgs } from "../../shared"
import {
MAX_WEBFETCH_REDIRECTS,
WEBFETCH_REDIRECT_ERROR_PATTERNS,
@@ -85,7 +85,7 @@ export function createWebFetchRedirectGuardHook(_ctx: PluginInput) {
})
if (resolution.type === "resolved") {
output.args.url = resolution.url
replaceToolArgs(output, { url: resolution.url })
return
}