Streaming
There are two complementary APIs:
- Callback-style — pass
onAssistantDelta(orOnAssistantDeltain Go) to a regularrunAgent/Sendcall. The promise/result still resolves with the final text. - Iterator-style — call
streamAgent/Session.Streamto get an async iterator (or channel in Go) of every event.
TypeScript
Section titled “TypeScript”// Callbackawait client.runAgent({ systemPrompt: "...", prompt: "Tell me a story.", onAssistantDelta: (delta) => process.stdout.write(delta),});
// Iteratorfor await (const event of client.streamAgent({ systemPrompt: "...", prompt: "Tell me a story.",})) { if (event.type === "assistant_delta") process.stdout.write(event.text); if (event.type === "result") process.stdout.write("\n");}Python
Section titled “Python”# Callbackclient.run_agent( system_prompt="...", prompt="Tell me a story.", on_assistant_delta=lambda d: print(d, end="", flush=True),)
# Iteratorfor event in client.stream_agent(system_prompt="...", prompt="Tell me a story."): if event.type == "assistant_delta": print(event.text, end="", flush=True)
# Async iteratorasync for event in async_client.stream_agent(system_prompt="...", prompt="..."): ...// Callback_, _ = client.RunAgent(ctx, mantyx.RunSpec{ SystemPrompt: "...", Prompt: "Tell me a story.", OnAssistantDelta: func(s string) { fmt.Print(s) },})
// Channelch, err := client.StreamAgent(ctx, mantyx.RunSpec{ SystemPrompt: "...", Prompt: "Tell me a story.",})if err != nil { log.Fatal(err) }for ev := range ch { if ev.Type == "assistant_delta" { if t, ok := ev.Data["text"].(string); ok { fmt.Print(t) } }}What events to expect
Section titled “What events to expect”| Event | When | Payload |
|---|---|---|
assistant_delta |
Streaming assistant tokens | { text } |
thinking_delta |
Reasoning models with reasoningLevel > 0 emit chain-of-thought tokens |
{ text } |
assistant_message |
Full assistant turn (text + tool calls) | { text, toolCalls } |
tool_call / tool_result |
Server-side tool execution | { toolUseId, name, ... } |
local_tool_call |
The SDK should run a handler | { toolUseId, name, args, kind?, ... } |
local_tool_result_in |
Echo of the SDK’s result | { toolUseId, output } |
loop_detected |
The loop-detection guard intervened | { consecutiveCount, hardCutoff, tools } |
tool_budget_exceeded |
A tool call hit its toolBudgets cap |
{ tool, maxCalls, callIndex } |
supervisor |
The run supervisor reviewed the transcript | { action, reason, redirect?, llmCalls, phase? } |
task_plan |
The task plan checklist was emitted or advanced | { brief?, steps: [{ title, status }] } |
result |
Terminal | { subtype, text?, error?, plan? } |
cancelled |
Terminal (after cancelRun) |
{} |
The local_tool_call payload carries a kind discriminator ("local" when omitted, "a2a_local", or "mcp_local") plus extra metadata for the specialised kinds. The SDKs route to the right handler automatically; you only need to care about this if you’re implementing a third-party client. The full event vocabulary is documented in Wire protocol §7.
loop_detected, tool_budget_exceeded, supervisor, and task_plan are observability-only — the synthetic skip / steering nudge / “budget exceeded” tool result (and supervisor verdicts when action is redirect or finalize) already ride the normal channels, so the run keeps progressing without any SDK action. Surface them as status banners or telemetry; see Run guards for tuning the thresholds, budgets, and supervisor interval, and Task planning for task_plan checklist rendering.
The SDKs all reconnect automatically via Last-Event-ID + ?lastSeq= if the SSE stream drops.
