Skip to content

fix(gateway): remap legacy agent:main:main alias onto live default agent (#2733)#2743

Merged
alexey-pelykh merged 1 commit into
mainfrom
feat/gateway-legacy-main-alias-remap-2733
Jun 18, 2026
Merged

fix(gateway): remap legacy agent:main:main alias onto live default agent (#2733)#2743
alexey-pelykh merged 1 commit into
mainfrom
feat/gateway-legacy-main-alias-remap-2733

Conversation

@alexey-pelykh

Copy link
Copy Markdown

Problem

A 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 defaultsrc/gateway/sessions-resolve.ts started rejecting that on-disk entry with Agent "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:main alias is remapped onto the live default agent's main session iff ALL FOUR conjuncts hold; otherwise the current reject/pass behavior is unchanged:

  1. The encoded agent-id segment is the literal historical default id main, AND the key is the main-SESSION alias agent:main:main (a concrete agent:main:<peer> session under the deleted main agent stays rejected).
  2. main is not a currently-configured agent.
  3. A live default agent exists — resolveDefaultAgentId(cfg) resolves to a configured agent, not the no-config default fallback.
  4. Legacy single-store provenance — the matched entry came from the single configured (non-template) store, not a discovered per-agent store.

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 via canonicalizeMainSessionAlias.

Changes

Test evidence

vitest run --config vitest.gateway.config.ts --pool=forks src/gateway/sessions-resolve-store.test.ts4 passed, 0 skipped:

  • ✓ resolves legacy main-alias matches by sessionId and label for the configured default agent (agent:main:mainagent:ops:main) — the un-skipped case
  • ✓ still rejects non-alias agent:main matches when main is no longer configured — falsifier
  • ✓ does not adopt legacy main aliases from discovered deleted-agent stores — falsifier (conjunct 4)
  • ✓ rejects an explicit listed deleted main key instead of remapping — falsifier

Broader 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

…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]>
@alexey-pelykh
alexey-pelykh merged commit 6e34bdc into main Jun 18, 2026
17 checks passed
@alexey-pelykh
alexey-pelykh deleted the feat/gateway-legacy-main-alias-remap-2733 branch June 18, 2026 14:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gateway: design legacy main-alias remap boundary (agent:main:main → default agent) — un-skip sessions-resolve-store test

1 participant