Repository navigation
ChatPanel profile selector can mismatch the active external-agent thread #141
Description
Activity
Additional sanitized evidence from a screenshot
The affected panel can show a prominent error banner stating that the thread cannot change driver kind, while the profile selector below still displays
Chat. The composer remains visible and showsGPT-5.6 SolwithAutomode selected.This supports the report's core symptom: thread/panel state and the profile/driver selection can become inconsistent after rehydration or refresh. Thread identifiers and other potentially identifying details from the screenshot are intentionally omitted.
Code-level finding from a current reproduction (
huabu devdisplayed asChat)The server-side Agent Node creation path appears correct:
apps/server/src/modules/agent/agent-node.service.tsresolves the selected Profile and creates the Question node with an externalagentBinding(profileId+ alias) andagentBindingPolicy: 'fixed'.The mismatch is introduced by Web state ownership/hydration:
ChatPanelreadsagentBindingfromchatStoreviaselectThreadBinding(state, threadId).chatStore.threadOf()returnsDEFAULT_BINDING = { kind: 'internal' }whenever that thread has no cached/persisted binding, which renders asChat.- Question-owned thread metadata is deliberately made ephemeral, so after reload its binding is expected to come from the Question/Agent node rather than
bindingByThread. - Opening a node interactively calls
enterQuestionConversation(..., data.agentBinding, ...), which seeds the store before mounting the panel. A Preview Workspace tab restored directly after refresh does not pass through that entry path. - The restored
ChatPaneltherefore initially uses the internal default and only later attempts to repair it in auseEffectfromconversationOwnerSource.agentBindingwhenagentBindingPolicy === 'fixed'. useChatHistoryhydrates messages but does not hydrate the thread binding. If the conversation owner is temporarily unavailable/stale during Canvas or World-reference hydration, the repair effect has no authoritative binding and the selector can remainChatdespite the correct thread/history.
This also explains why the symptom is associated with refresh/resume and long-lived panels rather than new interactive opens.
Suggested fix direction
For a fixed Agent Node, derive the effective binding synchronously from the authoritative conversation owner (
fixedAgentBinding ?? cachedThreadBinding) and use that value consistently for the selector, title, ACP hooks, and send path. The store synchronization effect can remain a cache update, but should not be the correctness boundary. Restored tabs should also be covered where the thread cache is empty and owner hydration arrives after the first render.Regression coverage
Add a test that mounts/restores a Question/Agent Preview tab with:
- an empty
chatStorebinding cache, - a fixed external node binding for
huabu dev, and - owner data arriving after the initial render.
The selector may show a neutral loading state before owner resolution, but must never identify the fixed external thread as
Chat, and all downstream ACP behavior must use the fixed binding.
Bug
In some cases, especially after a ChatPanel has been left inactive for a long time or after refreshing a long-open ChatPanel, the profile selector can show the wrong profile even though the panel's thread history and thread ID are correct.
A common example is an open panel whose thread belongs to an external agent node: the displayed history and thread ID correspond to that external-agent thread, but the profile selector below incorrectly displays
chat.Expected behavior
The profile selector should be derived from the currently active thread/panel context and should display the profile associated with that thread. Refreshing or resuming an idle panel must not replace the selected profile with a stale or default value.
Actual behavior
The ChatPanel can show the correct external-agent thread history and thread ID while the profile selector displays
chat, producing an inconsistent UI state and making it unclear which profile will be used for the next action.Conditions observed
Investigation notes
Please trace profile selection and synchronization across ChatPanel refresh/resume, thread hydration, thread switching, and external-agent thread loading. Check for stale closure/state, default-profile fallback, or an update ordering/race where thread metadata arrives after the selector state is initialized.
Acceptance criteria
chatwhen their thread metadata identifies another profile.