Download src/remote/sdkMessageAdapter.ts from chenbhao/codev: direct link, hf CLI and curl.
- Browser
- Download file 9.06 kB
-
https://huggingface.co/chenbhao/codev/resolve/main/src/remote/sdkMessageAdapter.ts
- Command line
-
hf download hf://chenbhao/codev/src/remote/sdkMessageAdapter.ts
-
curl -L -o sdkMessageAdapter.ts https://huggingface.co/chenbhao/codev/resolve/main/src/remote/sdkMessageAdapter.ts
9.06 kB
| import type { | |
| SDKAssistantMessage, | |
| SDKCompactBoundaryMessage, | |
| SDKMessage, | |
| SDKPartialAssistantMessage, | |
| SDKResultMessage, | |
| SDKStatusMessage, | |
| SDKSystemMessage, | |
| SDKToolProgressMessage, | |
| } from '../entrypoints/agentSdkTypes.js' | |
| import type { | |
| AssistantMessage, | |
| Message, | |
| StreamEvent, | |
| SystemMessage, | |
| } from '../types/message.js' | |
| import { logForDebugging } from '../utils/debug.js' | |
| import { fromSDKCompactMetadata } from '../utils/messages/mappers.js' | |
| import { createUserMessage } from '../utils/messages.js' | |
| /** | |
| * Converts SDKMessage from CCR to REPL Message types. | |
| * | |
| * The CCR backend sends SDK-format messages via WebSocket. The REPL expects | |
| * internal Message types for rendering. This adapter bridges the two. | |
| */ | |
| /** | |
| * Convert an SDKAssistantMessage to an AssistantMessage | |
| */ | |
| function convertAssistantMessage(msg: SDKAssistantMessage): AssistantMessage { | |
| return { | |
| type: 'assistant', | |
| message: msg.message, | |
| uuid: msg.uuid, | |
| requestId: undefined, | |
| timestamp: new Date().toISOString(), | |
| error: msg.error, | |
| } | |
| } | |
| /** | |
| * Convert an SDKPartialAssistantMessage (streaming) to a StreamEvent | |
| */ | |
| function convertStreamEvent(msg: SDKPartialAssistantMessage): StreamEvent { | |
| return { | |
| type: 'stream_event', | |
| event: msg.event, | |
| } | |
| } | |
| /** | |
| * Convert an SDKResultMessage to a SystemMessage | |
| */ | |
| function convertResultMessage(msg: SDKResultMessage): SystemMessage { | |
| const isError = msg.subtype !== 'success' | |
| const content = isError | |
| ? msg.errors?.join(', ') || 'Unknown error' | |
| : 'Session completed successfully' | |
| return { | |
| type: 'system', | |
| subtype: 'informational', | |
| content, | |
| level: isError ? 'warning' : 'info', | |
| uuid: msg.uuid, | |
| timestamp: new Date().toISOString(), | |
| } | |
| } | |
| /** | |
| * Convert an SDKSystemMessage (init) to a SystemMessage | |
| */ | |
| function convertInitMessage(msg: SDKSystemMessage): SystemMessage { | |
| return { | |
| type: 'system', | |
| subtype: 'informational', | |
| content: `Remote session initialized (model: ${msg.model})`, | |
| level: 'info', | |
| uuid: msg.uuid, | |
| timestamp: new Date().toISOString(), | |
| } | |
| } | |
| /** | |
| * Convert an SDKStatusMessage to a SystemMessage | |
| */ | |
| function convertStatusMessage(msg: SDKStatusMessage): SystemMessage | null { | |
| if (!msg.status) { | |
| return null | |
| } | |
| return { | |
| type: 'system', | |
| subtype: 'informational', | |
| content: | |
| msg.status === 'compacting' | |
| ? 'Compacting conversation…' | |
| : `Status: ${msg.status}`, | |
| level: 'info', | |
| uuid: msg.uuid, | |
| timestamp: new Date().toISOString(), | |
| } | |
| } | |
| /** | |
| * Convert an SDKToolProgressMessage to a SystemMessage. | |
| * We use a system message instead of ProgressMessage since the Progress type | |
| * is a complex union that requires tool-specific data we don't have from CCR. | |
| */ | |
| function convertToolProgressMessage( | |
| msg: SDKToolProgressMessage, | |
| ): SystemMessage { | |
| return { | |
| type: 'system', | |
| subtype: 'informational', | |
| content: `Tool ${msg.tool_name} running for ${msg.elapsed_time_seconds}s…`, | |
| level: 'info', | |
| uuid: msg.uuid, | |
| timestamp: new Date().toISOString(), | |
| toolUseID: msg.tool_use_id, | |
| } | |
| } | |
| /** | |
| * Convert an SDKCompactBoundaryMessage to a SystemMessage | |
| */ | |
| function convertCompactBoundaryMessage( | |
| msg: SDKCompactBoundaryMessage, | |
| ): SystemMessage { | |
| return { | |
| type: 'system', | |
| subtype: 'compact_boundary', | |
| content: 'Conversation compacted', | |
| level: 'info', | |
| uuid: msg.uuid, | |
| timestamp: new Date().toISOString(), | |
| compactMetadata: fromSDKCompactMetadata(msg.compact_metadata), | |
| } | |
| } | |
| /** | |
| * Result of converting an SDKMessage | |
| */ | |
| export type ConvertedMessage = | |
| | { type: 'message'; message: Message } | |
| | { type: 'stream_event'; event: StreamEvent } | |
| | { type: 'ignored' } | |
| type ConvertOptions = { | |
| /** Convert user messages containing tool_result content blocks into UserMessages. | |
| * Used by direct connect mode where tool results come from the remote server | |
| * and need to be rendered locally. CCR mode ignores user messages since they | |
| * are handled differently. */ | |
| convertToolResults?: boolean | |
| /** | |
| * Convert user text messages into UserMessages for display. Used when | |
| * converting historical events where user-typed messages need to be shown. | |
| * In live WS mode these are already added locally by the REPL so they're | |
| * ignored by default. | |
| */ | |
| convertUserTextMessages?: boolean | |
| } | |
| /** | |
| * Convert an SDKMessage to REPL message format | |
| */ | |
| export function convertSDKMessage( | |
| msg: SDKMessage, | |
| opts?: ConvertOptions, | |
| ): ConvertedMessage { | |
| switch (msg.type) { | |
| case 'assistant': | |
| return { type: 'message', message: convertAssistantMessage(msg) } | |
| case 'user': { | |
| const content = msg.message?.content | |
| // Tool result messages from the remote server need to be converted so | |
| // they render and collapse like local tool results. Detect via content | |
| // shape (tool_result blocks) — parent_tool_use_id is NOT reliable: the | |
| // agent-side normalizeMessage() hardcodes it to null for top-level | |
| // tool results, so it can't distinguish tool results from prompt echoes. | |
| const isToolResult = | |
| Array.isArray(content) && content.some(b => b.type === 'tool_result') | |
| if (opts?.convertToolResults && isToolResult) { | |
| return { | |
| type: 'message', | |
| message: createUserMessage({ | |
| content, | |
| toolUseResult: msg.tool_use_result, | |
| uuid: msg.uuid, | |
| timestamp: msg.timestamp, | |
| }), | |
| } | |
| } | |
| // When converting historical events, user-typed messages need to be | |
| // rendered (they weren't added locally by the REPL). Skip tool_results | |
| // here — already handled above. | |
| if (opts?.convertUserTextMessages && !isToolResult) { | |
| if (typeof content === 'string' || Array.isArray(content)) { | |
| return { | |
| type: 'message', | |
| message: createUserMessage({ | |
| content, | |
| toolUseResult: msg.tool_use_result, | |
| uuid: msg.uuid, | |
| timestamp: msg.timestamp, | |
| }), | |
| } | |
| } | |
| } | |
| // User-typed messages (string content) are already added locally by REPL. | |
| // In CCR mode, all user messages are ignored (tool results handled differently). | |
| return { type: 'ignored' } | |
| } | |
| case 'stream_event': | |
| return { type: 'stream_event', event: convertStreamEvent(msg) } | |
| case 'result': | |
| // Only show result messages for errors. Success results are noise | |
| // in multi-turn sessions (isLoading=false is sufficient signal). | |
| if (msg.subtype !== 'success') { | |
| return { type: 'message', message: convertResultMessage(msg) } | |
| } | |
| return { type: 'ignored' } | |
| case 'system': | |
| if (msg.subtype === 'init') { | |
| return { type: 'message', message: convertInitMessage(msg) } | |
| } | |
| if (msg.subtype === 'status') { | |
| const statusMsg = convertStatusMessage(msg) | |
| return statusMsg | |
| ? { type: 'message', message: statusMsg } | |
| : { type: 'ignored' } | |
| } | |
| if (msg.subtype === 'compact_boundary') { | |
| return { | |
| type: 'message', | |
| message: convertCompactBoundaryMessage(msg), | |
| } | |
| } | |
| // hook_response and other subtypes | |
| logForDebugging( | |
| `[sdkMessageAdapter] Ignoring system message subtype: ${msg.subtype}`, | |
| ) | |
| return { type: 'ignored' } | |
| case 'tool_progress': | |
| return { type: 'message', message: convertToolProgressMessage(msg) } | |
| case 'auth_status': | |
| // Auth status is handled separately, not converted to a display message | |
| logForDebugging('[sdkMessageAdapter] Ignoring auth_status message') | |
| return { type: 'ignored' } | |
| case 'tool_use_summary': | |
| // Tool use summaries are SDK-only events, not displayed in REPL | |
| logForDebugging('[sdkMessageAdapter] Ignoring tool_use_summary message') | |
| return { type: 'ignored' } | |
| case 'rate_limit_event': | |
| // Rate limit events are SDK-only events, not displayed in REPL | |
| logForDebugging('[sdkMessageAdapter] Ignoring rate_limit_event message') | |
| return { type: 'ignored' } | |
| default: { | |
| // Gracefully ignore unknown message types. The backend may send new | |
| // types before the client is updated; logging helps with debugging | |
| // without crashing or losing the session. | |
| logForDebugging( | |
| `[sdkMessageAdapter] Unknown message type: ${(msg as { type: string }).type}`, | |
| ) | |
| return { type: 'ignored' } | |
| } | |
| } | |
| } | |
| /** | |
| * Check if an SDKMessage indicates the session has ended | |
| */ | |
| export function isSessionEndMessage(msg: SDKMessage): boolean { | |
| return msg.type === 'result' | |
| } | |
| /** | |
| * Check if an SDKResultMessage indicates success | |
| */ | |
| export function isSuccessResult(msg: SDKResultMessage): boolean { | |
| return msg.subtype === 'success' | |
| } | |
| /** | |
| * Extract the result text from a successful SDKResultMessage | |
| */ | |
| export function getResultText(msg: SDKResultMessage): string | null { | |
| if (msg.subtype === 'success') { | |
| return msg.result | |
| } | |
| return null | |
| } | |