In the previous chapter, System Initialization Handshake, we established a connection and introduced the system to the client. Now that the "Pilot" has made the announcement, the passengers (Users) and the Crew (Assistant) start talking.
However, the way our backend "thinks" isn't exactly how the User Interface "speaks."
Imagine a United Nations meeting.
If the Internal System spoke directly to the Client, misunderstandings (or crashes) would happen immediately.
The SDK Message Translation Layer acts as the Diplomatic Translator. It stands in the middle, rewriting messages so both sides understand each other perfectly without leaking sensitive details.
The core functionality resides in src/mappers.ts. The most common operation is sending a message out to the user.
toSDKMessages)Let's say the user just typed "Hello". Internally, we store this simply. But before sending it back to the UI to confirm receipt, we need to dress it up.
import { toSDKMessages } from './mappers';
import { getSessionId } from 'src/bootstrap/state';
// 1. A raw internal message
const internalMsg = [{
type: 'user',
message: 'Hello World',
uuid: 'abc-123',
timestamp: '2023-10-27T10:00:00Z'
}];
// 2. Translate it
const externalMsgs = toSDKMessages(internalMsg);
The translator adds context, like the session_id, which the internal message didn't even know about, but the UI absolutely needs.
[
{
"type": "user",
"message": "Hello World",
"uuid": "abc-123",
"timestamp": "2023-10-27T10:00:00Z",
"session_id": "session_xyz_789",
"parent_tool_use_id": null
}
]
The translator ensured the message is now valid for the specific API protocol used by the mobile app.
How does the translator decide what to keep, what to change, and what to hide?
The translator looks at the type of the message (user, assistant, or system) and routes it to the correct formatting logic.
Let's look at src/mappers.ts to see how this routing works.
The function toSDKMessages is a loop that transforms every message in the list.
// src/mappers.ts
export function toSDKMessages(messages: Message[]): SDKMessage[] {
return messages.flatMap((message): SDKMessage[] => {
switch (message.type) {
case 'assistant':
// Handle AI responses
return [ /* ... formatted assistant msg ... */ ]
case 'user':
// Handle User inputs
return [ /* ... formatted user msg ... */ ]
case 'system':
// Handle special system events (more on this below)
return handleSystemMessage(message)
default:
return [] // Unknown types are dropped!
}
})
}
Note: We use flatMap. This allows one internal message to potentially turn into zero SDK messages (if hidden) or multiple SDK messages.
When mapping a User message, we determine if it's "Synthetic". A synthetic message is one that exists in the system but wasn't typed by a human (like a programmatic trigger).
// src/mappers.ts (Inside the switch case 'user')
return [{
type: 'user',
message: message.message,
session_id: getSessionId(), // Inject global session ID
uuid: message.uuid,
// If it's a meta message, mark it as synthetic for the UI
isSynthetic: message.isMeta || message.isVisibleInTranscriptOnly,
// Attach tool results if they exist
...(message.toolUseResult ? { tool_use_result: message.toolUseResult } : {}),
}]
The translator ensures that the UI knows if a message is "real" or "synthetic" so it can render it differently (e.g., greyed out).
Sometimes, the internal system runs a local command (like /cost to check API usage). The output comes from the System, usually with ugly color codes (ANSI) for the terminal.
Mobile apps don't understand ANSI colors or "System" messages. So, our translator performs a trick: It disguises the System message as an Assistant message.
// src/mappers.ts
export function localCommandOutputToSDKAssistantMessage(
rawContent: string,
uuid: UUID,
): SDKAssistantMessage {
// 1. Remove ugly terminal colors
const cleanContent = stripAnsi(rawContent).trim()
// 2. Wrap it as if the AI Assistant said it
const synthetic = createAssistantMessage({ content: cleanContent })
return {
type: 'assistant', // <-- The disguise!
message: synthetic.message,
session_id: getSessionId(),
uuid,
}
}
This is the "Diplomatic" part. The translator knows the Mobile App (the foreign delegate) doesn't have a protocol for "Local Command Output," so it translates it into "Assistant Speech" so the app can display it gracefully.
The SDK Message Translation Layer is the bridge that keeps the internal system decoupled from external clients.
However, translating Assistant messages is particularly complex. Sometimes the Assistant wants to use tools, but the data format for tools changes between versions. We need a specialized way to handle that.
Next Chapter: Assistant Message Normalization
Generated by Code IQ