fix: auto-truncate question option labels exceeding 30 characters
When AI generates AskUserQuestion tool calls with option labels longer than 30 characters, opencode validation rejects them with "too_big" error. This fix adds a pre-tool-use hook that automatically truncates labels to 30 characters (with "..." suffix) before the validation occurs. Fixes the error: "The question tool was called with invalid arguments: expected string to have <=30 characters" Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
cf2320480f
commit
3a22c24cf4
@ -30,3 +30,4 @@ export { createTaskResumeInfoHook } from "./task-resume-info";
|
|||||||
export { createStartWorkHook } from "./start-work";
|
export { createStartWorkHook } from "./start-work";
|
||||||
export { createAtlasHook } from "./atlas";
|
export { createAtlasHook } from "./atlas";
|
||||||
export { createDelegateTaskRetryHook } from "./delegate-task-retry";
|
export { createDelegateTaskRetryHook } from "./delegate-task-retry";
|
||||||
|
export { createQuestionLabelTruncatorHook } from "./question-label-truncator";
|
||||||
|
|||||||
63
src/hooks/question-label-truncator/index.ts
Normal file
63
src/hooks/question-label-truncator/index.ts
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import type { Plugin } from "@opencode-ai/plugin";
|
||||||
|
|
||||||
|
const MAX_LABEL_LENGTH = 30;
|
||||||
|
|
||||||
|
interface QuestionOption {
|
||||||
|
label: string;
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Question {
|
||||||
|
question: string;
|
||||||
|
header?: string;
|
||||||
|
options: QuestionOption[];
|
||||||
|
multiSelect?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AskUserQuestionArgs {
|
||||||
|
questions: Question[];
|
||||||
|
}
|
||||||
|
|
||||||
|
function truncateLabel(label: string, maxLength: number = MAX_LABEL_LENGTH): string {
|
||||||
|
if (label.length <= maxLength) {
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
return label.substring(0, maxLength - 3) + "...";
|
||||||
|
}
|
||||||
|
|
||||||
|
function truncateQuestionLabels(args: AskUserQuestionArgs): AskUserQuestionArgs {
|
||||||
|
if (!args.questions || !Array.isArray(args.questions)) {
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...args,
|
||||||
|
questions: args.questions.map((question) => ({
|
||||||
|
...question,
|
||||||
|
options: question.options?.map((option) => ({
|
||||||
|
...option,
|
||||||
|
label: truncateLabel(option.label),
|
||||||
|
})) ?? [],
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createQuestionLabelTruncatorHook(): Pick<
|
||||||
|
Plugin,
|
||||||
|
"tool.execute.before"
|
||||||
|
> {
|
||||||
|
return {
|
||||||
|
"tool.execute.before": async (input, output) => {
|
||||||
|
const toolName = input.tool?.toLowerCase();
|
||||||
|
|
||||||
|
if (toolName === "askuserquestion" || toolName === "ask_user_question") {
|
||||||
|
const args = output.args as AskUserQuestionArgs | undefined;
|
||||||
|
|
||||||
|
if (args?.questions) {
|
||||||
|
const truncatedArgs = truncateQuestionLabels(args);
|
||||||
|
Object.assign(output.args as object, truncatedArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -31,6 +31,7 @@ import {
|
|||||||
createStartWorkHook,
|
createStartWorkHook,
|
||||||
createAtlasHook,
|
createAtlasHook,
|
||||||
createPrometheusMdOnlyHook,
|
createPrometheusMdOnlyHook,
|
||||||
|
createQuestionLabelTruncatorHook,
|
||||||
} from "./hooks";
|
} from "./hooks";
|
||||||
import {
|
import {
|
||||||
contextCollector,
|
contextCollector,
|
||||||
@ -203,6 +204,8 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
|||||||
? createPrometheusMdOnlyHook(ctx)
|
? createPrometheusMdOnlyHook(ctx)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
const questionLabelTruncator = createQuestionLabelTruncatorHook();
|
||||||
|
|
||||||
const taskResumeInfo = createTaskResumeInfoHook();
|
const taskResumeInfo = createTaskResumeInfoHook();
|
||||||
|
|
||||||
const backgroundManager = new BackgroundManager(ctx, pluginConfig.background_task);
|
const backgroundManager = new BackgroundManager(ctx, pluginConfig.background_task);
|
||||||
@ -484,6 +487,7 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
"tool.execute.before": async (input, output) => {
|
"tool.execute.before": async (input, output) => {
|
||||||
|
await questionLabelTruncator["tool.execute.before"]?.(input, output);
|
||||||
await claudeCodeHooks["tool.execute.before"](input, output);
|
await claudeCodeHooks["tool.execute.before"](input, output);
|
||||||
await nonInteractiveEnv?.["tool.execute.before"](input, output);
|
await nonInteractiveEnv?.["tool.execute.before"](input, output);
|
||||||
await commentChecker?.["tool.execute.before"](input, output);
|
await commentChecker?.["tool.execute.before"](input, output);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user