fix(cli): defer finish-reason default until after compact branch

Setting handle.message.finish before the result === "compact" branch broke guardCompactionAttempt (which fills in "error" with ??=) and the overflow detection at compaction.create. Move the default to right before "continue" and gate it on result !== "compact" so the compaction paths see the original undefined value.
This commit is contained in:
kiloconnect[bot]
2026-05-06 15:12:15 +00:00
parent d8642deec6
commit 4398150bdc
+15 -15
View File
@@ -1600,21 +1600,6 @@ NOTE: At any point in time through this workflow you should feel free to ask the
toolChoice: format.type === "json_schema" ? "required" : undefined,
})
// kilocode_change start - guard against providers that end the stream
// without a terminal stop_reason (e.g. an Anthropic-style message_delta
// with stop_reason: null followed immediately by message_stop). Without
// a finishReason, the loop-exit check at the top of the next iteration
// sees a falsy `finish` (loaded from storage via filterCompactedEffect)
// and keeps stepping forever. Default to "unknown" and persist so the
// regular break condition fires when there are no tool calls — the
// tool-call path is unaffected because `!hasToolCalls` keeps the break
// gated when the assistant did emit tool calls.
if (!handle.message.finish) {
handle.message.finish = "unknown"
yield* sessions.updateMessage(handle.message)
}
// kilocode_change end
if (structured !== undefined) {
handle.message.structured = structured
handle.message.finish = handle.message.finish ?? "stop"
@@ -1672,6 +1657,21 @@ NOTE: At any point in time through this workflow you should feel free to ask the
return "break" as const
}
// kilocode_change end
// kilocode_change start - guard against providers that end the stream
// without a terminal stop_reason (e.g. an Anthropic-style message_delta
// with stop_reason: null followed immediately by message_stop). Without
// a finishReason, the loop-exit check at the top of the next iteration
// sees a falsy `finish` (loaded from storage via filterCompactedEffect)
// and keeps stepping forever. Default to "unknown" and persist so the
// regular break condition fires when there are no tool calls. Skipped
// for the compact path so guardCompactionAttempt can still fill in
// "error" on exhaustion. Tool-call turns already get "tool-calls" from
// the AI SDK; even without it, !hasToolCalls keeps the break gated.
if (result !== "compact" && !handle.message.finish) {
handle.message.finish = "unknown"
yield* sessions.updateMessage(handle.message)
}
// kilocode_change end
return "continue" as const
}).pipe(Effect.ensuring(instruction.clear(handle.message.id)))
if (outcome === "break") break