Skip to content

Commit 5826dd9

Browse files
committed
fix(connectors): index Google Chat spaces with no messages in the window
Review round 1. - orderBy takes a full ordering expression, not a bare direction. The reference documents the default as `createTime ASC`, so send `createTime DESC`; a bare `DESC` either 400s every hydration or is ignored, which would make the cap keep the oldest traffic and the later reverse render the transcript backwards. - getDocument no longer returns null when the message window is empty. A space with no messages is still a live space, and null is the "document is gone" signal the engine treats as last-known-good: returning it dropped spaces whose only prose is their description or guidelines, and left a stale transcript indexed after a space was cleared or lookbackDays was tightened past every message. - The transcript header is omitted when no message contributed text.
1 parent d06ef0d commit 5826dd9

2 files changed

Lines changed: 72 additions & 15 deletions

File tree

apps/sim/connectors/google-chat/google-chat.test.ts

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,20 +44,26 @@ const fetchMock = vi.fn<(input: string | URL | Request, init?: RequestInit) => P
4444
let listedSpaces: Record<string, unknown>[] = [SPACE]
4545
/** `nextPageToken` returned by `spaces.list`; per-test overridable. */
4646
let listNextPageToken: string | undefined
47+
/** Messages returned by `spaces.messages.list`; per-test overridable. */
48+
let listedMessages: Record<string, unknown>[] = MESSAGES
49+
/** Space returned by `spaces.list` / `spaces.get`; per-test overridable. */
50+
let fetchedSpace: Record<string, unknown> = SPACE
4751

4852
beforeEach(() => {
4953
requestedUrls.length = 0
5054
listedSpaces = [SPACE]
5155
listNextPageToken = undefined
56+
listedMessages = MESSAGES
57+
fetchedSpace = SPACE
5258
fetchMock.mockReset()
5359
fetchMock.mockImplementation(async (input) => {
5460
const url = String(input)
5561
requestedUrls.push(url)
56-
if (url.includes('/messages?')) return jsonResponse({ messages: MESSAGES })
62+
if (url.includes('/messages?')) return jsonResponse({ messages: listedMessages })
5763
if (url.includes('/spaces?')) {
5864
return jsonResponse({ spaces: listedSpaces, nextPageToken: listNextPageToken })
5965
}
60-
if (url.endsWith(`/${SPACE_NAME}`)) return jsonResponse(SPACE)
66+
if (url.endsWith(`/${SPACE_NAME}`)) return jsonResponse(fetchedSpace)
6167
return jsonResponse({ error: { message: 'not found' } }, 404)
6268
})
6369
vi.stubGlobal('fetch', fetchMock)
@@ -204,10 +210,50 @@ describe('google-chat listing caps', () => {
204210
})
205211
})
206212

213+
describe('google-chat empty windows', () => {
214+
it('indexes a space whose only prose is its description when no message has text', async () => {
215+
fetchedSpace = { ...SPACE, spaceDetails: { description: 'Release coordination' } }
216+
listedSpaces = [fetchedSpace]
217+
listedMessages = []
218+
219+
const doc = await googleChatConnector.getDocument('token', {}, SPACE_NAME)
220+
expect(doc).not.toBeNull()
221+
expect(doc?.content).toContain('Release coordination')
222+
})
223+
224+
it('indexes a space whose only prose is its guidelines when no message has text', async () => {
225+
fetchedSpace = { ...SPACE, spaceDetails: { guidelines: 'Be excellent to each other' } }
226+
listedSpaces = [fetchedSpace]
227+
listedMessages = []
228+
229+
const doc = await googleChatConnector.getDocument('token', {}, SPACE_NAME)
230+
expect(doc?.content).toContain('Be excellent to each other')
231+
})
232+
233+
it('returns a document rather than null when the window is empty, so a cleared space does not keep a stale transcript', async () => {
234+
listedMessages = []
235+
const doc = await googleChatConnector.getDocument('token', {}, SPACE_NAME)
236+
expect(doc).not.toBeNull()
237+
expect(doc?.content).not.toContain('Shipping today')
238+
expect(doc?.metadata?.messageCount).toBe(0)
239+
})
240+
241+
it('omits the transcript header entirely when no message contributed text', async () => {
242+
listedMessages = []
243+
const doc = await googleChatConnector.getDocument('token', {}, SPACE_NAME)
244+
expect(doc?.content).not.toContain('--- Messages ---')
245+
})
246+
247+
it('still returns null when the space itself is gone', async () => {
248+
const doc = await googleChatConnector.getDocument('token', {}, 'spaces/MISSING')
249+
expect(doc).toBeNull()
250+
})
251+
})
252+
207253
describe('google-chat message window', () => {
208254
it('requests messages newest-first so the cap keeps the most recent conversation', async () => {
209255
await googleChatConnector.getDocument('token', {}, SPACE_NAME)
210-
expect(messagesParams().get('orderBy')).toBe('DESC')
256+
expect(messagesParams().get('orderBy')).toBe('createTime DESC')
211257
})
212258

213259
it('renders the newest-first page back into chronological order', async () => {

apps/sim/connectors/google-chat/google-chat.ts

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -256,8 +256,12 @@ function rfc3339(date: Date): string {
256256

257257
/**
258258
* Fetches the newest `maxMessages` messages of a space, optionally bounded by a
259-
* lookback window. Messages are requested newest-first (`orderBy=DESC`) so the
260-
* cap keeps the most recent conversation, then returned in chronological order.
259+
* lookback window. Messages are requested newest-first so the cap keeps the most
260+
* recent conversation, then returned in chronological order.
261+
*
262+
* `orderBy` takes a full ordering expression, not a bare direction: the reference
263+
* documents the default as `createTime ASC` and lists ASC/DESC as the ordering
264+
* *operations* usable within one. `createTime` is the only orderable field here.
261265
*/
262266
async function fetchSpaceMessages(
263267
accessToken: string,
@@ -276,7 +280,7 @@ async function fetchSpaceMessages(
276280
while (collected.length < maxMessages) {
277281
const params = new URLSearchParams({
278282
pageSize: String(Math.min(MESSAGES_PAGE_SIZE, maxMessages - collected.length)),
279-
orderBy: 'DESC',
283+
orderBy: 'createTime DESC',
280284
})
281285
if (filter) params.set('filter', filter)
282286
if (pageToken) params.set('pageToken', pageToken)
@@ -326,14 +330,18 @@ function formatSpaceContent(space: Space, messages: ChatMessage[]): string {
326330
const guidelines = space.spaceDetails?.guidelines?.trim()
327331
if (guidelines) parts.push(`Guidelines: ${guidelines}`)
328332

329-
parts.push('')
330-
parts.push('--- Messages ---')
331-
333+
const lines: string[] = []
332334
for (const message of messages) {
333335
const text = message.text?.trim() || message.fallbackText?.trim()
334336
if (!text) continue
335337
const timestamp = message.createTime ?? ''
336-
parts.push(`[${timestamp}] ${senderLabel(message.sender)}: ${text}`)
338+
lines.push(`[${timestamp}] ${senderLabel(message.sender)}: ${text}`)
339+
}
340+
341+
if (lines.length > 0) {
342+
parts.push('')
343+
parts.push('--- Messages ---')
344+
parts.push(...lines)
337345
}
338346

339347
return parts.join('\n')
@@ -460,12 +468,15 @@ export const googleChatConnector: ConnectorConfig = {
460468
const lookbackDays = resolveLookbackDays(sourceConfig.lookbackDays)
461469
const messages = await fetchSpaceMessages(accessToken, spaceName, maxMessages, lookbackDays)
462470

471+
/**
472+
* A space with no messages in the window is still a live space, so it is
473+
* indexed rather than skipped. `null` is this connector's "document is gone"
474+
* signal, and the engine treats it as last-known-good: returning it here would
475+
* both drop spaces whose only prose is their description or guidelines, and
476+
* leave a previously indexed transcript in place after the space was cleared
477+
* or `lookbackDays` was tightened past every message.
478+
*/
463479
const messageCount = countIndexedMessages(messages)
464-
if (messageCount === 0) {
465-
logger.info('No indexable messages in Google Chat space', { externalId })
466-
return null
467-
}
468-
469480
const stub = spaceToStub(space, maxMessages, lookbackDays, syncContext)
470481

471482
return {

0 commit comments

Comments
 (0)