Welcome to the BriefTool project! In this first chapter, we are going to look at the most fundamental part of an AI assistant: how it speaks to you.
Imagine a busy restaurant.
In our system, the AI does a lot of "muttering" (running commands, checking files, debugging). If we showed all that raw text to the user as the main answer, it would be messy and confusing.
BriefTool (technically named SendUserMessage) is the Waiter. It is the specific tool the AI uses when it wants to formally address the user.
Imagine you ask the AI: "Check the server logs and tell me what went wrong."
grep, and analyzes text. You might see these as small loading spinners or debug text.server.log." This is the message that actually appears in your chat bubble.When the AI decides to speak, it doesn't just send a string of text. It sends a structured package. This ensures the message is formatted correctly and can carry extra items (like images or log files).
Here is the simplified structure of a message:
To send a message, the AI calls the tool SendUserMessage with specific inputs.
// Example Input: The AI sending a simple reply
{
"message": "I updated the config file for you.",
"status": "normal",
"attachments": []
}
If the AI needs to be more complex, it can include a file path:
// Example Input: Sending a reply with a file
{
"message": "Here is the screenshot you asked for.",
"status": "normal",
"attachments": ["/users/me/desktop/screenshot.png"]
}
Note: The
statusfield helps the system understand the context.normalmeans "I am replying to you."proactivemeans "I am interrupting you to tell you something new" (like a background task finishing).
When the AI calls SendUserMessage, it triggers a specific flow in our code. It's not just printing text; it's a pipeline that validates data and prepares the UI.
Here is what happens when the AI "speaks":
Let's look at the actual code definition for this tool (simplified). This is defined in BriefTool.ts.
First, we define what valid inputs look like using a library called zod. This tells the AI exactly what arguments it is allowed to use.
// From BriefTool.ts (Simplified)
const inputSchema = z.strictObject({
// The main text content
message: z.string().describe('The message for the user.'),
// Optional list of file paths
attachments: z.array(z.string()).optional(),
// Intent: replying (normal) or interrupting (proactive)
status: z.enum(['normal', 'proactive'])
})
Explanation: We force the AI to provide a message and a status. attachments are optional. If the AI tries to send a number instead of a string for the message, this schema will reject it.
When the tool is called, the call function runs.
// From BriefTool.ts (Simplified)
async call({ message, attachments, status }, context) {
// Capture the exact time the message was sent
const sentAt = new Date().toISOString()
// Log analytics event (invisible to user)
logEvent('tengu_brief_send', { proactive: status === 'proactive' })
// If we have files, prepare them (Simplified)
if (attachments && attachments.length > 0) {
// Logic to check files happens here
}
// Return the data so the UI can use it
return {
data: { message, sentAt }
}
}
Explanation:
sentAt).
You might notice we skipped over the heavy lifting of attachments in the code block above. Handling files is complex! We need to make sure the path is correct and the file isn't too big.
We will cover exactly how files are processed in Attachment Resolution Pipeline.
In this chapter, we learned:
message, attachments, and status.But simply sending the data isn't enough. We need to make it look good on the screen.
Next Step: How do we take this raw data and turn it into a beautiful, interactive chat bubble?
Next Chapter: Context-Aware UI Rendering
Generated by Code IQ