Skip to content

use widget for chat to edit#1995

Open
anubra266 wants to merge 7 commits intomainfrom
use-widget-inchat-to-edit
Open

use widget for chat to edit#1995
anubra266 wants to merge 7 commits intomainfrom
use-widget-inchat-to-edit

Conversation

@anubra266
Copy link
Contributor

No description provided.

@vercel
Copy link

vercel bot commented Feb 13, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agents-api Ready Ready Preview, Comment Feb 17, 2026 11:58pm
agents-manage-ui Ready Ready Preview, Comment Feb 17, 2026 11:58pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
agents-docs Skipped Skipped Feb 17, 2026 11:58pm

Request Review

@changeset-bot
Copy link

changeset-bot bot commented Feb 13, 2026

⚠️ No Changeset found

Latest commit: 89ca561

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

claude[bot]

This comment was marked as resolved.

@github-actions github-actions bot deleted a comment from claude bot Feb 13, 2026
claude[bot]

This comment was marked as resolved.

@vercel vercel bot temporarily deployed to Preview – agents-docs February 13, 2026 23:39 Inactive
claude[bot]

This comment was marked as resolved.

@github-actions github-actions bot deleted a comment from claude bot Feb 13, 2026
sarah-inkeep

This comment was marked as resolved.

claude[bot]

This comment was marked as resolved.

@github-actions github-actions bot deleted a comment from claude bot Feb 14, 2026
Copy link
Contributor

@sarah-inkeep sarah-inkeep left a comment

Choose a reason for hiding this comment

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

I'm seeing this error in the console when the tool approval ui shows:
Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
Here is a loom:
https://www.loom.com/share/922d7d2cb78948d184437fd1149ab593

I checked on the main branch and I don't see the error there.

- Updated IkpTool component to use a functional component syntax for better clarity.
- Enhanced parseToolOutputForRelations function to utilize flatMap for cleaner data extraction.
- Introduced ApprovalButtons component to streamline approval button rendering in ToolApproval.
- Replaced setConversationId with resetPlaygroundConversationId in ChatWidget and Playground components for consistency in state management.
- Added playgroundConversationId to agent state for better tracking of conversation IDs.
Copy link
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

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

PR Review Summary

(2) Total Issues | Risk: High

🔴❗ Critical (1) ❗🔴

Inline Comments:

  • 🔴 Critical: copilot-chat.tsx:208-219 Inline component definition causes infinite re-render loop (root cause of the "Maximum update depth exceeded" error)

🟡 Minor (1) 🟡

Inline Comments:

  • 🟡 Minor: tool-approval.tsx:90-115 ApprovalButtons receives unstable approve prop (contributing factor)

🕐 Pending Recommendations (2)

Items from prior reviews that remain applicable to unchanged code:

  • 💭 copilot-chat.tsx:209-216 Component invoked as function instead of JSX (now addressed by the delta, but replaced with another anti-pattern)
  • 💭 message.tsx File naming no longer reflects content

🚫 REQUEST CHANGES

Summary: This PR introduces a critical regression reported by @sarah-inkeep: a "Maximum update depth exceeded" React infinite re-render bug that does not occur on the main branch. The root cause is the inline IkpTool wrapper function defined inside the components prop object in copilot-chat.tsx. Every render of CopilotChat creates a new function reference, causing React to unmount and remount the ToolApproval subtree, which triggers its useEffectsetLoading(true) → parent re-renders → infinite loop.

To fix: Memoize the IkpTool wrapper component using useMemo and ensure handleOAuthLogin has a stable reference by wrapping the onFinish callback in useCallback. See the inline comment on copilot-chat.tsx:208-219 for the complete fix.

The overall refactor goal (delegating to the widget package) is good — this just needs the memoization fix to avoid the re-render cascade.

Discarded (1)
Location Issue Reason Discarded
tool-approval.tsx:96 approveIcon = <CheckIcon /> creates new element each render While technically true, this is a common pattern and not the cause of the infinite loop; the component receives this as a default prop, not in a dependency array
Reviewers (2)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
pr-review-frontend 2 0 0 0 2 0 0
pr-review-standards 1 0 0 0 1 0 1
Total 3 0 0 0 2 2 1

Note: Both reviewers converged on the same root cause (inline component definition). The findings were deduplicated into a single critical inline comment with the comprehensive fix.

Comment on lines 208 to +219
components: {
...(IkpMessage
? {
IkpMessage: (props: any) =>
IkpMessage({
...props,
copilotAgentId: PUBLIC_INKEEP_COPILOT_AGENT_ID,
copilotProjectId: PUBLIC_INKEEP_COPILOT_PROJECT_ID,
copilotTenantId: PUBLIC_INKEEP_COPILOT_TENANT_ID,
apiUrl: PUBLIC_INKEEP_AGENTS_API_URL,
targetTenantId: tenantId,
targetProjectId: projectId,
targetAgentId: agentId,
onOAuthLogin: handleOAuthLogin,
refreshAgentGraph: refreshAgentGraph,
cookieHeader: cookieHeader,
copilotToken: copilotToken,
}),
}
: {}),
IkpTool(props) {
return (
<IkpTool
{...props}
targetTenantId={tenantId}
targetProjectId={projectId}
onOAuthLogin={handleOAuthLogin}
refreshAgentGraph={refreshAgentGraph}
/>
);
},
Copy link
Contributor

Choose a reason for hiding this comment

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

🔴 CRITICAL React infinite re-render: Inline component definition causes unmount/remount loop

Issue: The IkpTool wrapper function is defined inline inside the components prop object. Every render of CopilotChat creates a new function reference, which React interprets as a completely new component type.

Why: When ToolApproval calls setLoading(true) in its useEffect, it can propagate back up, causing CopilotChat to re-render. This creates a new wrapper function reference, causing React to unmount and remount the entire IkpTool subtree, which triggers the useEffect again — creating an infinite loop. This matches the "Maximum update depth exceeded" error reported by @sarah-inkeep.

Fix: Memoize the component wrapper and ensure stable callback references:

// Step 1: At line ~49, wrap onFinish in useCallback to stabilize handleOAuthLogin:
const onOAuthFinish = useCallback(() => {
  refreshAgentGraph({ fetchTools: true });
}, [refreshAgentGraph]);

const { handleOAuthLogin } = useOAuthLogin({
  tenantId,
  projectId,
  onFinish: onOAuthFinish,
});

// Step 2: Before the return statement, create a stable component:
const IkpToolComponent = useMemo(
  () =>
    function IkpToolWrapper(props: React.ComponentProps<typeof IkpTool>) {
      return (
        <IkpTool
          {...props}
          targetTenantId={tenantId}
          targetProjectId={projectId}
          onOAuthLogin={handleOAuthLogin}
          refreshAgentGraph={refreshAgentGraph}
        />
      );
    },
  [tenantId, projectId, handleOAuthLogin, refreshAgentGraph]
);

// Step 3: At line 208, use the stable reference:
components: {
  IkpTool: IkpToolComponent,
},

Also add useMemo and useCallback to the react import at the top of the file.

Refs:

Comment on lines +90 to +115
const ApprovalButtons = ({
state,
approve,
approveLabel = 'Approve',
approveVariant = 'default' as 'default' | 'destructive',
rejectLabel = 'Reject',
approveIcon = <CheckIcon className="size-3" />,
}: {
state: string;
approve: (approved?: boolean) => Promise<void>;
approveLabel?: string;
approveVariant?: 'default' | 'destructive' | 'destructive-outline';
rejectLabel?: string;
approveIcon?: React.ReactNode;
}) =>
state === 'approval-requested' && (
<div className="flex gap-2 justify-end">
<Button variant="outline" size="xs" type="button" onClick={() => approve(false)}>
{rejectLabel}
</Button>
<Button variant={approveVariant} size="xs" type="button" onClick={() => approve(true)}>
{approveIcon}
{approveLabel}
</Button>
</div>
);
Copy link
Contributor

Choose a reason for hiding this comment

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

🟡 Minor Consider: ApprovalButtons receives unstable approve prop

Issue: The refactoring moved ApprovalButtons from an inline function (closure over approve) to a module-level component receiving approve as a prop. The approve callback from the parent widget library may not be memoized, causing unnecessary re-renders.

Why: While this is not the primary cause of the infinite re-render (that's in copilot-chat.tsx), it's a contributing factor. When approve is an unstable reference and React compares props between renders, it sees a "change" even when there's no semantic difference.

Fix: After fixing the primary issue in copilot-chat.tsx, if re-renders are still excessive, consider one of these options:

Option A: Move ApprovalButtons back inside ToolApproval (after the early returns):

// Inside ToolApproval, after loading/error checks:
const ApprovalButtons = ({ approveLabel, ... }: { ... }) =>
  tool.state === 'approval-requested' && ( ... );

Option B: Wrap ApprovalButtons with React.memo():

const ApprovalButtons = React.memo(({ state, approve, ... }) => ...)

Refs:

@github-actions github-actions bot deleted a comment from claude bot Feb 18, 2026
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.

2 participants