(
projectId: string,
content: string,
metadata: Record<string, unknown>,
requestId?: string,
options: {
messageType?: 'tool_use' | 'tool_result';
persist?: boolean;
isStreaming?: boolean;
streamedToolHashes?: Set<string>;
} = {},
)
| 348 | Buffer.from(value, 'utf-8').toString('base64'); |
| 349 | |
| 350 | async function dispatchToolMessage( |
| 351 | projectId: string, |
| 352 | content: string, |
| 353 | metadata: Record<string, unknown>, |
| 354 | requestId?: string, |
| 355 | options: { |
| 356 | messageType?: 'tool_use' | 'tool_result'; |
| 357 | persist?: boolean; |
| 358 | isStreaming?: boolean; |
| 359 | streamedToolHashes?: Set<string>; |
| 360 | } = {}, |
| 361 | ) { |
| 362 | const trimmedContent = content.trim(); |
| 363 | if (!trimmedContent) { |
| 364 | return; |
| 365 | } |
| 366 | |
| 367 | const { messageType = 'tool_use', persist = true, isStreaming = false, streamedToolHashes } = options; |
| 368 | |
| 369 | // Enhanced duplicate detection for tool messages |
| 370 | const toolHash = encodeHash(`${messageType}:${trimmedContent}:${JSON.stringify(metadata)}`).substring(0, 16); |
| 371 | if (streamedToolHashes?.has(toolHash)) { |
| 372 | console.debug(`[CodexService] Tool message already processed (hash: ${toolHash}), skipping`); |
| 373 | return; |
| 374 | } |
| 375 | |
| 376 | const enrichedMetadata: Record<string, unknown> = { |
| 377 | cli_type: 'codex', |
| 378 | ...(metadata ?? {}), |
| 379 | }; |
| 380 | const snakeToolName = typeof enrichedMetadata['tool_name'] === 'string' ? (enrichedMetadata['tool_name'] as string) : undefined; |
| 381 | const camelToolName = typeof enrichedMetadata['toolName'] === 'string' ? (enrichedMetadata['toolName'] as string) : undefined; |
| 382 | if (!camelToolName && snakeToolName) { |
| 383 | enrichedMetadata['toolName'] = snakeToolName; |
| 384 | } |
| 385 | if (!snakeToolName && camelToolName) { |
| 386 | enrichedMetadata['tool_name'] = camelToolName; |
| 387 | } |
| 388 | |
| 389 | if (streamedToolHashes) { |
| 390 | streamedToolHashes.add(toolHash); |
| 391 | } |
| 392 | |
| 393 | if (!persist) { |
| 394 | const transientMetadata = { |
| 395 | ...enrichedMetadata, |
| 396 | isTransientToolMessage: true, |
| 397 | }; |
| 398 | const realtime = createRealtimeMessage({ |
| 399 | projectId, |
| 400 | role: 'tool', |
| 401 | messageType, |
| 402 | content: trimmedContent, |
| 403 | metadata: transientMetadata, |
| 404 | cliSource: 'codex', |
| 405 | requestId, |
| 406 | isStreaming, |
| 407 | isFinal: !isStreaming, |
no test coverage detected