Skip to content

Preserve Open WebUI UUID sessions across Control UI#1467

Open
BingqingLyu wants to merge 9 commits into
mainfrom
fork-pr-55351-codex-openclaw-webui-session-fix-v2026.3.24
Open

Preserve Open WebUI UUID sessions across Control UI#1467
BingqingLyu wants to merge 9 commits into
mainfrom
fork-pr-55351-codex-openclaw-webui-session-fix-v2026.3.24

Conversation

@BingqingLyu

@BingqingLyu BingqingLyu commented Apr 27, 2026

Copy link
Copy Markdown
Owner

Summary

  • preserve Open WebUI UUID chat ids instead of canonicalizing them to agent:main:<uuid> in the gateway session store
  • normalize agent:main:<uuid> back to the original bare UUID when the Control UI reads or writes chat URLs
  • pass agent context into transcript lookup so agent-scoped histories resolve the correct transcript file
  • add regression coverage for UUID preservation, transcript fallback, and Control UI URL normalization

Problem

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-e3c990300d74
  • agent:main:aa28595d-ef49-4381-b332-e3c990300d74

Once 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

  1. Gateway session-key resolution now preserves opaque bare UUIDs instead of rewriting them to agent-scoped keys.
  2. The Control UI normalizes agent:main:<uuid> URLs back to the original bare UUID and keeps that normalized form in the address bar.
  3. Session history loading now carries agentId through transcript lookup so agent-scoped stores resolve the right transcript path for these chats.
  4. Regression tests cover the gateway store key, transcript fallback, and Control UI URL behavior.

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.ts
  • manual verification against a deployed Open WebUI + OpenClaw setup on v2026.3.24: opening the same UUID-backed chat in the Control UI reused the existing history instead of splitting into a second session

Fixes openclaw#49202

SHAREN added 9 commits March 27, 2026 00:54
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`.
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
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.

[Bug]: Open WebUI-backed UUID chats can split into separate <uuid> and agent:main:<uuid> session records

2 participants