Skip to content

feat: update conversational agent sample app with personal connection polling - #800

Open
norman-le wants to merge 12 commits into
mainfrom
feat/jar-10826-sample-app-polling
Open

norman-le wants to merge 12 commits into
mainfrom
feat/jar-10826-sample-app-polling

Conversation

@norman-le

@norman-le norman-le commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

uipath-typescript

feat/jar-10826-cas-personal-connections-parity-widgets — adds getConnectionAuthUrl() and getConnectionSessionStatus() (both @internal) #798

  • sessionId added to ConnectionAuthResponse
  • getConnectionSessionStatus() — new method to poll OAuth session completion
  • ConnectionSessionStatusResponse type — pending | success | failed
  • CONNECTION_SESSION_STATUS endpoint
  • @internal caveat on all 4 connection methods (require user-scoped tokens)

Sample app branch (feat/jar-10826-sample-app-polling) — ConnectionsPanel polling fix (setInterval → setTimeout), new ConnectionReadinessCard, ChatArea stale-response guard, isConfigurable filtering.

uipath-ui-widgets — feat/jar-10826-cas-personal-connections-parity-widgets

Porting CAS personal connections UI: UiPath/uipath-ui-widgets#171

  • ConnectionReadinessCard — 3 states (collapsed/yellow/red), OAuth polling at 500ms for connect buttons, auto-collapse
  • ConnectionsSection — settings tab with connection picker, search, grouped by personalWorkspace/folder, dirty tracking, save/cancel, OAuth flow with auth fallback
  • ConversationalAgentChat — fetches readiness on agent init, agent start-up autobinding, race condition guard, clears on send/new chat
  • SettingsDialog — ConnectionsSection accordion item
image

Known Limitations (not code bugs)

  1. Save fails in standalone sample app — external app token lacks OrchestratorApiUserAccess audience, causing Orchestrator's SetResourceUserOverwrites to reject. Should work fine when widgets runs inside the CAS platform with the platform token.

norman-le and others added 7 commits October 1, 2026 16:01
- URL-encode sessionId in CONNECTION_SESSION_STATUS endpoint
- Serialize polling in JSDoc example (setTimeout instead of setInterval)
- Add console.warn to silent catch in getAddConnectionUrl

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Sample app updates depend on unreleased SDK APIs (sessionId,
getConnectionSessionStatus). Moving to a follow-up PR after
the SDK version is published.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
All sample app updates moved to feat/jar-10826-sample-app-polling
branch for a follow-up PR after the SDK version is published.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
- Add cancellation guards for stale agent responses (ChatArea, ConnectionsPanel)
- Serialize polling with recursive setTimeout (ConnectionsPanel)
- Resolve connection state by currentConnectionId, not any enabled connection
- Filter broken/auto-save lists by isConfigurable (ConnectionReadinessCard)
- Disable all Connect buttons while any OAuth flow is active
- Fix "X of Y connections still needed" count
- Add console.warn to all silent catches

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Polling races, stale agent handling, incorrect state mapping, and premature success state can produce incorrect or duplicate updates.

Review effort: Balanced
Findings: 3 Medium severity

Open (3)
What changed in this PR

Updates the conversational-agent sample app with personal connection readiness and OAuth polling.

Changes:

  • Adds in-chat connection readiness UI.
  • Adds OAuth session polling and connection refresh.
  • Guards stale readiness responses and filters configurable connections.
File Description
ConnectionsPanel.tsx Adds connection creation polling and refresh.
ConnectionReadinessCard.tsx Adds readiness states, OAuth flow, polling, and auto-binding.
ChatArea.tsx Fetches and displays connection readiness.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread samples/conversational-agent-app/src/components/ChatArea.tsx Outdated
Comment thread samples/conversational-agent-app/src/components/ConnectionReadinessCard.tsx Outdated
Comment thread samples/conversational-agent-app/src/components/ConnectionReadinessCard.tsx Outdated
@claude

claude Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

Review summary — one issue flagged inline on ConnectionReadinessCard.tsx:147: stale localConnectors closure in the auto-save payload. The setInterval callback captures localConnectors from the useCallback closure at the time startOAuthFlow runs. If the visibilitychange handler refreshes state between when polling starts and when the success tick fires, updateConnectionSelections sends stale connection IDs for every connector other than the one just connected. Suggested fix: mirror state into a localConnectorsRef and read from the ref inside the interval callback.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Polling races and stale state can duplicate updates, overwrite selections, or discard unsaved changes.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
Resolved since last review (3)
Previously missed (2)

In code that hasn't changed since last review

Medium severity Stale auth callbacks persist after target identity changes

samples/​conversational-agent-app/​src/​components/​ConnectionReadinessCard.tsx:79

Clearing the timer alone does not invalidate an already-running auth or status request, and this effect never reruns when agentId/folderId changes. The old callback can consequently auto-save and replace readiness after the card has switched agents. Invalidate the session during cleanup and reset the connecting state whenever the target identity changes.

This issue also appears in the following locations of the same file:

  • line 135
  • line 155
Medium severity OAuth callback discards unsaved staged selections

samples/​conversational-agent-app/​src/​components/​ConnectionsPanel.tsx:96

Using load as the OAuth-success callback silently discards any unsaved picker changes: useConnections.load() replaces both initialSelections and stagedSelections with the server response. If a user changes one row and then adds a connection from another, the first change disappears. Refresh the available connection lists while preserving staged selections (or explicitly merge them) instead of invoking the resetting load path.

This issue also appears on line 164 of the same file.

Comment thread samples/conversational-agent-app/src/components/ChatArea.tsx Outdated
Comment thread samples/conversational-agent-app/src/components/ChatArea.tsx Outdated
@claude

claude Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

Review summary — 2 new findings posted as inline comments:

  1. ConnectionReadinessCard.tsx:135 — setInterval with async callback can overlap. The PR description calls out a setInterval-to-setTimeout fix for ConnectionsPanel; the new ConnectionReadinessCard repeats the same pattern. On slow networks, two interval ticks can both observe a success response and both call updateConnectionSelections, causing a double-save race. Fix: convert to recursive setTimeout as done in ConnectionsPanel.

  2. ChatArea.tsx:77 — silent catch. All other catches in this file call console.warn; this one swallows the error from the deferred re-fetch silently, making failures invisible in dev tooling.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

OAuth races and error paths can discard or overwrite selections and incorrectly hide readiness prompts.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
Resolved since last review (1)
Previously missed (2)

In code that hasn't changed since last review

Medium severity Invalidate pending OAuth flows during cleanup

samples/​conversational-agent-app/​src/​components/​ConnectionReadinessCard.tsx:78

Cleanup clears an existing interval but does not invalidate a pending OAuth flow. If the component unmounts while getConnectionAuthUrl is awaiting, its continuation still passes the session check, opens a tab, and creates a new interval after cleanup has already run. Increment oauthSessionRef during cleanup so pending continuations return without side effects.

This issue also appears in the following locations of the same file:

  • line 157
  • line 175
Medium severity Refresh connections without discarding unsaved selections

samples/​conversational-agent-app/​src/​components/​ConnectionsPanel.tsx:96

Refreshing via load after OAuth completion replaces both initialSelections and stagedSelections with server values (useConnections.ts:40-44). If the user has unsaved edits in another row, creating a connection silently discards them. Refresh the available connection list while preserving/merging staged selections instead of invoking the resetting load path.

This issue also appears on line 164 of the same file.

Comment thread samples/conversational-agent-app/src/components/ChatArea.tsx Outdated
Comment on lines +195 to +197
// Fallback to the platform URL if auth endpoint isn't available
const url = await conversationalAgent.getAddConnectionUrl(item)
if (url) window.open(url, '_blank', 'noopener,noreferrer')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unhandled rejection: getAddConnectionUrl is an await inside a catch block with no surrounding try. If it throws, the error propagates out of the outer catch, and since handleAddConnection is called from a button onClick with no .catch() wrapper, the rejection goes unhandled and shows up as an error in the console.

Wrap the fallback in its own try/catch:

Suggested change
// Fallback to the platform URL if auth endpoint isn't available
const url = await conversationalAgent.getAddConnectionUrl(item)
if (url) window.open(url, '_blank', 'noopener,noreferrer')
try {
const url = await conversationalAgent.getAddConnectionUrl(item)
if (url) window.open(url, '_blank', 'noopener,noreferrer')
} catch (fallbackError) {
console.warn('Failed to open platform connections URL:', fallbackError)
}

@claude

claude Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

Review summary — 1 new finding:

ConnectionsPanel.tsx:195–197 — unhandled rejection in fallback path. getAddConnectionUrl is awaited inside a catch block with no try/catch of its own. If it throws, the error escapes the outer catch and becomes an unhandled promise rejection from the button's onClick handler. Fix: wrap in a nested try/catch with a console.warn.

@claude

claude Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

✅ No issues found. Checked for bugs and CLAUDE.md compliance.

@claude

claude Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

✅ No issues found. Checked for bugs and CLAUDE.md compliance.

Base automatically changed from feat/jar-10826-cas-personal-connections-parity-widgets to main October 7, 2026 16:27
@norman-le
norman-le requested a review from a team October 7, 2026 16:27

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Polling lifecycle races and unsafe state refresh/update behavior can overwrite or misrepresent connection selections.

4 open findings
1 resolved since last review

🧠 Review effort: Balanced

Comment on lines +80 to +83
// Cleanup polling on unmount
useEffect(() => {
return () => { clearPoll() }
}, [clearPoll])
Comment on lines +160 to +165
await conversationalAgent.updateConnectionSelections(agentId, folderId, {
selections: localConnectorsRef.current.filter(c => c.isConfigurable).map(c => ({
connectorKey: c.connectorKey,
connectionId: c.connectorKey === connectorKey ? status.connectionId : c.currentConnectionId,
})),
})
Comment on lines +178 to +184
// Fallback: optimistically update just the connected connector
if (oauthSessionRef.current === oauthSession) {
updateLocalConnectors(prev =>
prev.map(c =>
c.connectorKey === connectorKey
? { ...c, currentConnectionId: status.connectionId, currentConnectionState: 'Enabled' as const }
: c,
Comment on lines +58 to +66
useEffect(() => {
const handler = () => {
if (document.visibilityState !== 'visible') return
if (isLoading || isSaving) return
load()
}
document.addEventListener('visibilitychange', handler)
return () => document.removeEventListener('visibilitychange', handler)
}, [load, isLoading, isSaving])
@claude

claude Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

✅ No issues found. Checked for bugs and CLAUDE.md compliance.

@sonarqubecloud

sonarqubecloud Bot commented Oct 7, 2026

Copy link
Copy Markdown

This branch has not been deployed

No deployments
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.

3 participants