From 4b24b3337775a328c4de1c41f0ce0caad8c76bc9 Mon Sep 17 00:00:00 2001 From: Levi Neely <141506390+lneely@users.noreply.github.com> Date: Sun, 19 Jul 2026 21:25:58 +0200 Subject: [PATCH] streaming: tool as first-class streaming role in chat log --- session/session.go | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/session/session.go b/session/session.go index bdc55eb..10fd0a3 100644 --- a/session/session.go +++ b/session/session.go @@ -127,7 +127,8 @@ func (sess *Session) EnsureTrailingNewline() { // startEventLog subscribes to the agent's "event" bus topic and writes // all events to the session chat log. func (sess *Session) startEventLog() { - streamingRole := "" // tracks current streaming role ("assistant" or "reasoning") + streamingRole := "" // tracks current streaming role ("assistant", "reasoning", or "tool") + sess.Core.Bus().Subscribe("event", func(ev agent.Event) { switch ev.Role { case "assistant", "reasoning": @@ -144,6 +145,40 @@ func (sess *Session) startEventLog() { streamingRole = ev.Role } sess.AppendLog(FormatEvent(ev)) + + case "tool": + // Tool events can stream (multiple chunks with same name) or + // arrive as a single final result. Write header on first chunk, + // then append content. Duplicate final result is suppressed. + if streamingRole == "tool" { + // Continuation of streaming tool output. + if ev.Content != "" { + sess.AppendLog([]byte(ev.Content)) + } else { + // Empty content = final result marker; streaming is done. + sess.AppendLog([]byte("\n")) + streamingRole = "" + } + return + } + if streamingRole != "" { + sess.AppendLog([]byte("\n")) + streamingRole = "" + } + // First tool event — write header and start streaming. + sess.AppendLog([]byte("[tool:" + ev.Name + "]\n")) + if ev.Content != "" { + sess.AppendLog([]byte(ev.Content)) + } + streamingRole = "tool" + + case "call": + if streamingRole != "" { + sess.AppendLog([]byte("\n")) + streamingRole = "" + } + sess.AppendLog(FormatEvent(ev)) + default: if streamingRole != "" { sess.AppendLog([]byte("\n")) @@ -1190,8 +1225,6 @@ func FormatEvent(ev agent.Event) []byte { return []byte("[stalled]\n") case "maxsteps": return []byte("[maxsteps]\n" + ev.Content + "\n") - case "exec": - return []byte("[exec]\n" + ev.Content + "\n") default: return nil }