feat(googlechat): add thread parent context for thread replies

- Add getThreadParentMessage() to fetch root message of thread
- Include ThreadParentText in agent context
- Thread parent text included in message body for visibility

Completes thread context support for Google Chat
This commit is contained in:
root 2026-01-29 12:59:35 +00:00
parent 149ad09832
commit c544acdcbe
2 changed files with 49 additions and 0 deletions

View File

@ -274,3 +274,32 @@ export async function getGoogleChatMessage(params: {
return null;
}
}
export async function getThreadParentMessage(params: {
account: ResolvedGoogleChatAccount;
threadResourceName: string;
}): Promise<{ text?: string; name?: string } | null> {
const { account, threadResourceName } = params;
// The parent message ID is often the same as the thread ID
// Thread: spaces/XXX/threads/YYY -> Message: spaces/XXX/messages/YYY.YYY
const match = threadResourceName.match(/^(.+)\/threads\/(.+)$/);
if (!match) return null;
const [, spaceId, threadId] = match;
// Try common message name patterns
const possibleMessageNames = [
`${spaceId}/messages/${threadId}.${threadId}`,
`${spaceId}/messages/${threadId}`,
];
for (const messageName of possibleMessageNames) {
try {
const result = await getGoogleChatMessage({ account, messageName });
if (result) return result;
} catch {
// Try next pattern
}
}
return null;
}

View File

@ -12,6 +12,7 @@ import {
sendGoogleChatMessage,
updateGoogleChatMessage,
getGoogleChatMessage,
getThreadParentMessage,
} from "./api.js";
import { verifyGoogleChatRequest, type GoogleChatAudienceType } from "./auth.js";
import { getGoogleChatRuntime } from "./runtime.js";
@ -615,6 +616,24 @@ async function processMessageWithPipeline(params: {
}
}
// Fetch thread parent message for thread replies
let threadParentText: string | undefined;
if (message.threadReply && message.thread?.name) {
try {
const parentMsg = await getThreadParentMessage({
account,
threadResourceName: message.thread.name,
});
threadParentText = parentMsg?.text;
} catch {
// Ignore fetch errors
}
}
// Include thread parent context in rawBody for visibility
if (threadParentText && !quotedMessageText) {
rawBody = `[THREAD PARENT: "${threadParentText.substring(0, 200)}${threadParentText.length > 200 ? '...' : ''}"] ${rawBody}`;
}
const previousTimestamp = core.channel.session.readSessionUpdatedAt({
storePath,
sessionKey: route.sessionKey,
@ -662,6 +681,7 @@ async function processMessageWithPipeline(params: {
IsThreadReply: message.threadReply,
QuotedMessageId: message.quotedMessageMetadata?.name,
QuotedMessageText: quotedMessageText,
ThreadParentText: threadParentText,
});
void core.channel.session