What
In a chat with a historical connect_service tool call, after the OAuth flow completes successfully and the page is refreshed, the embedded LinkProvider card re-renders as an active "Connect Google Calendar" CTA — even though the credential is connected and subsequent calendar tool calls in the same chat work fine.
Repro: chat chat_VjcXp8h3dx, see attached screenshot. Backend confirms /internal/v1/credentials/default/google-calendar returns 200 from 11:28:14 onward; the chat just keeps showing the old "isn't connected yet" copy + a live Connect button.
Why
Two reinforcing problems in tools/agent-playground/src/lib/components/chat/connect-service.svelte:
- Component never reconciles connection state at mount.
connected is initialized to false and only flips to true when the OAuth callback message fires in the current page session ($effect at line 100). On a fresh page load the listener can't fire — the OAuth dance already completed — so the card re-renders the active "Connect" state.
- Persisted tool output is frozen. The assistant's
tool-connect_service part in the chat stream stays { progress: { status: "active" } } forever. The successful OAuth arrives as a separate data-credential-linked user-message part rather than mutating the original tool call's output, so even an export/replay sees "still connecting."
tool-call-card.svelte:397-400 unconditionally mounts <ConnectService> whenever a connect_service tool call exists, regardless of whether the credential is now present.
Fix idea
Smallest safe fix: in connect-service.svelte's mount $effect, also query Link for current credential status (e.g. GET /v1/summary or a per-provider check) and set connected = true if the credential already exists — reusing the existing "✓ {provider} connected" success state.
Larger follow-up: update the persisted tool-call output when the credential-linked event arrives, so chat history/export is internally consistent.
What
In a chat with a historical
connect_servicetool call, after the OAuth flow completes successfully and the page is refreshed, the embedded LinkProvider card re-renders as an active "Connect Google Calendar" CTA — even though the credential is connected and subsequent calendar tool calls in the same chat work fine.Repro: chat
chat_VjcXp8h3dx, see attached screenshot. Backend confirms/internal/v1/credentials/default/google-calendarreturns 200 from 11:28:14 onward; the chat just keeps showing the old "isn't connected yet" copy + a live Connect button.Why
Two reinforcing problems in
tools/agent-playground/src/lib/components/chat/connect-service.svelte:connectedis initialized tofalseand only flips totruewhen the OAuth callback message fires in the current page session ($effectat line 100). On a fresh page load the listener can't fire — the OAuth dance already completed — so the card re-renders the active "Connect" state.tool-connect_servicepart in the chat stream stays{ progress: { status: "active" } }forever. The successful OAuth arrives as a separatedata-credential-linkeduser-message part rather than mutating the original tool call's output, so even an export/replay sees "still connecting."tool-call-card.svelte:397-400unconditionally mounts<ConnectService>whenever aconnect_servicetool call exists, regardless of whether the credential is now present.Fix idea
Smallest safe fix: in
connect-service.svelte's mount$effect, also query Link for current credential status (e.g.GET /v1/summaryor a per-provider check) and setconnected = trueif the credential already exists — reusing the existing "✓ {provider} connected" success state.Larger follow-up: update the persisted tool-call output when the credential-linked event arrives, so chat history/export is internally consistent.