feat(omo-codex): block budgeted create_goal calls
This commit is contained in:
@@ -11,6 +11,32 @@ export interface UserPromptSubmitPayload {
|
||||
readonly turn_id?: string;
|
||||
}
|
||||
|
||||
export interface PreToolUsePayload {
|
||||
readonly cwd: string;
|
||||
readonly hook_event_name: "PreToolUse";
|
||||
readonly model: string;
|
||||
readonly permission_mode: string;
|
||||
readonly session_id: string;
|
||||
readonly tool_input: unknown;
|
||||
readonly tool_name: string;
|
||||
readonly tool_use_id: string;
|
||||
readonly transcript_path: string | null;
|
||||
readonly turn_id: string;
|
||||
}
|
||||
|
||||
interface PreToolUseHookOutput {
|
||||
readonly hookSpecificOutput: {
|
||||
readonly hookEventName: "PreToolUse";
|
||||
readonly permissionDecision: "deny";
|
||||
readonly permissionDecisionReason: string;
|
||||
readonly additionalContext: string;
|
||||
};
|
||||
}
|
||||
|
||||
const CREATE_GOAL_TOOL_NAME = "create_goal";
|
||||
const GOAL_BUDGET_WARNING =
|
||||
"Do not set token_budget on create_goal. Omit the budget field so the goal stays unlimited; ultrawork and ultragoal runs must always use unlimited goals.";
|
||||
|
||||
export function parseUserPromptSubmitPayload(raw: string): UserPromptSubmitPayload | null {
|
||||
if (raw.trim().length === 0) return null;
|
||||
try {
|
||||
@@ -22,6 +48,17 @@ export function parseUserPromptSubmitPayload(raw: string): UserPromptSubmitPaylo
|
||||
}
|
||||
}
|
||||
|
||||
export function parsePreToolUsePayload(raw: string): PreToolUsePayload | null {
|
||||
if (raw.trim().length === 0) return null;
|
||||
try {
|
||||
const parsed: unknown = JSON.parse(raw);
|
||||
return isPreToolUsePayload(parsed) ? parsed : null;
|
||||
} catch (error) {
|
||||
if (error instanceof SyntaxError) return null;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function applyUserPromptUltragoalSteering(payload: UserPromptSubmitPayload): Promise<string> {
|
||||
try {
|
||||
if (payload.hook_event_name !== "UserPromptSubmit") return "";
|
||||
@@ -41,6 +78,21 @@ export async function applyUserPromptUltragoalSteering(payload: UserPromptSubmit
|
||||
}
|
||||
}
|
||||
|
||||
export function applyPreToolUseGoalBudgetGuard(payload: PreToolUsePayload): string {
|
||||
if (payload.hook_event_name !== "PreToolUse") return "";
|
||||
if (payload.tool_name !== CREATE_GOAL_TOOL_NAME) return "";
|
||||
if (!hasGoalBudgetInput(payload.tool_input)) return "";
|
||||
const output: PreToolUseHookOutput = {
|
||||
hookSpecificOutput: {
|
||||
hookEventName: "PreToolUse",
|
||||
permissionDecision: "deny",
|
||||
permissionDecisionReason: GOAL_BUDGET_WARNING,
|
||||
additionalContext: GOAL_BUDGET_WARNING,
|
||||
},
|
||||
};
|
||||
return `${JSON.stringify(output)}\n`;
|
||||
}
|
||||
|
||||
export async function runUltragoalHookCli(stdin: NodeJS.ReadableStream, stdout: NodeJS.WritableStream): Promise<void> {
|
||||
try {
|
||||
const payload = parseUserPromptSubmitPayload(await readAll(stdin));
|
||||
@@ -53,6 +105,21 @@ export async function runUltragoalHookCli(stdin: NodeJS.ReadableStream, stdout:
|
||||
}
|
||||
}
|
||||
|
||||
export async function runPreToolUseGoalBudgetGuardCli(
|
||||
stdin: NodeJS.ReadableStream,
|
||||
stdout: NodeJS.WritableStream,
|
||||
): Promise<void> {
|
||||
try {
|
||||
const payload = parsePreToolUsePayload(await readAll(stdin));
|
||||
if (payload === null) return;
|
||||
const output = applyPreToolUseGoalBudgetGuard(payload);
|
||||
if (output.length > 0) stdout.write(output);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) return;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function isUserPromptSubmitPayload(value: unknown): value is UserPromptSubmitPayload {
|
||||
if (!isRecord(value)) return false;
|
||||
return (
|
||||
@@ -64,6 +131,26 @@ function isUserPromptSubmitPayload(value: unknown): value is UserPromptSubmitPay
|
||||
);
|
||||
}
|
||||
|
||||
function isPreToolUsePayload(value: unknown): value is PreToolUsePayload {
|
||||
if (!isRecord(value)) return false;
|
||||
return (
|
||||
value["hook_event_name"] === "PreToolUse" &&
|
||||
typeof value["cwd"] === "string" &&
|
||||
typeof value["model"] === "string" &&
|
||||
typeof value["permission_mode"] === "string" &&
|
||||
typeof value["session_id"] === "string" &&
|
||||
typeof value["tool_name"] === "string" &&
|
||||
typeof value["tool_use_id"] === "string" &&
|
||||
(value["transcript_path"] === null || typeof value["transcript_path"] === "string") &&
|
||||
typeof value["turn_id"] === "string" &&
|
||||
Object.hasOwn(value, "tool_input")
|
||||
);
|
||||
}
|
||||
|
||||
function hasGoalBudgetInput(value: unknown): boolean {
|
||||
return isRecord(value) && (Object.hasOwn(value, "token_budget") || Object.hasOwn(value, "tokenBudget"));
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user