refactor: remove redundant subagent-question-blocker hook

Replace PreToolUse hook-based question tool blocking with the existing
tools parameter approach (tools: { question: false }) which physically
removes the tool from the LLM's toolset before inference.

The hook was redundant because every session.prompt() call already passes
question: false via the tools parameter. OpenCode converts this to a
PermissionNext deny rule and deletes the tool from the toolset, preventing
the LLM from even seeing it. The hook only fired after the LLM already
called the tool, wasting tokens.

Changes:
- Remove subagent-question-blocker hook invocation from PreToolUse chain
- Remove hook registration from create-session-hooks.ts
- Delete src/hooks/subagent-question-blocker/ directory (dead code)
- Remove hook from HookNameSchema and barrel export
- Fix sync-executor.ts missing question: false in tools parameter
- Add regression tests for both the removal and the tools parameter
This commit is contained in:
YeonGyu-Kim
2026-02-13 14:55:46 +09:00
parent 1ee8f4a2d6
commit d16073aa59
12 changed files with 166 additions and 125 deletions
-4
View File
@@ -19,7 +19,6 @@ import {
createPrometheusMdOnlyHook,
createSisyphusJuniorNotepadHook,
createQuestionLabelTruncatorHook,
createSubagentQuestionBlockerHook,
createPreemptiveCompactionHook,
} from "../../hooks"
import { createAnthropicEffortHook } from "../../hooks/anthropic-effort"
@@ -49,7 +48,6 @@ export type SessionHooks = {
prometheusMdOnly: ReturnType<typeof createPrometheusMdOnlyHook> | null
sisyphusJuniorNotepad: ReturnType<typeof createSisyphusJuniorNotepadHook> | null
questionLabelTruncator: ReturnType<typeof createQuestionLabelTruncatorHook>
subagentQuestionBlocker: ReturnType<typeof createSubagentQuestionBlockerHook>
taskResumeInfo: ReturnType<typeof createTaskResumeInfoHook>
anthropicEffort: ReturnType<typeof createAnthropicEffortHook> | null
}
@@ -149,7 +147,6 @@ export function createSessionHooks(args: {
: null
const questionLabelTruncator = createQuestionLabelTruncatorHook()
const subagentQuestionBlocker = createSubagentQuestionBlockerHook()
const taskResumeInfo = createTaskResumeInfoHook()
const anthropicEffort = isHookEnabled("anthropic-effort")
@@ -174,7 +171,6 @@ export function createSessionHooks(args: {
prometheusMdOnly,
sisyphusJuniorNotepad,
questionLabelTruncator,
subagentQuestionBlocker,
taskResumeInfo,
anthropicEffort,
}
+35 -5
View File
@@ -1,10 +1,38 @@
import { describe, expect, test } from "bun:test"
import { createToolExecuteBeforeHandler } from "./tool-execute-before"
import type { CreatedHooks } from "../create-hooks"
const { describe, expect, test } = require("bun:test")
const { createToolExecuteBeforeHandler } = require("./tool-execute-before")
describe("createToolExecuteBeforeHandler", () => {
test("does not execute subagent question blocker hook for question tool", async () => {
//#given
const ctx = {
client: {
session: {
messages: async () => ({ data: [] }),
},
},
}
const hooks = {
subagentQuestionBlocker: {
"tool.execute.before": async () => {
throw new Error("subagentQuestionBlocker should not run")
},
},
}
const handler = createToolExecuteBeforeHandler({ ctx, hooks })
const input = { tool: "question", sessionID: "ses_sub", callID: "call_1" }
const output = { args: { questions: [] } as Record<string, unknown> }
//#when
const run = handler(input, output)
//#then
await expect(run).resolves.toBeUndefined()
})
describe("task tool subagent_type normalization", () => {
const emptyHooks = {} as CreatedHooks
const emptyHooks = {}
function createCtxWithSessionMessages(messages: Array<{ info?: { agent?: string; role?: string } }> = []) {
return {
@@ -13,7 +41,7 @@ describe("createToolExecuteBeforeHandler", () => {
messages: async () => ({ data: messages }),
},
},
} as unknown as Parameters<typeof createToolExecuteBeforeHandler>[0]["ctx"]
}
}
test("sets subagent_type to sisyphus-junior when category is provided without subagent_type", async () => {
@@ -136,3 +164,5 @@ describe("createToolExecuteBeforeHandler", () => {
})
})
})
export {}
-1
View File
@@ -17,7 +17,6 @@ export function createToolExecuteBeforeHandler(args: {
const { ctx, hooks } = args
return async (input, output): Promise<void> => {
await hooks.subagentQuestionBlocker?.["tool.execute.before"]?.(input, output)
await hooks.writeExistingFileGuard?.["tool.execute.before"]?.(input, output)
await hooks.questionLabelTruncator?.["tool.execute.before"]?.(input, output)
await hooks.claudeCodeHooks?.["tool.execute.before"]?.(input, output)