fix(sessions): preserve terminal status after agent run completes#1913
Open
BingqingLyu wants to merge 5 commits intomainfrom
Open
fix(sessions): preserve terminal status after agent run completes#1913BingqingLyu wants to merge 5 commits intomainfrom
BingqingLyu wants to merge 5 commits intomainfrom
Conversation
…ostReplyRootId Direct messages in Mattermost were creating threads even with replyToMode=off because resolveMattermostReplyRootId would fall back to payload.replyToId regardless of chat kind. When a downstream payload carried a replyToId (e.g. from block-streaming delivery), this bypassed the earlier DM threading guard and set root_id on the outbound post, making DM replies appear as threads instead of in the channel body. Fix: pass kind through all three delivery call sites and hard-return undefined inside resolveMattermostReplyRootId for kind="direct", mirroring the resolveMattermostEffectiveReplyToId guard that already existed for session-key resolution. Fixes openclaw#59981
…hreshold The generic_repeat detector only checked warningThreshold and always returned level "warning", making criticalThreshold effectively a no-op for the most common runaway loop pattern (same tool + same args). Add a critical-threshold check before the warning check, consistent with how known_poll_no_progress and ping_pong already behave. Fixes openclaw#60111 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…aining-only fields MiniMax's usage_percent / usagePercent fields report the *remaining* quota as a percentage, not the consumed quota. When count fields (prompt_limit / prompt_remain) are also present, fromCounts already computed the correct usedPercent and the inverted value was silently ignored. But when only usage_percent is returned (no count fields), the code treated it as a used-percent and passed it through unchanged, causing the menu bar to show "2% left" instead of "98% left". Move usage_percent and usagePercent from PERCENT_KEYS to a new REMAINING_PERCENT_KEYS array. deriveUsedPercent now inverts remaining-percent values to obtain usedPercent, matching the behaviour already validated by the existing "prefers count-based usage when percent looks inverted" test. Count- based fromCounts still takes priority over both key groups. Fixes openclaw#60193 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MiMo reasoning models (mimo-v2-pro, mimo-v2-omni) output their full response to reasoning_content with an empty content field. This causes OpenClaw to emit no visible text since the pi-ai stream emits reasoning_content deltas as thinking blocks, not as main content. Setting enable_thinking: false in the request payload directs the model to write its reply to the standard content field instead. Closes openclaw#60261 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
persistGatewaySessionLifecycleEvent writes the terminal status to disk as a fire-and-forget write. updateSessionStoreAfterAgentRun runs after it, but reads the in-memory sessionStore (loaded before the run) which still carries status: "running". The stale status was being spread into the merge patch, clobbering the "done"/"failed"/"killed" status on disk. Result: sessions could remain stuck as "running" even though endedAt and runtimeMs were correctly set, blocking new inbound messages and stop/abort commands until the store row was edited manually. Fix: omit the status field from the patch in updateSessionStoreAfterAgentRun so that the lifecycle-managed terminal status on disk is always preserved. Closes openclaw#60250 Co-Authored-By: Claude Sonnet 4.6 <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
After a run completes, a session can remain persisted with
status: "running"even thoughendedAtandruntimeMsare already set. This wedges the session: new inbound messages are not accepted and stop/abort commands do not work until the store row is manually edited.Root cause
persistGatewaySessionLifecycleEventis called withvoid(fire-and-forget) in the agent event listener. It enqueues a write to set the terminal status on disk. Immediately after,updateSessionStoreAfterAgentRunenqueues its own write.The lock queue runs them in order:
{ status: "done", endedAt: X, runtimeMs: Y }✅updateSessionStoreAfterAgentRun: reads disk (has"done"), but patches from the in-memorysessionStorewhich was loaded during session initialisation — before the run — and still carriesstatus: "running".mergeSessionEntry(diskState, next)spreadsnext.status = "running"over the disk's"done", leavingendedAt/runtimeMsintact (they were never innext).Final persisted state:
{ status: "running", endedAt: X, runtimeMs: Y }— the exact stuck pattern described in the issue.Fix
Omit
statusfrom the patch passed tomergeSessionEntryinsideupdateSessionStoreAfterAgentRun. This function is responsible for token/cost/model fields only; lifecycle status is exclusively owned bypersistGatewaySessionLifecycleEvent.Test
Added a regression test in
src/commands/agent/session-store.test.ts:{ status: "done", endedAt: X, runtimeMs: 1234 }(as if lifecycle end already ran){ status: "running" }(stale)updateSessionStoreAfterAgentRun, assertspersisted.status === "done"andendedAt/runtimeMspreservedCloses openclaw#60250
🤖 Generated with Claude Code