Summary
Kit's public EventBus currently surfaces only a subset of the streaming callbacks that Fantasy's AgentStreamCall provides. Several useful Fantasy callbacks are either silently swallowed inside internal/agent/agent.go or never wired up at all. This makes it impossible for SDK consumers to build responsive UIs for certain scenarios — most notably, showing a tool call block the moment the LLM starts generating tool arguments, rather than waiting for the full argument JSON to finish streaming.
Motivation / Use Case
I'm building a web UI on top of Kit's public pkg/kit API with SSE streaming. For tools whose arguments are large (e.g. create_artifact with a full HTML document as content, or write with a large file body), the user sees nothing in the chat for several seconds while the LLM streams the tool arguments. The ToolCallEvent only fires after the arguments are fully parsed — by which time 5-10+ seconds may have passed in silence.
Fantasy already fires OnToolInputStart(id, toolName) as soon as the tool name is known, which would let me show a "running" block immediately. But Kit never passes this through.
The same gap exists for several other Fantasy callbacks that would be useful for richer UI feedback.
Fantasy callbacks vs Kit events — full mapping
✅ Currently mirrored (Kit surfaces these)
| Fantasy Callback |
Kit Event |
Notes |
OnReasoningDelta(id, delta) |
ReasoningDeltaEvent |
✅ |
OnReasoningEnd(id, content) |
ReasoningCompleteEvent |
✅ |
OnTextDelta(id, text) |
MessageUpdateEvent |
✅ (via onStreamingResponse) |
OnToolCall(tc) |
ToolCallEvent |
✅ (fires after args are fully parsed) |
OnToolResult(tr) |
ToolResultEvent |
✅ |
OnStepFinish(step) |
StepUsageEvent + ToolCallContentEvent |
✅ (partially — usage + text-alongside-tools extracted) |
❌ Not mirrored (Fantasy provides but Kit drops)
| Fantasy Callback |
Suggested Kit Event |
Use Case |
OnToolInputStart(id, toolName) |
ToolCallStartEvent |
High priority. Show a "running" tool block immediately when the LLM begins generating tool call arguments, before the full JSON is streamed. The id is the tool call ID and toolName is the tool being invoked. This is the minimum needed to eliminate the "dead air" gap for tools with large arguments. |
OnToolInputDelta(id, delta) |
ToolCallDeltaEvent |
Stream tool argument fragments in real-time. Useful for live-previewing artifact content as it's generated, or showing a progress indicator with byte count. |
OnToolInputEnd(id) |
ToolCallEndEvent |
Signals that tool argument streaming is complete (before execution begins). Could be used to transition a UI block from "generating args" to "executing". |
OnTextStart(id) |
TextStartEvent |
Know when a new text content block begins. Useful for creating DOM containers before chunks arrive. |
OnTextEnd(id) |
TextEndEvent |
Know when a text content block is finalized. Currently Kit infers this from MessageEndEvent but that fires per-message, not per-content-block. |
OnReasoningStart(id, content) |
ReasoningStartEvent |
Know when reasoning begins (Kit only has delta + complete). |
OnSource(source) |
SourceEvent |
Surface source/citation references from providers that support them (e.g. web search grounding). |
OnWarnings(warnings) |
WarningsEvent |
Surface provider warnings (e.g. content filter triggers, deprecated features). |
OnStreamFinish(usage, finishReason, metadata) |
StreamFinishEvent |
Per-stream (per-step) finish with granular usage/metadata. Currently Kit only exposes this partially through StepUsageEvent. |
OnAgentStart() |
(maybe TurnStartEvent is close enough) |
Low priority — Kit's TurnStartEvent likely covers this. |
OnAgentFinish(result) |
(maybe TurnEndEvent is close enough) |
Low priority — Kit's TurnEndEvent likely covers this. |
OnStepStart(stepNumber) |
StepStartEvent |
Know when a new agentic step begins (useful for step counters in UI). |
OnChunk(StreamPart) |
(raw catch-all) |
Low priority — most consumers would use typed events instead. |
OnError(err) |
(partially via TurnEndEvent.Error) |
Low priority. |
OnFinish(result) |
(same as OnAgentFinish) |
Low priority. |
Proposed Changes
Minimum viable (high impact, small diff)
Add three new event types and wire them through internal/agent/agent.go:
// events.go — new types
type ToolCallStartEvent struct {
ToolCallID string
ToolName string
ToolKind string
}
type ToolCallDeltaEvent struct {
ToolCallID string
Delta string // JSON fragment of tool arguments
}
type ToolCallEndEvent struct {
ToolCallID string
}
// internal/agent/agent.go — wire into AgentStreamCall
OnToolInputStart: func(id, toolName string) error {
if onToolCallStart != nil {
onToolCallStart(id, toolName)
}
return nil
},
OnToolInputDelta: func(id, delta string) error {
if onToolCallDelta != nil {
onToolCallDelta(id, delta)
}
return nil
},
OnToolInputEnd: func(id string) error {
if onToolCallEnd != nil {
onToolCallEnd(id)
}
return nil
},
Full parity (longer term)
Mirror all remaining Fantasy callbacks as Kit events so SDK consumers have full observability without needing to fork Kit or drop down to Fantasy directly.
Impact
- No breaking changes — purely additive new event types
- Small diff — the plumbing from Fantasy → Kit agent → EventBus is mechanical
- Unblocks real-time tool call UIs, live artifact preview, source citations, and provider warning surfacing for all SDK consumers
Environment
- Kit: v0.56.1
- Fantasy: v0.17.2
Summary
Kit's public
EventBuscurrently surfaces only a subset of the streaming callbacks that Fantasy'sAgentStreamCallprovides. Several useful Fantasy callbacks are either silently swallowed insideinternal/agent/agent.goor never wired up at all. This makes it impossible for SDK consumers to build responsive UIs for certain scenarios — most notably, showing a tool call block the moment the LLM starts generating tool arguments, rather than waiting for the full argument JSON to finish streaming.Motivation / Use Case
I'm building a web UI on top of Kit's public
pkg/kitAPI with SSE streaming. For tools whose arguments are large (e.g.create_artifactwith a full HTML document ascontent, orwritewith a large file body), the user sees nothing in the chat for several seconds while the LLM streams the tool arguments. TheToolCallEventonly fires after the arguments are fully parsed — by which time 5-10+ seconds may have passed in silence.Fantasy already fires
OnToolInputStart(id, toolName)as soon as the tool name is known, which would let me show a "running" block immediately. But Kit never passes this through.The same gap exists for several other Fantasy callbacks that would be useful for richer UI feedback.
Fantasy callbacks vs Kit events — full mapping
✅ Currently mirrored (Kit surfaces these)
OnReasoningDelta(id, delta)ReasoningDeltaEventOnReasoningEnd(id, content)ReasoningCompleteEventOnTextDelta(id, text)MessageUpdateEventonStreamingResponse)OnToolCall(tc)ToolCallEventOnToolResult(tr)ToolResultEventOnStepFinish(step)StepUsageEvent+ToolCallContentEvent❌ Not mirrored (Fantasy provides but Kit drops)
OnToolInputStart(id, toolName)ToolCallStartEventidis the tool call ID andtoolNameis the tool being invoked. This is the minimum needed to eliminate the "dead air" gap for tools with large arguments.OnToolInputDelta(id, delta)ToolCallDeltaEventOnToolInputEnd(id)ToolCallEndEventOnTextStart(id)TextStartEventOnTextEnd(id)TextEndEventMessageEndEventbut that fires per-message, not per-content-block.OnReasoningStart(id, content)ReasoningStartEventOnSource(source)SourceEventOnWarnings(warnings)WarningsEventOnStreamFinish(usage, finishReason, metadata)StreamFinishEventStepUsageEvent.OnAgentStart()TurnStartEventis close enough)TurnStartEventlikely covers this.OnAgentFinish(result)TurnEndEventis close enough)TurnEndEventlikely covers this.OnStepStart(stepNumber)StepStartEventOnChunk(StreamPart)OnError(err)TurnEndEvent.Error)OnFinish(result)OnAgentFinish)Proposed Changes
Minimum viable (high impact, small diff)
Add three new event types and wire them through
internal/agent/agent.go:Full parity (longer term)
Mirror all remaining Fantasy callbacks as Kit events so SDK consumers have full observability without needing to fork Kit or drop down to Fantasy directly.
Impact
Environment