fix: subagent hardening batch — eviction order, args-buffer scoping, field-based lookup, AIMOCK_MODE warning - #960
Merged
Merged
Conversation
…ght ones With more than _MAX_SESSIONS concurrent delegations the insertion-order eviction could drop an UNFINISHED session, whose next event then re-emitted SUBAGENT_STARTED. Eviction now prefers the oldest finished session and only falls back to the oldest in-flight one when none is finished (so the cap still bounds memory), never the session being registered. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…n RUN_STARTED The subagent TOOL_CALL_ARGS accumulation buffer was keyed by toolCallId alone, so two children reusing an id interleaved fragments into one buffer and neither ever parsed. Key it by subagentRunId + toolCallId. RUN_STARTED now also clears every args buffer, so a dangling fragment from an aborted run (TOOL_CALL_END never arrived) cannot prefix a same-id call next run. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…olCallId field The lookup used the subagents map KEY, but the neutral contract anchors a subagent on the `toolCallId` field (the key is an adapter detail — AG-UI keys activities by `<toolCallId>-sub`). Extract the lookup into resolveSubagentsByMessage(), which re-indexes the map by the field before resolving the message's tool-call ids, matching <chat-tool-calls>. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Only the exact string 'record' flips record mode; anything else (a typo like `Record` or `recording`) silently replayed against stale fixtures. Warn once with the offending value before falling back to replay. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Four small review-specified hardening fixes, one commit each (TDD: failing test → fix → green).
1. Strands emitter evicts finished sessions before in-flight ones
cockpit/runtimes/aws-strands/python/src/subagent_emitter.py— with more than_MAX_SESSIONS(512) concurrent delegations, insertion-order eviction could drop an UNFINISHED session, whose next event then re-emittedSUBAGENT_STARTED. Eviction now prefers the oldest finished session, falls back to the oldest in-flight one only when none is finished (the cap still bounds memory), and never evicts the session being registered. The generated copy underdeployments/ag-ui-devis regenerated in a separate commit.Tests:
uv run pytest -q→ 16 passed (2 new: preference test + no-finished fallback; the preference test failed before the fix).2. AG-UI adapter: subagent args buffers keyed by run id, cleared on RUN_STARTED
libs/ag-ui/src/lib/reducer.ts— theTOOL_CALL_ARGSaccumulation buffer for subagent events was keyed bytoolCallIdalone, so two children reusing an id interleaved fragments into one buffer and neither parsed. Now keyedsubagent:<subagentRunId>:<toolCallId>.RUN_STARTEDalso clears all args buffers so a dangling fragment from an aborted run (noTOOL_CALL_END) cannot prefix a same-id call in the next run.Tests:
npx vitest run→ 243 passed (2 new, both failed before the fix).3. LangGraph
getSubagentsByMessageresolves throughSubagent.toolCallIdlibs/langgraph/src/lib/agent.fn.ts— the lookup used the subagents map KEY; the neutral contract anchors on thetoolCallIdfield (same rule<chat-tool-calls>applies since #956). Lookup extracted intoresolveSubagentsByMessage()(test-only export, same precedent ascomputeMessageCheckpoints) which re-indexes by the field. Every LangGraph path keys by the id today, so the mismatch cannot be produced through the transport; the unit test drives the helper with a map keyedcall_x-sub/ fieldcall_xand returned[]before the change.Tests:
npx vitest run→ 359 passed (1 new; mutation check confirmed).4. Warn on unrecognized
AIMOCK_MODElibs/e2e-harness/src/aimock-mode.ts— only exact'record'flipped record mode; a typo likeRecordsilently replayed. Now warns once ([aimock-harness] unrecognized AIMOCK_MODE="Record" — falling back to replay) then replays.replayand unset do not warn.Tests:
npx vitest run→ 29 passed (2 new).Lint:
nx lint ag-ui | langgraph | e2e-harness— 0 errors.🤖 Generated with Claude Code