fix: preserve accumulated modifiedInput and common fields on deny/ask from exit code paths
When a hook returns exit code 2 (deny) or 1 (ask), previously accumulated modifiedInput and common fields from earlier allow hooks were discarded. Now all exit paths (exit code and JSON) include accumulated state.
This commit is contained in:
@@ -216,5 +216,37 @@ describe("executePreToolUseHooks", () => {
|
|||||||
expect(result.suppressOutput).toBe(true)
|
expect(result.suppressOutput).toBe(true)
|
||||||
expect(result.systemMessage).toBe("Budget warning: approaching limit")
|
expect(result.systemMessage).toBe("Budget warning: approaching limit")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("#when first hook allows with modifiedInput and second hook denies #then deny includes accumulated modifiedInput", async () => {
|
||||||
|
let callCount = 0
|
||||||
|
dispatchSpy.mockImplementation(async () => {
|
||||||
|
callCount++
|
||||||
|
if (callCount === 1) {
|
||||||
|
return {
|
||||||
|
exitCode: 0,
|
||||||
|
stdout: JSON.stringify({
|
||||||
|
decision: "allow",
|
||||||
|
hookSpecificOutput: {
|
||||||
|
permissionDecision: "allow",
|
||||||
|
updatedInput: { file_path: "/tmp/modified.md" },
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
stderr: "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { exitCode: 2, stdout: "", stderr: "BUDGET EXCEEDED" }
|
||||||
|
})
|
||||||
|
|
||||||
|
const config = createConfig([
|
||||||
|
{ matcher: "*", hooks: [{ type: "command", command: "node modifier.mjs" }] },
|
||||||
|
{ matcher: "Edit|Write", hooks: [{ type: "command", command: "bash budget-guard.sh" }] },
|
||||||
|
])
|
||||||
|
|
||||||
|
const result = await executePreToolUseHooks(createContext(), config)
|
||||||
|
|
||||||
|
expect(callCount).toBe(2)
|
||||||
|
expect(result.decision).toBe("deny")
|
||||||
|
expect(result.modifiedInput).toEqual({ file_path: "/tmp/modified.md" })
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -100,10 +100,12 @@ export async function executePreToolUseHooks(
|
|||||||
return {
|
return {
|
||||||
decision: "deny",
|
decision: "deny",
|
||||||
reason: result.stderr || result.stdout || "Hook blocked the operation",
|
reason: result.stderr || result.stdout || "Hook blocked the operation",
|
||||||
|
modifiedInput: accumulatedModifiedInput,
|
||||||
elapsedMs: Date.now() - startTime,
|
elapsedMs: Date.now() - startTime,
|
||||||
hookName: firstHookName,
|
hookName: firstHookName,
|
||||||
toolName: transformedToolName,
|
toolName: transformedToolName,
|
||||||
inputLines,
|
inputLines,
|
||||||
|
...accumulatedCommonFields,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,10 +113,12 @@ export async function executePreToolUseHooks(
|
|||||||
return {
|
return {
|
||||||
decision: "ask",
|
decision: "ask",
|
||||||
reason: result.stderr || result.stdout,
|
reason: result.stderr || result.stdout,
|
||||||
|
modifiedInput: accumulatedModifiedInput,
|
||||||
elapsedMs: Date.now() - startTime,
|
elapsedMs: Date.now() - startTime,
|
||||||
hookName: firstHookName,
|
hookName: firstHookName,
|
||||||
toolName: transformedToolName,
|
toolName: transformedToolName,
|
||||||
inputLines,
|
inputLines,
|
||||||
|
...accumulatedCommonFields,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user