diff --git a/extensions/googlechat/src/api.ts b/extensions/googlechat/src/api.ts index 7757ad0fb..909a29f71 100644 --- a/extensions/googlechat/src/api.ts +++ b/extensions/googlechat/src/api.ts @@ -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; +} diff --git a/extensions/googlechat/src/monitor.ts b/extensions/googlechat/src/monitor.ts index 836012106..7f2172738 100644 --- a/extensions/googlechat/src/monitor.ts +++ b/extensions/googlechat/src/monitor.ts @@ -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