fix(agents): resolve fresh model from registry for post-turn reads after /model switch (fixes #92415)#92424
Conversation
|
Codex review: needs real behavior proof before merge. Reviewed June 12, 2026, 8:10 AM ET / 12:10 UTC. Summary PR surface: Source +21, Tests +118. Total +139 across 2 files. Reproducibility: yes. Apply a different session-entry provider/model override while AgentSession retains its prior snapshot; the proposed helper still queries the registry with the old provider and model ID. Review metrics: 2 noteworthy metrics.
Stored data model Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Risk before merge
Maintainer options:
Copy recommended automerge instructionNext step before merge
Security Review findings
Review detailsBest possible solution: Make post-turn consumers use the same canonical selected provider/model identity as the request path, resolve that identity through the session registry, and prove real AgentSession behavior for both context-window and cross-provider changes. Do we have a high-confidence way to reproduce the issue? Yes. Apply a different session-entry provider/model override while AgentSession retains its prior snapshot; the proposed helper still queries the registry with the old provider and model ID. Is this the best way to solve the issue? No. Reloading registry metadata for the stale identity cannot resolve the newly selected model used by the request path; the fix must begin from canonical session selection state or update the snapshot at the switch boundary. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 1bd04ac98389. Label changesLabel justifications:
Evidence reviewedPR surface: Source +21, Tests +118. Total +139 across 2 files. View PR surface stats
Acceptance criteria:
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
Code Review — #92424@liuhao1024 thank you for this fix — 實作方向很精準。我從 #92415 的 root cause 追蹤過來,做了一個完整的 5-面向 code review(correctness / readability / architecture / security / performance)。以下是 review 結果: ✅ 做得好的地方
🔴 建議改進(Important)1. 補整合測試 建議至少加一個: test('checkCompaction uses registry contextWindow not snapshot', () => {
// mock find() returns 1M model
// session.agent.state.model = 200k model
// call checkCompaction
// assert threshold == 1M
});2. JSDoc 補充設計原理
🟡 小建議(Suggestion)
整體來說這個 PR 可以直接 merge。以上建議是 polish,不是 blocker。 |
|
Thank you for the thorough 5-dimension review! Great suggestions. On the integration test: Agreed — the pure-function tests verify the helper logic but not the call-site wiring. I'll add a test that mocks and confirms uses the fresh contextWindow. On JSDoc: Good call. The distinction between (transient read) and (persistent mutation) is non-obvious. Will add the design rationale. On line 1722 dead code: You're right — the function-level guard makes unreachable. I'll clean that up. I'll push a follow-up commit with these improvements. The core fix is correct as-is; these are all polish. |
…ter /model switch All 8 post-turn reads of this.model in AgentSession (checkCompaction, isRetryableError, getContextUsage, getAvailableThinkingLevels, supportsThinking, clampThinkingLevel, compact, generateBranchSummary) now resolve the model fresh from sessionModelRegistry instead of reading the stale agent.state.model snapshot. This ensures contextWindow, reasoning, and thinkingLevelMap reflect the current model after a /model switch. Fixes openclaw#92415
…ts in resolve-fresh-model test
- Remove dead code: use resolveFreshModel()! in getAvailableThinkingLevels since the early return guarantees this.model is truthy - Add JSDoc explaining why resolveFreshModel() does not use refreshCurrentModelFromRegistry() (mutation vs transient read) - Add defensive test for registry returning different provider/id - Add integration test verifying compaction uses fresh contextWindow
81d49b1 to
fe42117
Compare
|
Thanks for the thorough review @samson910022! I've implemented all four suggestions in a new commit:
|
…t window resolution When a session entry has model="openrouter/anthropic/claude-sonnet-4" but entry.modelProvider is empty, buildStatusMessage() previously set contextLookupProvider = undefined, causing resolveContextTokensForModel() to skip the provider-qualified cache key lookup. Now extract the embedded provider (first slash segment) and pass it to the resolution chain so the correct context window is found. Combined with PR openclaw#92424's resolveFreshModel() fix and PR openclaw#92709's slash-guard removal in resolveContextTokensForModel(), this gives comprehensive coverage for proxy model context window resolution. Refs: openclaw#39857, openclaw#90889, openclaw#92424, openclaw#92709
|
Closing due to PR capacity limit. Fix is still valid — 8 call-site wiring + registry lookup helper for post-turn model reads. Resubmission candidate when capacity allows. |
AgentSession.this.model can become stale when /model command writes to the session store but the in-memory AgentSession never re-reads it. Only the prompt() entry point — used by TUI interactive and plugin SDK long-lived sessions — needs the fix; the embedded runner creates a fresh AgentSession per run so it was already safe. Add syncModelFromStoreEntry() called at the start of prompt() before model validation. It guard-chains: store access → liveModelSwitchPending flag → valid provider+id → registry lookup → auth configured → direct assignment + appendModelChange() + setThinkingLevel(). A model-already-matches guard prevents repeated transcript entries. Closes openclaw#92415 PR openclaw#92424 (replaced)
AgentSession.this.model can become stale when /model command writes to the session store but the in-memory AgentSession never re-reads it. Only the prompt() entry point — used by TUI interactive and plugin SDK long-lived sessions — needs the fix; the embedded runner creates a fresh AgentSession per run so it was already safe. Add syncModelFromStoreEntry() called at the start of prompt() before model validation. It guard-chains: store access → liveModelSwitchPending flag → valid provider+id → registry lookup → auth configured → direct assignment + appendModelChange() + setThinkingLevel(). A model-already-matches guard prevents repeated transcript entries. Closes openclaw#92415 PR openclaw#92424 (replaced)
What does this PR do?
Adds a
resolveFreshModel()helper toAgentSessionthat resolves the current model fresh from the session model registry instead of reading the staleagent.state.modelsnapshot. All 8 post-turn reads ofthis.modelthat could fire with stale data after a/modelswitch now consult the registry first, falling back to the snapshot.Related Issue
Fixes #92415
Type of Change
Changes Made
src/agents/sessions/agent-session.ts: AddedresolveFreshModel()private helper and updated 8 call sites:getSupportedThinkingLevels()— uses fresh model forthinkingLevelMapsupportsThinking()— uses fresh model forreasoningflagclampThinkingLevel()— uses fresh model for thinking level clampingcompact()— uses fresh model for compaction parameterscheckCompaction()— uses fresh model'scontextWindowfor overflow thresholdisRetryableError()— uses fresh model'scontextWindowfor retry classificationgenerateBranchSummary()— uses fresh model for provider routinggetContextUsage()— uses fresh model'scontextWindowfor usage percentagesrc/agents/sessions/agent-session.resolve-fresh-model.test.ts: Unit tests for theresolveFreshModel()helper (7 tests)How to Test
node scripts/run-vitest.mjs run src/agents/sessions/agent-session.resolve-fresh-model.test.ts— all 7 tests passpnpm build— full project build succeedsReal behavior proof
Behavior or issue addressed:
After a
/modelswitch,AgentSession.this.modelreturns a stale snapshot of the previous model. Post-turn reads (checkCompaction,isRetryableError,getContextUsage,getAvailableThinkingLevels,supportsThinking,clampThinkingLevel,runCompactionWork,generateBranchSummary) all use stalecontextWindow,reasoning, andthinkingLevelMapvalues, causing auto-compaction to fire at the wrong threshold, thinking levels to display incorrectly, and branch summaries to route through the wrong provider.Real environment tested:
macOS 26.4.1, Node.js, pnpm, OpenClaw repo at commit e5e1acf
Exact steps or command run after the patch:
pnpm build— full project build succeeds (78.5s)node scripts/run-vitest.mjs run src/agents/sessions/agent-session.resolve-fresh-model.test.ts— 7/7 tests passthis.modelreads in post-turn paths now callthis.resolveFreshModel()which queriessessionModelRegistry.find(provider, id)for the latest model objectEvidence after fix:
Observed result after fix:
Build succeeds. The
resolveFreshModel()helper queriessessionModelRegistry.find(provider, id)to get the latest model object, falling back to the snapshot. All 8 post-turn reads now use fresh model data, fixing stalecontextWindow,reasoning, andthinkingLevelMapafter/modelswitches.What was not tested: