fix(tools): carry the parked reply text in the WithOnPark callback#59
Merged
Conversation
The no-tool reply the model produces right before a park gate was only recorded inside the fragment; embedders had no way to read it at park time. nib worked around this by capturing the reasoning callback, but toolSelection fires that callback with resp.ReasoningContent (reasoning tokens, empty for most models), not the reply text — so parked replies (e.g. the answer to a mid-run injected user message) were silently dropped by the UI. WithOnPark now passes the assistant reply text that preceded the park: the no-tool reply at the agents-still-running gate, or "" at the sink-state gate where the reply is produced after the loop. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
mudler
added a commit
to mudler/nib
that referenced
this pull request
Jun 9, 2026
…w-ups (#31) Two bugs made a message queued during a backgrounded sub-agent look ignored: 1. Parked replies were invisible. The TUI surfaces a parked run's reply via OnParked, which captured cogito's reasoning callback as "the reply that preceded the park" — but that callback carries reasoning tokens (empty for most models), not the reply text. Any answer the model produced at a park boundary (e.g. to a follow-up injected while a detached agent kept the run alive) existed in the fragment but was never rendered. cogito's WithOnPark now passes the parked reply (mudler/cogito#59); the session forwards it and drops the lastReply hack. 2. End-of-run drain discarded unconsumed follow-ups. A queue entry released into the live run while its final LLM call was in flight was echoed to the transcript but never consumed — SendMessage's deferred channel drain silently threw it away. The session now tracks user-typed injections (InjectUser) and hands unconsumed ones back (TakeUndelivered); the TUI re-dispatches them as fresh turns ahead of the queue, without a second echo. System notices (shell-job completions, wake-ups) are still dropped by the drain so stale notices can't re-trigger finished work. Co-authored-by: Ettore Di Giacinto <mudler@localai.io> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The no-tool reply the model produces right before a park gate is recorded in the fragment, but embedders have no way to read it at park time:
WithOnParkfires with no arguments.nib worked around this by capturing the reasoning callback as "the reply that preceded the park" — but
toolSelectionfires that callback withresp.ReasoningContent(reasoning tokens, empty for most models), not the reply text, which travels separately asresults.message. The result: any answer the model produces at a park boundary (e.g. the reply to a user follow-up injected while a detached sub-agent keeps the run parked) exists in the fragment but is never surfaced — to the user it looks like the injected message was silently ignored.Fix
WithOnPark(func())→WithOnPark(func(reply string)):""(the reply is produced by the sink state after the loop, so there is no text yet).TestExecuteTools_OnParkOnResumeFirenow also asserts the parked reply is carried.This is a signature change to an option introduced in #49; updating the only known consumer (nib) alongside.
🤖 Generated with Claude Code