π File detail
utils/processUserInput/processTextPrompt.ts
π― Use case
This file lives under βutils/β, which covers cross-cutting helpers (shell, tempfiles, settings, messages, process input, β¦). On the API surface it exposes processTextPrompt β mainly functions, hooks, or classes. Dependencies touch @anthropic-ai, crypto, and src. It composes internal code from services, types, messages, telemetry, and userPromptKeywords (relative imports).
Generated from folder role, exports, dependency roots, and inline comments β not hand-reviewed for every path.
π§ Inline summary
import type { ContentBlockParam } from '@anthropic-ai/sdk/resources' import { randomUUID } from 'crypto' import { setPromptId } from 'src/bootstrap/state.js' import type { AttachmentMessage,
π€ Exports (heuristic)
processTextPrompt
π External import roots
Package roots from from "β¦" (relative paths omitted).
@anthropic-aicryptosrc
π₯οΈ Source preview
import type { ContentBlockParam } from '@anthropic-ai/sdk/resources'
import { randomUUID } from 'crypto'
import { setPromptId } from 'src/bootstrap/state.js'
import type {
AttachmentMessage,
SystemMessage,
UserMessage,
} from 'src/types/message.js'
import { logEvent } from '../../services/analytics/index.js'
import type { PermissionMode } from '../../types/permissions.js'
import { createUserMessage } from '../messages.js'
import { logOTelEvent, redactIfDisabled } from '../telemetry/events.js'
import { startInteractionSpan } from '../telemetry/sessionTracing.js'
import {
matchesKeepGoingKeyword,
matchesNegativeKeyword,
} from '../userPromptKeywords.js'
export function processTextPrompt(
input: string | Array<ContentBlockParam>,
imageContentBlocks: ContentBlockParam[],
imagePasteIds: number[],
attachmentMessages: AttachmentMessage[],
uuid?: string,
permissionMode?: PermissionMode,
isMeta?: boolean,
): {
messages: (UserMessage | AttachmentMessage | SystemMessage)[]
shouldQuery: boolean
} {
const promptId = randomUUID()
setPromptId(promptId)
const userPromptText =
typeof input === 'string'
? input
: input.find(block => block.type === 'text')?.text || ''
startInteractionSpan(userPromptText)
// Emit user_prompt OTEL event for both string (CLI) and array (SDK/VS Code)
// input shapes. Previously gated on `typeof input === 'string'`, so VS Code
// sessions never emitted user_prompt (anthropics/claude-code#33301).
// For array input, use the LAST text block: createUserContent pushes the
// user's message last (after any <ide_selection>/attachment context blocks),
// so .findLast gets the actual prompt. userPromptText (first block) is kept
// unchanged for startInteractionSpan to preserve existing span attributes.
const otelPromptText =
typeof input === 'string'
? input
: input.findLast(block => block.type === 'text')?.text || ''
if (otelPromptText) {
void logOTelEvent('user_prompt', {
prompt_length: String(otelPromptText.length),
prompt: redactIfDisabled(otelPromptText),
'prompt.id': promptId,
})
}
const isNegative = matchesNegativeKeyword(userPromptText)
const isKeepGoing = matchesKeepGoingKeyword(userPromptText)
logEvent('tengu_input_prompt', {
is_negative: isNegative,
is_keep_going: isKeepGoing,
})
// If we have pasted images, create a message with image content
if (imageContentBlocks.length > 0) {
// Build content: text first, then images below
const textContent =
typeof input === 'string'
? input.trim()
? [{ type: 'text' as const, text: input }]
: []
: input
const userMessage = createUserMessage({
content: [...textContent, ...imageContentBlocks],
uuid: uuid,
imagePasteIds: imagePasteIds.length > 0 ? imagePasteIds : undefined,
permissionMode,
isMeta: isMeta || undefined,
})
return {
messages: [userMessage, ...attachmentMessages],
shouldQuery: true,
}
}
const userMessage = createUserMessage({
content: input,
uuid,
permissionMode,
isMeta: isMeta || undefined,
})
return {
messages: [userMessage, ...attachmentMessages],
shouldQuery: true,
}
}