feat(msteams): auto-inject parent message context for thread replies (#54932)#63945
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 193eed8031
ℹ️ 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".
| if (parentSummary && shouldInjectParentContext(route.sessionKey, activity.replyToId)) { | ||
| core.system.enqueueSystemEvent(formatParentContextEvent(parentSummary), { | ||
| sessionKey: route.sessionKey, | ||
| contextKey: `msteams:thread-parent:${conversationId}:${activity.replyToId}`, | ||
| }); |
There was a problem hiding this comment.
Gate parent-context injection by allowlist visibility
This new parent-context event is emitted as soon as parentSummary exists, but it does not apply the same sender visibility checks used for thread/quote context (contextVisibilityMode + allowlist via filterSupplementalContextItems). In channels configured with contextVisibility: "allowlist", a blocked parent author can now still inject Replying to @... text into system events, so unallowlisted content reaches the next prompt even though BodyForAgent correctly filters it out.
Useful? React with 👍 / 👎.
Greptile SummaryAdds parent-message context injection for Teams channel thread replies: a new
Confidence Score: 4/5Safe to merge after addressing the Promise.all coupling; the feature logic and test coverage are otherwise solid. One P1 defect: a transient Graph error on the parent-message fetch will also drop thread reply context due to Promise.all coupling. The remaining findings are P2 style issues. Score is 4 rather than lower because the failure mode is bounded (graceful degradation still applies, no data loss or crash) and is easily fixed. extensions/msteams/src/monitor-handler/message-handler.ts — the Promise.all block at lines 573–576
|
| const [parentMsg, replies] = await Promise.all([ | ||
| fetchChannelMessage(graphToken, groupId, conversationId, activity.replyToId), | ||
| fetchParentMessageCached(graphToken, groupId, conversationId, activity.replyToId), | ||
| fetchThreadReplies(graphToken, groupId, conversationId, activity.replyToId), | ||
| ]); |
There was a problem hiding this comment.
Promise.all couples parent-fetch failures to thread-context loss
If fetchParentMessageCached throws (transient Graph error, 429, etc.), Promise.all rejects immediately and the catch block discards both parentMsg and replies. Any thread history that fetchThreadReplies would have returned is silently lost, degrading the agent more than necessary. The two fetches are independent and should fail independently.
| const [parentMsg, replies] = await Promise.all([ | |
| fetchChannelMessage(graphToken, groupId, conversationId, activity.replyToId), | |
| fetchParentMessageCached(graphToken, groupId, conversationId, activity.replyToId), | |
| fetchThreadReplies(graphToken, groupId, conversationId, activity.replyToId), | |
| ]); | |
| const [parentResult, repliesResult] = await Promise.allSettled([ | |
| fetchParentMessageCached(graphToken, groupId, conversationId, activity.replyToId), | |
| fetchThreadReplies(graphToken, groupId, conversationId, activity.replyToId), | |
| ]); | |
| const parentMsg = | |
| parentResult.status === "fulfilled" ? parentResult.value : undefined; | |
| const replies = | |
| repliesResult.status === "fulfilled" ? repliesResult.value : []; |
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/msteams/src/monitor-handler/message-handler.ts
Line: 573-576
Comment:
**`Promise.all` couples parent-fetch failures to thread-context loss**
If `fetchParentMessageCached` throws (transient Graph error, 429, etc.), `Promise.all` rejects immediately and the `catch` block discards both `parentMsg` and `replies`. Any thread history that `fetchThreadReplies` would have returned is silently lost, degrading the agent more than necessary. The two fetches are independent and should fail independently.
```suggestion
const [parentResult, repliesResult] = await Promise.allSettled([
fetchParentMessageCached(graphToken, groupId, conversationId, activity.replyToId),
fetchThreadReplies(graphToken, groupId, conversationId, activity.replyToId),
]);
const parentMsg =
parentResult.status === "fulfilled" ? parentResult.value : undefined;
const replies =
repliesResult.status === "fulfilled" ? repliesResult.value : [];
```
How can I resolve this? If you propose a fix, please make it concise.| function buildInjectedKey(sessionKey: string): string { | ||
| return sessionKey; | ||
| } |
There was a problem hiding this comment.
buildInjectedKey is a no-op identity function
This helper just returns sessionKey unchanged and adds unnecessary indirection. Every call site should use sessionKey directly.
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/msteams/src/thread-parent-context.ts
Line: 64-66
Comment:
**`buildInjectedKey` is a no-op identity function**
This helper just returns `sessionKey` unchanged and adds unnecessary indirection. Every call site should use `sessionKey` directly.
How can I resolve this? If you propose a fix, please make it concise.…penclaw#54932) (openclaw#63945) * feat(msteams): auto-inject parent message context for thread replies (openclaw#54932) * msteams: use Promise.allSettled for thread context, remove no-op buildInjectedKey * fix(msteams): gate thread parent context by visibility --------- Co-authored-by: Brad Groux <3053586+BradGroux@users.noreply.github.com>
…penclaw#54932) (openclaw#63945) * feat(msteams): auto-inject parent message context for thread replies (openclaw#54932) * msteams: use Promise.allSettled for thread context, remove no-op buildInjectedKey * fix(msteams): gate thread parent context by visibility --------- Co-authored-by: Brad Groux <3053586+BradGroux@users.noreply.github.com>
…penclaw#54932) (openclaw#63945) * feat(msteams): auto-inject parent message context for thread replies (openclaw#54932) * msteams: use Promise.allSettled for thread context, remove no-op buildInjectedKey * fix(msteams): gate thread parent context by visibility --------- Co-authored-by: Brad Groux <3053586+BradGroux@users.noreply.github.com>
Summary
When a user replies to a message in a Teams channel thread, the bot now fetches the parent message via Graph and prepends it as context for the agent. This makes thread replies coherent — the agent knows what is being responded to.
What changed
/teams/{teamId}/channels/{channelId}/messages/{replyToId}Replying to @{sender}: {parentText}Builds on PR #62713 (per-thread session isolation): when a new thread session has no history, the parent context gives the agent immediate grounding.
Test plan
Closes #54932