fix: ensure chat log lines are newline-terminated

assistant events are streamed without trailing newlines, causing
line-buffered 9P reads to lag one turn behind. Close the assistant
block with \n on role transitions and after Submit returns. Set
ChatOffset at EOF before the user event so offset spans the full
exchange. Remove 500-char truncation of tool call args in FormatEvent.
This commit is contained in:
Levi Neely 2026-04-22 16:34:06 +02:00
parent f220d0ee96
commit 81fedb862c
3 changed files with 43 additions and 15 deletions

View File

@ -373,13 +373,24 @@ func (s *BatchStore) executeJob(ctx context.Context, job *batchJob) (string, err
assistantStarted = true
}
case "user", "call", "tool":
assistantStarted = false
if assistantStarted {
job.appendLog([]byte("\n"))
assistantStarted = false
}
case "error":
errBuf.WriteString(ev.Content)
}
job.appendLog(FormatEvent(ev))
})
// Ensure log ends with newline (assistant content has no trailing \n).
job.mu.Lock()
if len(job.log) > 0 && job.log[len(job.log)-1] != '\n' {
job.log = append(job.log, '\n')
job.logVers++
}
job.mu.Unlock()
usage := core.Usage()
ctxsz := core.CtxSz()
@ -620,9 +631,6 @@ func FormatEvent(ev agent.Event) []byte {
return []byte(ev.Content)
case "call":
args := squashWhitespace(ev.Content)
if len(args) > 500 {
args = args[:500] + "..."
}
return []byte("-> " + ev.Name + "(" + args + ")\n")
case "tool":
return []byte(strings.TrimRight(ev.Content, "\n") + "\n")

View File

@ -52,6 +52,16 @@ func (sess *Session) AppendLog(data []byte) {
sess.mu.Unlock()
}
// EnsureTrailingNewline appends a newline if the log doesn't already end with one.
func (sess *Session) EnsureTrailingNewline() {
sess.mu.Lock()
if len(sess.log) > 0 && sess.log[len(sess.log)-1] != '\n' {
sess.log = append(sess.log, '\n')
sess.logVers++
}
sess.mu.Unlock()
}
// LogInfo returns the current log length and version atomically.
func (sess *Session) LogInfo() (length int, vers uint32) {
sess.mu.RLock()

View File

@ -159,7 +159,9 @@ func (s *SessionFileStore) writeFile(name string, data []byte) error {
case "chat":
return s.saveTranscript([]byte(input))
case "prompt":
s.sess.Core.Submit(s.sess.Ctx, input, s.makePublish())
pub := s.makePublish()
s.sess.Core.Submit(s.sess.Ctx, input, pub)
s.sess.EnsureTrailingNewline()
case "fifo.in":
s.sess.Core.Queue(input)
case "ctl":
@ -352,21 +354,29 @@ func (s *SessionFileStore) currentWaitValue(name string) string {
func (s *SessionFileStore) makePublish() func(agent.Event) {
assistantStarted := false
return func(ev agent.Event) {
switch ev.Role {
case "user", "call", "tool":
assistantStarted = false
case "assistant":
if !assistantStarted {
s.sess.AppendLog([]byte("assistant: "))
assistantStarted = true
}
}
s.sess.AppendLog(FormatEvent(ev))
if ev.Role == "user" {
if assistantStarted {
s.sess.AppendLog([]byte("\n"))
assistantStarted = false
}
s.sess.mu.Lock()
s.sess.ChatOffset = len(s.sess.log)
s.sess.mu.Unlock()
} else {
switch ev.Role {
case "call", "tool":
if assistantStarted {
s.sess.AppendLog([]byte("\n"))
assistantStarted = false
}
case "assistant":
if !assistantStarted {
s.sess.AppendLog([]byte("assistant: "))
assistantStarted = true
}
}
}
s.sess.AppendLog(FormatEvent(ev))
}
}