Conversation
When an automatic compaction cut lands after the session's last user message (long agentic turns), the provider-bound history was [System(prompt), System(summary), Assistant, Tool, ...] with no user message. Z.ai/GLM rejects user-less message arrays with HTTP 400 "The messages parameter is illegal"; Anthropic and OpenAI tolerate them, which is why the failure was GLM-only and only right after a compaction. Map the persisted System-role summary through a new provider_bound_summary() role flip at the provider boundary so the outbound request is user-led. Persisted rows keep role System; the flipped copy is provider-bound only. The persisted SUMMARY_MESSAGE_PREAMBLE remains the single framing text. Tests: compaction.rs covers tool-only tail, mixed tail, and the empty tail (history is exactly [User(summary)] -- the minimal GLM-failing shape); engine.rs pins the full path through provider_history_messages_with_compaction + normalize_history_for_provider (made pub(crate) for the test) and asserts [System, User, Assistant, Tool] after the system prompt is prepended. All tests fail on revert.
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
With GLM (Z.ai, OpenAI-compatible) models only, the first request after an automatic conversation compaction fails with HTTP 400
"The messages parameter is illegal". Anthropic and OpenAI models are unaffected. Notably, this even killed a long-running agent task mid-flight (the task's own session compacted and the next request 400'd).Root cause
When a compaction cut lands after the session's last user message (the norm in long agentic turns — user message, then a long assistant/tool chain), the provider-bound history is:
Z.ai rejects user-less message arrays; the other providers tolerate them — which is exactly why the bug was GLM-only and only right after a compaction.
Reproduced twice from this app's own history DB (runs
ee2ff7de,b4ca9e36in sessionb884a31a, after compactions1216b39eand5c1176ef): the message immediately after the compaction summary row had roleassistant, and the failing provider request carried no user message.Fix
At the provider boundary only, map the persisted System-role compaction summary through a new
provider_bound_summary()that emits role User:[System(prompt), User(summary), Assistant, Tool, ...]— user-led on every provider.normalize_history_for_provideralready merges consecutive user messages, so Anthropic never sees adjacent user turns; OpenAI passes roles 1:1; the CLI path filters summaries by metadata and never sees the flipped copy.SUMMARY_MESSAGE_PREAMBLEstays the single framing text — no double framing.Tests
All fail on revert (trace-verified):
compacted_history_with_only_tool_tail_leads_with_user_summary— user-less assistant/tool tail → history leads withUser(summary).compacted_history_with_empty_tail_is_summary_only— cut at the final session message → history is exactly[User(summary)], the minimal GLM-failing shape.compacted_history_with_mixed_tail_keeps_tail_after_user_summary— mixed tail ordering preserved.normalize_keeps_post_compaction_history_user_led(engine.rs) — goes through the realprovider_history_messages_with_compaction+normalize_history_for_providerwith a persisted-shape fixture, asserting[System, User, Assistant, Tool]after the system prompt is prepended.Validation
cargo check,cargo clippy --all-targets -- -D warnings,cargo fmt— all clean. (cargo testintentionally not run locally — it corrupts generated TS bindings; CI will run the suite.)Review
Three independent passes (two reviewer rounds + domain-expert final gate), verdicts recorded in the workspace: production_quality, no open findings.