Join every turn-final answer instead of only the last (Group A)

Pi drains queued follow-ups and steers inside the same run with no
agent_end/agent_start between them, so when a scheduled job's follow-up (or
a user steer) merges into a run, the model can answer twice before
agent_settled fires once. AgentHost.settle() used only the last assistant
message as finalText, so the first answer was lost (it only ever appeared
as progress-message narration, which gets deleted).

RunRecord now tracks `finals`: assistant texts whose stopReason is not
toolUse (and, as before, not error/aborted), collected alongside `texts`.
settle() joins `finals` with blank lines, falling back to `texts` and then
to the placeholder, matching the documented fallback chain.

Claude-Session: https://claude.ai/code/session_01JtuZTCMW9TyogEmBgGf8JT
This commit is contained in:
EugeneTes
2026-09-06 18:06:46 +02:00
parent d8561748a7
commit d2a0ec2cf1
3 changed files with 49 additions and 11 deletions

View File

@@ -127,10 +127,11 @@ Mapping from Pi session events:
- `stopReason` `error`: `run_error` with the message's `errorMessage`.
- `stopReason` `aborted`: `run_settled` with `aborted: true` and empty
`finalText`.
- Otherwise `run_settled` with `aborted: false`. `finalText` is the text of
that last message. If that is empty, it is all recorded assistant texts of
the run joined with blank lines. If still empty, it is "Done, no text
reply."
- Otherwise `run_settled` with `aborted: false`. `finalText` is every
assistant message in the run whose `stopReason` was not `toolUse`, joined
with blank lines, because Pi drains queued follow-ups and steers inside
the same run. If that is empty, it is all recorded assistant texts joined
with blank lines. If still empty, it is "Done, no text reply."
- `status()` reads `session.isStreaming`, `session.model`,
`session.thinkingLevel`, `session.getContextUsage()`, and
`session.sessionFile`.