[Bug] Single-member speak reply missing from group chat in v2.2.18-canary.10
Summary
After upgrading the macOS desktop app to v2.2.18-canary.10, an existing group conversation still does not show a member reply. The reply exists in stored messages and in the persisted desktop message cache.
A read-only investigation and offline replay using the exact v2.2.18-canary.10 conversation-flow source found that the reply remains in messageMap but is omitted from flatList, including its nested entries. A synthetic five-message example reproduces the omission without user interruption, compression, or stale operation metadata.
Environment and scope
- macOS desktop app: 2.2.18-canary.10 (installed package version checked)
- CLI used for read-only inspection: 0.0.54
- Offline test runtime: Node.js 26.0.0; TypeScript sources transpiled for local execution
- Source tag:
v2.2.18-canary.10 - Source SHA:
d6cfa75d931dc36885906d45a741d972a1c6447d - The affected conversation was created on September 11, before this upgrade. The user-visible symptom persists after upgrading.
- The currently loaded renderer/OTA revision has not been verified. This is an offline parser reproduction, not a newly generated live end-to-end reproduction on .10.
Observed message structure
user
└─ supervisor assistant with a speak call
├─ tool result
│ └─ supervisor follow-up
└─ member assistant replyThe member reply is parented to the supervisor assistant that issued the tool call, not to the tool-result message. The supervisor continues through the tool-result path.
Results from the actual persisted cache
The desktop cache snapshot was updated at 2026-09-14T01:20:24.513Z.
- The target member reply is present as a top-level input message, with 11,505 characters of content. It is not inside compressed history.
- The cache contains the actual
tool_call_idlinking the tool result to the supervisor call; no tool linkage was guessed or reconstructed for this test. - SuperJSON
Dateandundefinedannotations were restored before replay. - Both direct
parse(messages)and the .10 frontend pathreconcileAssistantToolLinks(messages)followed byparse(...)omit the target from the display list. reconcileAssistantToolLinkschanges zero entries in this cache.- Target in
messageMap: yes. - Target anywhere in
flatList, recursively including nested entries: no.
The cache has 58 top-level items including a compressed group, while the CLI storage listing has 102 messages. These counts represent different shapes and are not evidence that 44 messages were lost.
Minimal synthetic reproduction
Use the following input with parse from packages/conversation-flow/src/parse.ts at the SHA above. All identifiers and text below are synthetic.
[
{
"id": "user", "role": "user", "parentId": null,
"content": "user", "createdAt": 1, "updatedAt": 1,
"agentId": "supervisor"
},
{
"id": "supervisor-call", "role": "assistant", "parentId": "user",
"content": "supervisor-call", "createdAt": 2, "updatedAt": 2,
"agentId": "supervisor",
"tools": [{
"id": "call-speak", "type": "builtin",
"identifier": "lobe-group-management", "apiName": "speak",
"arguments": "{\"agentId\":\"member\"}",
"result_msg_id": "tool-result"
}]
},
{
"id": "tool-result", "role": "tool", "parentId": "supervisor-call",
"content": "Member started", "createdAt": 3, "updatedAt": 3,
"agentId": "supervisor", "tool_call_id": "call-speak"
},
{
"id": "member-reply", "role": "assistant", "parentId": "supervisor-call",
"content": "SYNTHETIC_MEMBER_REPLY", "createdAt": 4, "updatedAt": 4,
"agentId": "member", "metadata": {"orchestrationRole": "member"}
},
{
"id": "supervisor-followup", "role": "assistant", "parentId": "tool-result",
"content": "supervisor-followup", "createdAt": 5, "updatedAt": 5,
"agentId": "supervisor"
}
]Steps:
- Pass this array to
parsefrom the specified source revision. - Check
result.messageMap["member-reply"]. - Recursively inspect
result.flatListfor an object withid === "member-reply"; do not check only the top-level array.
Actual result: the index contains the member reply, but the display list does not. The output contains the user and a supervisor assistant group whose children include the supervisor call and follow-up, without the member reply.
Diagnostic controls tested offline:
| Input | Member reply in nested display list |
|---|---|
| Five-message fixture above | No |
| Change only the member agentId to supervisor | No |
| Change only the member parentId to tool-result | Yes |
These controls indicate a structural/traversal issue, not simply a different-agent identity filter. Reparenting was only tested in an offline copy; no production message relationships were modified, and reparenting historical data is not proposed as a workaround.
Relation to #18806 and #19250
Related: #18806 and its merged fix #19250.
The earlier six-message user-interruption fixture was also tested against the same .10 parser. Its interrupted user branch and subsequent assistant reply are retained. The single-member speak structure above still loses the member reply.
Therefore, this report does not claim that #19250 failed or that the current defect has already been proven to share the same root cause. It appears to be another omission scenario in the same conversation-flow area. Please merge or relate the issues as appropriate.
Candidate code path to inspect
src/store/chat/slices/message/actions/query.ts: reconciles incoming tool links and assigns the parsedflatListto display messages.packages/conversation-flow/src/transformation/FlatListBuilder.ts:continueAfterAssistantGroup,continueInterruptedAssistantGroup, and member/council handling.packages/conversation-flow/src/transformation/MessageCollector.ts: assistant/tool-chain collection.
The continuation logic follows the supervisor chain, while interruption recovery specifically handles user children under intermediate assistants. The missing member reply is an assistant child under such an intermediate call. This is a candidate explanation, not a reviewed patch or proof that only the parser needs changing.
Expected behavior / request
The member reply should remain accessible alongside the supervisor continuation.
- Is this assistant-parented group-member reply a supported message structure?
- If yes, please add this case to regression coverage and preserve the member reply in the display result.
- If not, please clarify the intended writer behavior and how existing conversations with this structure will remain readable.
A fix should preserve explicit branch/thread selection and avoid indiscriminately appending every hidden message. The member reply should appear once, the supervisor tool chain should remain intact, and the existing #18806 interruption test should continue to pass. Model-context behavior needs separate verification; display recovery alone does not establish context recovery.
Separate observations, not established causes
Two later speak calls returned Agent member(s) failed to start. The topic also retained running-operation metadata referencing an assistant placeholder absent from the current message listing. Their causes and causal relationship remain unverified and require runtime logs. They should not be assumed to explain why the earlier, already-persisted member reply is omitted from the display list.
Privacy and evidence availability
No original conversation content, real agent/topic/message identifiers, or private cache is attached here. The synthetic fixture above is intended for public reproduction.
After the unmodified-cache replay, local investigation files were consolidated into private LobeHub Documents and cleaned up. The retained cache is a structure-preserving reduced copy with long non-target text omitted; the target member reply remains complete. That reduced copy was replayed separately and produced the same omission. The retained evidence must not be represented as a byte-for-byte copy of the original cache. The original group conversations were not deleted or modified.
Source: lobehub/lobehub