/ utils / hooks / hookHelpers.ts
hookHelpers.ts
 1  import { z } from 'zod/v4'
 2  import type { Tool } from '../../Tool.js'
 3  import {
 4    SYNTHETIC_OUTPUT_TOOL_NAME,
 5    SyntheticOutputTool,
 6  } from '../../tools/SyntheticOutputTool/SyntheticOutputTool.js'
 7  import { substituteArguments } from '../argumentSubstitution.js'
 8  import { lazySchema } from '../lazySchema.js'
 9  import type { SetAppState } from '../messageQueueManager.js'
10  import { hasSuccessfulToolCall } from '../messages.js'
11  import { addFunctionHook } from './sessionHooks.js'
12  
13  /**
14   * Schema for hook responses (shared by prompt and agent hooks)
15   */
16  export const hookResponseSchema = lazySchema(() =>
17    z.object({
18      ok: z.boolean().describe('Whether the condition was met'),
19      reason: z
20        .string()
21        .describe('Reason, if the condition was not met')
22        .optional(),
23    }),
24  )
25  
26  /**
27   * Add hook input JSON to prompt, either replacing $ARGUMENTS placeholder or appending.
28   * Also supports indexed arguments like $ARGUMENTS[0], $ARGUMENTS[1], or shorthand $0, $1, etc.
29   */
30  export function addArgumentsToPrompt(
31    prompt: string,
32    jsonInput: string,
33  ): string {
34    return substituteArguments(prompt, jsonInput)
35  }
36  
37  /**
38   * Create a StructuredOutput tool configured for hook responses.
39   * Reusable by agent hooks and background verification.
40   */
41  export function createStructuredOutputTool(): Tool {
42    return {
43      ...SyntheticOutputTool,
44      inputSchema: hookResponseSchema(),
45      inputJSONSchema: {
46        type: 'object',
47        properties: {
48          ok: {
49            type: 'boolean',
50            description: 'Whether the condition was met',
51          },
52          reason: {
53            type: 'string',
54            description: 'Reason, if the condition was not met',
55          },
56        },
57        required: ['ok'],
58        additionalProperties: false,
59      },
60      async prompt(): Promise<string> {
61        return `Use this tool to return your verification result. You MUST call this tool exactly once at the end of your response.`
62      },
63    }
64  }
65  
66  /**
67   * Register a function hook that enforces structured output via SyntheticOutputTool.
68   * Used by ask.tsx, execAgentHook.ts, and background verification.
69   */
70  export function registerStructuredOutputEnforcement(
71    setAppState: SetAppState,
72    sessionId: string,
73  ): void {
74    addFunctionHook(
75      setAppState,
76      sessionId,
77      'Stop',
78      '', // No matcher - applies to all stops
79      messages => hasSuccessfulToolCall(messages, SYNTHETIC_OUTPUT_TOOL_NAME),
80      `You MUST call the ${SYNTHETIC_OUTPUT_TOOL_NAME} tool to complete this request. Call this tool now.`,
81      { timeout: 5000 },
82    )
83  }