Skip to content

🐛 fix(hetero-agent): keep parentId chain across toolless middle steps#14839

Closed
arvinxx wants to merge 1 commit into
canaryfrom
fix/cc-monitor-toolless-step-parent-chain
Closed

🐛 fix(hetero-agent): keep parentId chain across toolless middle steps#14839
arvinxx wants to merge 1 commit into
canaryfrom
fix/cc-monitor-toolless-step-parent-chain

Conversation

@arvinxx

@arvinxx arvinxx commented May 15, 2026

Copy link
Copy Markdown
Member

💻 Change Type

  • 🐛 fix

🔗 Related Issue

Fixes LOBE-8993
Related to LOBE-7365

🔀 Description of Change

When a Claude Code step produced only text (no tool_use) — for example
Monitor pushing stdout that drove Claude to reply "等 list 完。" without
invoking a tool — the next step's parentId fell back to the previous
assistant message. MessageCollector.collectAssistantChain only walks
the assistant → tool → assistant zigzag, so each Monitor stdout line
split off into its own bubble, every one re-rendering the model label.

The executor tracks tool persistence in toolState.payloads, which is
cleared on every step boundary. So when a step had no tools,
lastToolMsgId came back empty and stepParentId defaulted to
currentAssistantMessageId (the toolless assistant) — producing an
assistant → assistant link that the UI couldn't aggregate.

Fix: introduce a run-scoped lastToolMsgIdEver tracker that survives
step boundaries (resets only when a new executor spawns, i.e. on a new
user message). Step boundary now resolves stepParentId in this order:

  1. lastToolMsgId — last tool in this step (normal zigzag)
  2. lastToolMsgIdEver — last tool ever seen in the run (rescues
    consecutive toolless middle steps)
  3. currentAssistantMessageId — pure-text run with no tools at all

The reader (MessageCollector) is untouched; the semantics it expects
are now produced correctly on the writer side.

🧪 How to Test

  • Tested locally
  • Added/updated tests
  • No tests needed

Test changes in heterogeneousAgentExecutor.test.ts:

  • Flipped LOBE-7365 Monitor parentId chain > toolless middle step
    asserts Step 2's parentId is the Step 0 Monitor tool (tool-1),
    not the toolless Step 1 assistant.
  • Added LOBE-7365 Monitor parentId chain > consecutive toolless steps
    — replays 3 consecutive toolless text-only steps after a Monitor
    tool_use; asserts all 4 follow-up assistants chain back to tool-1.
  • Existing should fall back to assistant parentId when step has no tools still passes (covers the pure-text run case where the
    fallback is currentAssistantMessageId).

Run:

bunx vitest run --silent='passed-only' src/store/chat/slices/aiChat/actions/__tests__/heterogeneousAgentExecutor.test.ts

53/53 passing.

📝 Additional Information

Impact: all Claude Code desktop sessions that use long-running
Monitor (and similar streaming tools — tail -f, CI watch, migration
progress loops). Beyond UI bubble fragmentation, the broken
assistant → assistant chain could mislead findLastMessageId and
session resume parent-pointer logic.

🤖 Generated with Claude Code

… steps

LOBE-8993: when a CC step produced only text (e.g. Monitor stdout drove
Claude to reply without invoking a tool), the next step's parentId fell
back to the previous assistant. MessageCollector only walks the
assistant → tool → assistant zigzag, so each Monitor stdout line split
into its own bubble.

Carry the most recent tool result_msg_id across step boundaries via a
`lastToolMsgIdEver` tracker so toolless middle steps still chain back to
the originating tool result.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented May 15, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
lobehub Ready Ready Preview, Comment May 15, 2026 10:56am

Request Review

@sourcery-ai sourcery-ai Bot left a comment

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.

Sorry @arvinxx, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 69d42c6c28

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// Prefer this step's last tool, then the most recent tool ever seen
// in the run (rescues toolless middle steps — see LOBE-8993), then
// the previous assistant as a last resort.
const stepParentId = lastToolMsgId ?? lastToolMsgIdEver ?? currentAssistantMessageId;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve a linear chain for toolless steps

When two or more text-only steps follow the same tool, this fallback assigns every new assistant the same lastToolMsgIdEver parent, making them sibling children of that tool. MessageCollector.collectAssistantChain only follows the first assistant child of each tool and then returns, so the later Monitor replies (and the subsequent tool-using assistant) still render as separate bubbles/branches instead of one chain. The new consecutive-toolless test asserts the duplicated parent IDs but does not exercise the collector behavior that this change is meant to fix.

Useful? React with 👍 / 👎.

@dosubot dosubot Bot added the size:S This PR changes 10-29 lines, ignoring generated files. label May 15, 2026
@codecov

codecov Bot commented May 15, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 65.88%. Comparing base (59d2915) to head (69d42c6).
⚠️ Report is 3 commits behind head on canary.

Additional details and impacted files
@@             Coverage Diff             @@
##           canary   #14839       +/-   ##
===========================================
- Coverage   82.74%   65.88%   -16.86%     
===========================================
  Files         771     2958     +2187     
  Lines       61950   261203   +199253     
  Branches     9492    31714    +22222     
===========================================
+ Hits        51261   172100   +120839     
- Misses      10531    88945    +78414     
  Partials      158      158               
Flag Coverage Δ
app 60.65% <100.00%> (?)
database 91.96% <ø> (ø)
packages/agent-runtime 80.37% <ø> (ø)
packages/builtin-tool-lobe-agent 18.33% <ø> (ø)
packages/context-engine 83.93% <ø> (ø)
packages/conversation-flow 92.43% <ø> (ø)
packages/file-loaders 87.60% <ø> (ø)
packages/memory-user-memory 75.01% <ø> (ø)
packages/model-bank 99.94% <ø> (ø)
packages/model-runtime 83.88% <ø> (ø)
packages/prompts 70.39% <ø> (ø)
packages/python-interpreter 92.90% <ø> (ø)
packages/ssrf-safe-fetch 0.00% <ø> (ø)
packages/types 12.69% <ø> (ø)
packages/utils 88.02% <ø> (ø)
packages/web-crawler 87.74% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
Store 67.15% <100.00%> (∅)
Services 53.78% <ø> (∅)
Server 71.81% <ø> (∅)
Libs 56.16% <ø> (∅)
Utils 82.65% <ø> (-10.82%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S This PR changes 10-29 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant