8.6 KiB
Context Output JS API
The Context object now provides Send, SendGroup, and Flush methods directly for sending messages to clients from JavaScript.
Usage
ctx.Send(message)
Send a single message to the client.
Parameters:
message: Can be a string (shorthand) or an object
String Shorthand:
// Automatically converts to a text message
ctx.Send("Hello World");
Object Format:
// Send text message
ctx.Send({
type: "text",
props: {
content: "Hello from JavaScript",
},
});
// Send loading message
ctx.Send({
type: "loading",
props: {
message: "Processing...",
},
});
// Send error message
ctx.Send({
type: "error",
props: {
message: "Something went wrong",
code: "ERR_500",
},
});
// Send custom message
ctx.Send({
type: "custom_widget",
props: {
data: { foo: "bar" },
},
});
Complete Message Object:
ctx.Send({
type: "text",
props: {
content: "Hello",
},
id: "msg_123", // Optional: message ID
delta: true, // Optional: incremental update
done: false, // Optional: whether complete
delta_path: "content", // Optional: update path
delta_action: "append", // Optional: update action (append, replace, merge, set)
group_id: "grp_1", // Optional: message group ID
metadata: {
// Optional: metadata
timestamp: Date.now(),
sequence: 1,
trace_id: "trace_123",
},
});
ctx.SendGroup(group)
Send a group of messages to the client.
Parameters:
group: Message group object
Example:
ctx.SendGroup({
id: "group_123",
messages: [
{
type: "text",
props: { content: "First message" },
},
{
type: "text",
props: { content: "Second message" },
},
],
metadata: {
timestamp: Date.now(),
},
});
ctx.Flush()
Flush the output buffer to ensure all messages are sent to the client.
Example:
ctx.Send("Processing...");
ctx.Flush(); // Send immediately
Complete Examples
Using in Hook Functions
/**
* Create hook - called before assistant processes
*/
function Create(input, options) {
const ctx = input.context;
// Send welcome message
ctx.Send("Welcome to AI Assistant!");
// Send loading indicator
ctx.Send({
type: "loading",
props: {
message: "Thinking...",
},
});
return { messages: input.messages };
}
/**
* Done hook - called after assistant completes
*/
function Done(input, output) {
const ctx = input.context;
// Send completion message
ctx.Send({
type: "text",
props: {
content: "Processing completed!",
},
});
// Flush output
ctx.Flush();
return {};
}
Streaming Response Example
function StreamingResponse(input) {
const ctx = input.context;
// Send initial message
ctx.Send({
type: "text",
props: { content: "Starting process" },
id: "msg_1",
delta: false,
});
// Send incremental updates
ctx.Send({
type: "text",
props: { content: "..." },
id: "msg_1",
delta: true,
delta_path: "content",
delta_action: "append",
});
// Send completion marker
ctx.Send({
type: "text",
props: { content: "" },
id: "msg_1",
delta: false,
done: true,
});
ctx.Flush();
}
Error Handling Example
function ProcessWithErrorHandling(input) {
const ctx = input.context;
try {
// Processing logic
ctx.Send("Processing...");
// Simulate error
throw new Error("Something went wrong");
} catch (error) {
// Send error message
ctx.Send({
type: "error",
props: {
message: error.message,
code: "ERR_PROCESSING",
},
});
ctx.Flush();
}
}
Multi-step Process Example
function MultiStepProcess(input) {
const ctx = input.context;
// Step 1
ctx.Send({
type: "loading",
props: { message: "Step 1: Analyzing input..." },
});
ctx.Flush();
// ... processing ...
// Step 2
ctx.Send({
type: "loading",
props: { message: "Step 2: Generating response..." },
});
ctx.Flush();
// ... processing ...
// Final result
ctx.Send({
type: "text",
props: { content: "Process completed successfully!" },
});
ctx.Flush();
}
Message Types
Built-in message types supported:
user_input- User input (display only)text- Text content (supports Markdown)thinking- Reasoning/thinking processloading- Loading indicatortool_call- Tool/function callerror- Error messageimage- Image contentaudio- Audio contentvideo- Video contentaction- System action (silent in OpenAI clients)event- Lifecycle event (CUI only)
Message Props by Type
Text Message
{
type: "text",
props: {
content: "Text content (supports Markdown)"
}
}
Thinking Message
{
type: "thinking",
props: {
content: "Reasoning process..."
}
}
Loading Message
{
type: "loading",
props: {
message: "Loading message..."
}
}
Tool Call Message
{
type: "tool_call",
props: {
id: "call_123",
name: "function_name",
arguments: '{"key": "value"}'
}
}
Error Message
{
type: "error",
props: {
message: "Error message",
code: "ERROR_CODE",
details: "Additional details"
}
}
Image Message
{
type: "image",
props: {
url: "https://example.com/image.jpg",
alt: "Image description",
width: 800,
height: 600
}
}
Audio Message
{
type: "audio",
props: {
url: "https://example.com/audio.mp3",
format: "mp3",
duration: 120.5,
transcript: "Audio transcript...",
autoplay: false,
controls: true
}
}
Video Message
{
type: "video",
props: {
url: "https://example.com/video.mp4",
format: "mp4",
thumbnail: "https://example.com/thumb.jpg",
width: 1920,
height: 1080,
autoplay: false,
controls: true
}
}
Delta Updates
Use delta updates for streaming scenarios:
// Initial message
ctx.Send({
type: "text",
props: { content: "Hello" },
id: "msg_1",
delta: false,
});
// Append to content
ctx.Send({
type: "text",
props: { content: " World" },
id: "msg_1",
delta: true,
delta_path: "content",
delta_action: "append",
});
// Mark as complete
ctx.Send({
type: "text",
props: {},
id: "msg_1",
done: true,
});
Delta Actions:
append- Append to string or arrayreplace- Replace valuemerge- Merge objectsset- Set new field
Notes
-
No Separate Output API Needed: The previous
const output = new Output(ctx)approach is deprecated. Now usectx.Send()methods directly. -
Automatic Client Handling: Messages are automatically converted to the appropriate format based on
ctx.accept:standard→ OpenAI formatcui-web/cui-native/cui-desktop→ CUI native format
-
Performance Optimization: Output objects are automatically cached and managed, no manual management needed.
-
Error Handling: All methods throw JavaScript exceptions on failure, which can be caught with try-catch.
-
Streaming Support: Use delta updates with unique message IDs for real-time streaming scenarios.
-
Metadata: Optional metadata can be attached to messages for tracking, debugging, or custom processing.
Migration Guide
Before (Deprecated):
// Old way - no longer needed
const output = new Output(ctx)
output.Send("Hello")
output.SendGroup({ id: "grp1", messages: [...] })
After (Current):
// New way - simpler and cleaner
ctx.Send("Hello")
ctx.SendGroup({ id: "grp1", messages: [...] })
ctx.Flush()
Best Practices
-
Use String Shorthand for Simple Messages:
ctx.Send("Hello")instead ofctx.Send({ type: "text", props: { content: "Hello" } }) -
Always Flush After Important Messages: Use
ctx.Flush()to ensure messages are sent immediately -
Use Unique IDs for Delta Updates: Assign unique IDs to messages that will receive incremental updates
-
Handle Errors Gracefully: Wrap Send operations in try-catch blocks for robust error handling
-
Use Loading Indicators: Show loading messages for long-running operations to improve UX
-
Group Related Messages: Use
SendGroupfor semantically related messages that should be displayed together