Preserve Open WebUI UUID sessions across Control UI#1467
Open
BingqingLyu wants to merge 9 commits into
Open
Conversation
What changed: - preserve opaque bare UUID session keys in gateway session-store resolution instead of canonicalizing them to agent-scoped keys - normalize Control UI chat URLs from agent:main:<uuid> back to the original bare UUID and keep that normalized form when syncing the URL - pass agent context into transcript lookup so agent-scoped session stores resolve the correct transcript file - add regression coverage for the gateway, transcript-path fallback, and Control UI URL normalization flows - include the new app-settings regression file in the active Vitest unit suites Why: - Open WebUI uses opaque UUID chat ids for its conversations while OpenClaw's gateway and Control UI usually canonicalize bare keys into agent-prefixed session keys - that mismatch can split one Open WebUI conversation into a bare UUID session and an agent:main:<uuid> session, which makes history look missing or incomplete depending on which path is opened - the bug is narrow to the Open WebUI integration path, but it breaks the multi-chat workflow that makes Open WebUI useful as a ChatGPT-style frontend for OpenClaw Impact: - Open WebUI and OpenClaw Control UI now converge on the same session key for main-agent UUID chats - opening an Open WebUI chat in Control UI reuses the existing history instead of creating a second session shell - session history loading follows the agent-scoped transcript location consistently for these chats Validation: - pnpm exec vitest run --config vitest.gateway.config.ts src/gateway/session-utils.fs.test.ts -t "uses agentId fallback to read agent-scoped transcripts from OPENCLAW_HOME" - pnpm exec vitest run --config vitest.gateway.config.ts src/gateway/session-utils.test.ts -t "preserves opaque bare UUID session keys" - pnpm exec vitest run --config vitest.unit.config.ts ui/src/ui/app-settings.test.ts - manually verified on the deployed Open WebUI + Control UI flow that the same UUID chat opens with shared history instead of splitting into a second session
What changed: - add a legacy agent-scoped UUID alias to gateway session-store scan targets so upgraded stores still resolve old `agent:<id>:<uuid>` entries - switch Control UI URL normalization to the shared `looksLikeSessionId` helper instead of a narrower local UUID regex - add regression tests for the legacy alias path and for future UUID variants that still match the gateway session id format Why: - automated review surfaced two real edge cases in the original Open WebUI fix - upgraded installs could lose history for chats that were previously stored under `agent:main:<uuid>` once the Control UI starts normalizing those URLs back to bare UUIDs - the UI regex also diverged from the gateway-side session id matcher, which could recreate the same split bug for newer UUID versions Impact: - pre-existing prefixed Open WebUI chat keys remain readable after upgrade - the Control UI and gateway now agree on which UUID strings should be treated as opaque session ids Validation: - pnpm exec vitest run --config vitest.gateway.config.ts src/gateway/session-utils.test.ts -t "resolveSessionStoreKey preserves opaque bare UUID session keys" - pnpm exec vitest run --config vitest.gateway.config.ts src/gateway/session-utils.test.ts -t "resolveGatewaySessionStoreTarget keeps legacy agent-scoped UUID aliases addressable" - pnpm exec vitest run --config vitest.unit.config.ts ui/src/ui/app-settings.test.ts -t "normalizeChatUrlSessionKey"
What changed: - extend gateway session-store scan targets to probe the bare UUID form when the incoming key is the legacy agent-scoped UUID alias - add a regression test that covers prefixed legacy links resolving a store entry that already lives under the bare UUID key Why: - the earlier Open WebUI fix preserved upgraded prefixed UUID sessions when a bare UUID lookup arrived, but older Control UI links could still arrive in the opposite direction as agent:main:<uuid> - without the reciprocal fallback, upgraded stores could still look empty for those legacy links even though the chat had already converged onto the bare UUID form Impact: - Open WebUI UUID chats remain readable from both old prefixed links and the normalized bare UUID form during and after migration Validation: - pnpm exec vitest run --config vitest.gateway.config.ts src/gateway/session-utils.test.ts - pnpm exec vitest run --config vitest.gateway.config.ts src/gateway/session-utils.fs.test.ts - pnpm exec vitest run --config vitest.unit.config.ts ui/src/ui/app-settings.test.ts - note: src/gateway/session-utils.test.ts still includes a pre-existing unrelated baseline failure in uses persisted active subagent runs when the local worker only has terminal snapshots; the new Open WebUI UUID tests pass in that run
Resolve the two PR merge conflicts in vitest include lists by keeping the new upstream unit test patterns (`packages/**/*.test.ts`, `ui/src/ui/chat/**/*.test.ts`) alongside the Open WebUI regression entry for `ui/src/ui/app-settings.test.ts`.
…session-fix-v2026.3.24
What changed: - reintroduced a runtime agent.getApiKey hook in the embedded attempt path so custom providers can resolve credentials through modelRegistry.getApiKeyForProvider - wrapped agent.streamFn in applyExtraParamsToAgent with a dedicated Codex proxy transport selector - added codex-proxy-transport.ts so openai-codex-responses models on non-ChatGPT base URLs can use the proxy-aware WebSocket transport instead of the default simple transport - preserved bearer auth while synthesizing a separate chatgpt-account-id when proxy transports do not provide one explicitly - cleaned up a constant-truthiness error branch in the proxy transport so the staged files pass the repository lint gate Why: - direct embedded codex-lb runs had been failing because the embedded runtime lost provider credentials after the auth-aware stream path was replaced - even after credentials were restored, Codex-style proxy backends still needed a different transport contract than the default ChatGPT/OpenAI path - these changes keep generic embedded runs working while allowing non-OpenAI Codex proxy endpoints to speak the backend /codex/responses transport they actually expect Impact: - embedded sessions can once again resolve runtime API keys for custom providers such as codex-lb - models using openai-codex-responses on proxy base URLs can take the dedicated WebSocket transport path instead of being forced through the default streamSimple contract - standard providers continue using the existing transport path unless the model explicitly matches the proxy Codex criteria
What changed: - imported resolveAgentRoute into the gateway send handler - when callers omit explicitAgentId and sessionKey but provide an accountId, derive a routedAgentId from the configured channel/account route before falling back to the default agent - kept the existing explicit agent and session-derived agent precedence intact Why: - outbound sends that targeted a bound account such as the paperclip Telegram account could still mirror into the default main agent session when no sessionKey was supplied - that made manual sends land in the wrong agent context even though the route configuration already knew which agent owned the account Impact: - account-scoped sends can now inherit the bound agent route automatically - mirrored session keys and agent ownership stay aligned with the configured channel route instead of drifting back to the default agent when sessionKey is omitted
What changed: - added a compat auth resolver inside compaction-safeguard.ts that accepts getApiKeyAndHeaders, getApiKeyForProvider, or the legacy getApiKey method on ctx.modelRegistry - preserved the existing headers-aware safeguard contract so compaction can still proceed when auth is supplied through request headers rather than only a raw API key - switched the safeguard entrypoint to use that compat resolver before deciding whether compaction must be cancelled - exported the resolver through __testing and added focused regression coverage in compaction-safeguard.test.ts for new-model-registry auth resolution and the unsupported-interface error case Why: - manual and automatic compaction were still calling ctx.modelRegistry.getApiKey(model) directly - newer pi-coding-agent model registries no longer expose that method, which caused compaction safeguard to cancel with ctx.modelRegistry.getApiKey is not a function - the embedded runtime had already been patched to use getApiKeyForProvider, so compaction was the remaining incompatible auth path Impact: - compaction safeguard can resolve request auth on both old and new model registry interfaces - custom providers such as codex-lb no longer fail compaction solely because the registry dropped the legacy getApiKey method - the fix is covered by targeted safeguard tests that passed on the server checkout before commit
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.
Summary
agent:main:<uuid>in the gateway session storeagent:main:<uuid>back to the original bare UUID when the Control UI reads or writes chat URLsProblem
Open WebUI uses opaque UUIDs for chat ids. When the same chat is opened through the OpenClaw Control UI, OpenClaw currently treats that UUID like an ordinary bare session key and canonicalizes it into an agent-prefixed key.
In practice, one Open WebUI conversation can end up with two identities:
aa28595d-ef49-4381-b332-e3c990300d74agent:main:aa28595d-ef49-4381-b332-e3c990300d74Once that happens, the Control UI can open an empty shell instead of the real Open WebUI conversation, or it can miss the existing transcript even though the messages are already present on disk.
This is a narrow integration bug rather than a broad session cleanup issue. It shows up when Open WebUI is used as a multi-chat frontend for OpenClaw and the same UUID-backed chat is later opened in the Control UI.
Why this matters
Open WebUI is a useful ChatGPT-style frontend for OpenClaw because it makes it easy to keep multiple long-running topic-specific chats in the browser. Without this fix, that workflow still requires a downstream monkey patch after upgrades, because the shared Open WebUI/OpenClaw session identity drifts apart.
What changed
agent:main:<uuid>URLs back to the original bare UUID and keeps that normalized form in the address bar.agentIdthrough transcript lookup so agent-scoped stores resolve the right transcript path for these chats.Scope
This PR intentionally stays focused on shared session identity and transcript lookup for Open WebUI-backed chats.
It does not try to mirror external turns into Open WebUI's own SQLite history database. That visual-sync behavior is a separate follow-up concern.
Testing
pnpm exec vitest run --config vitest.gateway.config.ts src/gateway/session-utils.fs.test.ts -t "uses agentId fallback to read agent-scoped transcripts from OPENCLAW_HOME"pnpm exec vitest run --config vitest.gateway.config.ts src/gateway/session-utils.test.ts -t "preserves opaque bare UUID session keys"pnpm exec vitest run --config vitest.unit.config.ts ui/src/ui/app-settings.test.tsv2026.3.24: opening the same UUID-backed chat in the Control UI reused the existing history instead of splitting into a second sessionFixes openclaw#49202