fix(gateway): remap legacy agent:main:main alias onto live default agent (#2733)#2743
Merged
Merged
Conversation
…ent (#2733) The legacy default-agent main session serialized as `agent:main:main` in a single (non-per-agent) store. After the default agent was renamed (e.g. to `ops`) — or the no-config default became `default` — that on-disk entry was rejected with `Agent "main" no longer exists`, breaking continuity for the user's old default-agent main thread. Add a NARROW remap gated on all four conjuncts: the encoded agent-id is the historical `main` AND the key is the main-SESSION alias `agent:main:main`; `main` is not a configured agent; a configured live default exists (not the no-config `default` fallback); and the entry came from the single configured (non-template) store. The single-store provenance conjunct is load-bearing for security — a discovered per-agent deleted-agent store stays rejected, preserving the openclaw#65524 deleted-agent send/steer guard, so the boundary cannot re-open that hole. - session-utils.ts: add resolveLegacyDefaultMainRemap (4-conjunct gate) and isSingleConfiguredSessionStore; loadCombinedSessionStoreForGateway now returns fromSingleStore provenance for the sessionId/label paths. - sessions-resolve.ts: 2-state rejectDeletedAgentSessionKey (reject|pass) becomes 3-state resolveDeletedAgentSessionKey (remap|reject|pass), applied at all four resolution sites; in-code ADR-NOTE documents the boundary and records that the reject-guard + remap are fork-introduced (#2720/#2733) — upstream has neither guard nor test — so a future diff-sync must not restore the no-guard form (ADR-0011 re-introduction-guard pattern). - sessions-resolve-store.test.ts: un-skip the legacy main-alias remap case; the discovered-store and explicit-key reject falsifiers stay green, proving the boundary is narrow. Closes #2733 Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
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
A legacy default-agent main session serialized as
agent:main:mainin a single (non-per-agent) store. After the default agent was renamed (e.g. toops) — or the no-config default becamedefault—src/gateway/sessions-resolve.tsstarted rejecting that on-disk entry withAgent "main" no longer exists in configuration, breaking continuity for the user's old default-agent main thread.Approach — NARROW 4-conjunct remap
The legacy
agent:main:mainalias is remapped onto the live default agent's main session iff ALL FOUR conjuncts hold; otherwise the current reject/pass behavior is unchanged:main, AND the key is the main-SESSION aliasagent:main:main(a concreteagent:main:<peer>session under the deletedmainagent stays rejected).mainis not a currently-configured agent.resolveDefaultAgentId(cfg)resolves to a configured agent, not the no-configdefaultfallback.Conjunct 4 is load-bearing for security. It keeps a discovered per-agent deleted-agent store rejected, preserving the openclaw#65524 deleted-agent send/steer guard. A remap that ignored provenance would re-open that hole.
When all four hold:
agent:main:<rest>→agent:<live-default>:<rest>, then re-canonicalized viacanonicalizeMainSessionAlias.Changes
session-utils.ts—resolveLegacyDefaultMainRemap(the 4-conjunct gate) +isSingleConfiguredSessionStore;loadCombinedSessionStoreForGatewaynow returnsfromSingleStoreprovenance so the sessionId/label paths can distinguish single-store from discovered-store.sessions-resolve.ts— the 2-staterejectDeletedAgentSessionKey(reject | pass) becomes the 3-stateresolveDeletedAgentSessionKey(remap | reject | pass), applied at all four resolution sites (key / sessionId / label). Key-path sites pass single-store provenance directly; combined-loader paths threadfromSingleStore.sessions-resolve-store.test.ts— un-skipped the legacy main-alias remap case (refs gateway: design legacy main-alias remap boundary (agent:main:main → default agent) — un-skip sessions-resolve-store test #2733).Test evidence
vitest run --config vitest.gateway.config.ts --pool=forks src/gateway/sessions-resolve-store.test.ts→ 4 passed, 0 skipped:agent:main:main→agent:ops:main) — the un-skipped caseagent:mainmatches when main is no longer configured — falsifierBroader gateway lane: 143 files / 1582 passed, 6 skipped (pre-existing), 0 failed.
pnpm check(format + typecheck + type-aware lint + css-drift) green; rebrand-gate, zombie-import, stub-debt, throwing-stub-callers gates green.Closes #2733
🤖 Generated with Claude Code