fix: address PR bugfix review for a2a discovery + metadata forwarding

- agent create form (add_agent_form.tsx): drop the skills.length > 0
  guard so an admin can clear all discovered skills during creation,
  matching the edit form's overlay behavior (consistency between
  create and edit flows).

- agent_card_discovery.tsx: stop including savedAgentCard in the
  handleDiscover useCallback deps. Read it via a ref inside
  resetSelections instead, so a parent-driven re-render that hands us
  a new savedAgentCard object reference (e.g. a background refresh of
  the agent record) does not recreate handleDiscover and re-fire the
  auto-discover effect, which would otherwise overwrite in-progress
  user edits in parent-driven mode (debounceMs = 0).

- a2a_endpoints.invoke_agent_a2a: skip 'metadata' when moving
  litellm params off of A2A MessageSendParams into body. The A2A
  protocol defines params.metadata as a first-class request-level
  field, and the completion bridge's get_forward_metadata is supposed
  to merge it with message.metadata. Previously the proxy always
  stripped params.metadata before constructing MessageSendParams, so
  the params-level branch in get_forward_metadata was dead code in
  the proxy flow.

Co-authored-by: Yassin Kortam <yassin@berri.ai>
This commit is contained in:
Cursor Agent 2026-05-28 04:16:11 +00:00
parent c1eb1ebcdd
commit 59e36bcfb4
No known key found for this signature in database
3 changed files with 34 additions and 17 deletions

View file

@ -332,9 +332,14 @@ async def invoke_agent_a2a( # noqa: PLR0915
if params:
# extract any litellm params from the params - eg. 'guardrails'
# ``metadata`` is intentionally excluded: it's a first-class A2A
# ``MessageSendParams`` field that the completion bridge forwards
# downstream via ``get_forward_metadata``. Stripping it here would
# collide with litellm's spend-tracking ``metadata`` kwarg and
# silently drop the caller's A2A request-level metadata.
params_to_remove = []
for key, value in params.items():
if key in all_litellm_params:
if key in all_litellm_params and key != "metadata":
params_to_remove.append(key)
body[key] = value
for key in params_to_remove:

View file

@ -254,8 +254,9 @@ const AddAgentForm: React.FC<AddAgentFormProps> = ({
const discovered = appliedDiscoveredSelection.selected_card;
agentData.agent_card_params = {
...agentData.agent_card_params,
...(Array.isArray(discovered.skills) &&
discovered.skills.length > 0 && { skills: discovered.skills }),
...(Array.isArray(discovered.skills) && {
skills: discovered.skills,
}),
...(discovered.capabilities && {
capabilities: discovered.capabilities,
}),

View file

@ -96,16 +96,6 @@ const AgentCardDiscovery: React.FC<AgentCardDiscoveryProps> = ({
Record<string, boolean>
>({});
const resetSelections = (fresh: DiscoveredAgentCard) => {
const initial = savedAgentCard
? selectionsFromSavedAgentCard(fresh, savedAgentCard)
: selectionsFromUpstreamCard(fresh);
setEditedName(initial.editedName);
setEditedDescription(initial.editedDescription);
setSelectedSkillIds(initial.selectedSkillIds);
setSelectedCapabilities(initial.selectedCapabilities);
};
const onApplyRef = useRef(onApply);
onApplyRef.current = onApply;
const discoverRequestIdRef = useRef(0);
@ -118,6 +108,25 @@ const AgentCardDiscovery: React.FC<AgentCardDiscoveryProps> = ({
// the callback / effect only re-run when content actually changes.
const discoveryRequestRef = useRef(discoveryRequest);
discoveryRequestRef.current = discoveryRequest;
// Hold ``savedAgentCard`` in a ref so ``resetSelections`` always sees the
// latest value without making it a dependency of ``handleDiscover``.
// Putting ``savedAgentCard`` directly in the callback deps means any parent
// re-render that hands us a new object reference (e.g. a background
// agent-data refresh during editing) recreates ``handleDiscover``, which
// re-fires the auto-discover effect and overwrites in-progress user edits.
const savedAgentCardRef = useRef(savedAgentCard);
savedAgentCardRef.current = savedAgentCard;
const resetSelections = (fresh: DiscoveredAgentCard) => {
const saved = savedAgentCardRef.current;
const initial = saved
? selectionsFromSavedAgentCard(fresh, saved)
: selectionsFromUpstreamCard(fresh);
setEditedName(initial.editedName);
setEditedDescription(initial.editedDescription);
setSelectedSkillIds(initial.selectedSkillIds);
setSelectedCapabilities(initial.selectedCapabilities);
};
const discoveryMode = discoveryRequest?.discovery_mode;
const discoveryParamsKey = useMemo(
@ -176,9 +185,12 @@ const AgentCardDiscovery: React.FC<AgentCardDiscoveryProps> = ({
// ``discoveryMode`` / ``discoveryParamsKey`` are primitive proxies for
// ``discoveryRequest`` content; the actual object is read via the ref
// above so identity churn from the parent doesn't recreate this callback.
// ``savedAgentCard`` is captured indirectly via ``resetSelections``: if
// the parent refetches the agent and hands us a new saved card, we need
// a fresh callback so "Re-discover" pre-selects against the latest data.
// ``savedAgentCard`` is intentionally NOT a dep — it's read via
// ``savedAgentCardRef`` inside ``resetSelections``. Including it here
// would recreate this callback whenever the parent hands us a new
// ``savedAgentCard`` object (e.g. a background refresh of agent data
// during editing), which would re-fire the auto-discover effect and
// wipe in-progress user selections.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
accessToken,
@ -186,7 +198,6 @@ const AgentCardDiscovery: React.FC<AgentCardDiscoveryProps> = ({
isParentDriven,
discoveryMode,
discoveryParamsKey,
savedAgentCard,
]);
// Auto-discover when the URL (or parent plan) becomes available.