rhodawk-ai-devops-engine / vendor /openclaude /src /services /toolUseSummary /toolUseSummaryGenerator.ts
| /** | |
| * Tool Use Summary Generator | |
| * | |
| * Generates human-readable summaries of completed tool batches using Haiku. | |
| * Used by the SDK to provide high-level progress updates to clients. | |
| */ | |
| import { E_TOOL_USE_SUMMARY_GENERATION_FAILED } from '../../constants/errorIds.js' | |
| import { toError } from '../../utils/errors.js' | |
| import { logError } from '../../utils/log.js' | |
| import { jsonStringify } from '../../utils/slowOperations.js' | |
| import { asSystemPrompt } from '../../utils/systemPromptType.js' | |
| import { queryHaiku } from '../api/claude.js' | |
| const TOOL_USE_SUMMARY_SYSTEM_PROMPT = `Write a short summary label describing what these tool calls accomplished. It appears as a single-line row in a mobile app and truncates around 30 characters, so think git-commit-subject, not sentence. | |
| Keep the verb in past tense and the most distinctive noun. Drop articles, connectors, and long location context first. | |
| Examples: | |
| - Searched in auth/ | |
| - Fixed NPE in UserService | |
| - Created signup endpoint | |
| - Read config.json | |
| - Ran failing tests` | |
| type ToolInfo = { | |
| name: string | |
| input: unknown | |
| output: unknown | |
| } | |
| export type GenerateToolUseSummaryParams = { | |
| tools: ToolInfo[] | |
| signal: AbortSignal | |
| isNonInteractiveSession: boolean | |
| lastAssistantText?: string | |
| } | |
| /** | |
| * Generates a human-readable summary of completed tools. | |
| * | |
| * @param params - Parameters including tools executed and their results | |
| * @returns A brief summary string, or null if generation fails | |
| */ | |
| export async function generateToolUseSummary({ | |
| tools, | |
| signal, | |
| isNonInteractiveSession, | |
| lastAssistantText, | |
| }: GenerateToolUseSummaryParams): Promise<string | null> { | |
| if (tools.length === 0) { | |
| return null | |
| } | |
| try { | |
| // Build a concise representation of what tools did | |
| const toolSummaries = tools | |
| .map(tool => { | |
| const inputStr = truncateJson(tool.input, 300) | |
| const outputStr = truncateJson(tool.output, 300) | |
| return `Tool: ${tool.name}\nInput: ${inputStr}\nOutput: ${outputStr}` | |
| }) | |
| .join('\n\n') | |
| const contextPrefix = lastAssistantText | |
| ? `User's intent (from assistant's last message): ${lastAssistantText.slice(0, 200)}\n\n` | |
| : '' | |
| const response = await queryHaiku({ | |
| systemPrompt: asSystemPrompt([TOOL_USE_SUMMARY_SYSTEM_PROMPT]), | |
| userPrompt: `${contextPrefix}Tools completed:\n\n${toolSummaries}\n\nLabel:`, | |
| signal, | |
| options: { | |
| querySource: 'tool_use_summary_generation', | |
| enablePromptCaching: true, | |
| agents: [], | |
| isNonInteractiveSession, | |
| hasAppendSystemPrompt: false, | |
| mcpTools: [], | |
| }, | |
| }) | |
| const summary = response.message.content | |
| .filter(block => block.type === 'text') | |
| .map(block => (block.type === 'text' ? block.text : '')) | |
| .join('') | |
| .trim() | |
| return summary || null | |
| } catch (error) { | |
| // Log but don't fail - summaries are non-critical | |
| const err = toError(error) | |
| err.cause = { errorId: E_TOOL_USE_SUMMARY_GENERATION_FAILED } | |
| logError(err) | |
| return null | |
| } | |
| } | |
| /** | |
| * Truncates a JSON value to a maximum length for the prompt. | |
| */ | |
| function truncateJson(value: unknown, maxLength: number): string { | |
| try { | |
| const str = jsonStringify(value) | |
| if (str.length <= maxLength) { | |
| return str | |
| } | |
| return str.slice(0, maxLength - 3) + '...' | |
| } catch { | |
| return '[unable to serialize]' | |
| } | |
| } | |