Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/docs/content/docs/integrations/google_calendar.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ List events from Google Calendar.
| `pageToken` | string | No | Token for retrieving the next page of results |
| `orderBy` | string | No | Order of events: startTime \(chronological, the default\) or updated \(last-modified\). startTime is always valid here because singleEvents is set. |
| `timeZone` | string | No | IANA time zone used for times in the response \(e.g., America/New_York\). Defaults to the calendar's time zone. |
| `updatedMin` | string | No | Only return events modified at or after this time \(RFC3339 timestamp, e.g., 2025-06-03T00:00:00Z\) |
| `updatedMin` | string | No | Only return events modified at or after this time \(RFC3339 timestamp, e.g., 2025-06-03T00:00:00Z\). Events deleted since then are always included. |

#### Output

Expand Down
2 changes: 1 addition & 1 deletion apps/docs/content/docs/integrations/google_drive.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,7 @@ Search for files in Google Drive using advanced query syntax (e.g., fullText con
| `query` | string | Yes | Google Drive query string using advanced search syntax \(e.g., "fullText contains 'budget'", "mimeType = 'application/pdf'", "modifiedTime > '2024-01-01'"\) |
| `pageSize` | number | No | Maximum number of files to return \(default: 100\) |
| `pageToken` | string | No | Token for fetching the next page of results |
| `orderBy` | string | No | Comma-separated sort keys: createdTime, folder, modifiedByMeTime, modifiedTime, name, name_natural, quotaBytesUsed, recency, sharedWithMeTime, starred, viewedByMeTime. Append " desc" to reverse \(e.g., "modifiedTime desc"\) |
| `orderBy` | string | No | Comma-separated sort keys: createdTime, folder, modifiedByMeTime, modifiedTime, name, name_natural, quotaBytesUsed, recency, sharedWithMeTime, starred, viewedByMeTime. Append " desc" to reverse \(e.g., "modifiedTime desc"\). Ignored for fullText queries, which Drive always orders by relevance |
| `driveId` | string | No | ID of a shared drive to restrict results to \(default: search My Drive and all shared drives\) |

#### Output
Expand Down
17 changes: 16 additions & 1 deletion apps/sim/blocks/blocks/google_sheets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,17 @@ Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
{ label: 'User Entered (Parse formulas)', id: 'USER_ENTERED' },
{ label: "Raw (Don't parse formulas)", id: 'RAW' },
],
condition: { field: 'operation', value: ['write', 'update', 'append', 'batch_update'] },
condition: { field: 'operation', value: ['write', 'update', 'batch_update'] },
},
{
id: 'appendValueInputOption',
title: 'Value Input Option',
type: 'dropdown',
options: [
{ label: 'User Entered (Parse formulas)', id: 'USER_ENTERED' },
{ label: "Raw (Don't parse formulas)", id: 'RAW' },
],
condition: { field: 'operation', value: 'append' },
},
{
id: 'includeValuesInResponse',
Expand Down Expand Up @@ -937,6 +947,7 @@ Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
filterValue,
filterMatchType,
majorDimension,
appendValueInputOption,
...rest
} = params

Expand Down Expand Up @@ -1067,6 +1078,9 @@ Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
? { filterValue: filterValue as string }
: {}),
...(filterMatchType ? { filterMatchType: filterMatchType as string } : {}),
...(operation === 'append'
? { valueInputOption: appendValueInputOption || undefined }
Comment thread
waleedlatif1 marked this conversation as resolved.
Resolved
: {}),
}
},
},
Expand All @@ -1079,6 +1093,7 @@ Return ONLY the JSON array - no explanations, no markdown, no extra text.`,
cellRange: { type: 'string', description: 'Cell range (e.g., A1:D10)' },
values: { type: 'string', description: 'Cell values data' },
valueInputOption: { type: 'string', description: 'Value input option' },
appendValueInputOption: { type: 'string', description: 'Value input option for Append' },
insertDataOption: { type: 'string', description: 'Data insertion option' },
includeValuesInResponse: {
type: 'boolean',
Expand Down
13 changes: 13 additions & 0 deletions apps/sim/blocks/blocks/google_slides.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3479,6 +3479,19 @@ Return ONLY the text content - no explanations, no markdown formatting markers,
type: 'json',
description: 'Page properties from Get Page (pageBackgroundFill, colorScheme)',
},
layoutProperties: {
type: 'json',
description:
'Layout properties from Get Page, for layout pages (masterObjectId, name, displayName)',
},
notesProperties: {
type: 'json',
description: 'Notes properties from Get Page, for notes pages (speakerNotesObjectId)',
},
masterProperties: {
type: 'json',
description: 'Master properties from Get Page, for master pages (displayName)',
},
pageText: { type: 'string', description: 'Text on the page from Get Page' },
speakerNotes: { type: 'string', description: 'Speaker notes of the slide from Get Page' },
deleted: { type: 'boolean', description: 'Whether object was deleted' },
Expand Down
2 changes: 1 addition & 1 deletion apps/sim/tools/generated/tool-metadata.ts

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion apps/sim/tools/google_calendar/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export const listTool: ToolConfig<GoogleCalendarListParams, GoogleCalendarListRe
required: false,
visibility: 'user-or-llm',
description:
'Only return events modified at or after this time (RFC3339 timestamp, e.g., 2025-06-03T00:00:00Z)',
'Only return events modified at or after this time (RFC3339 timestamp, e.g., 2025-06-03T00:00:00Z). Events deleted since then are always included.',
},
},

Expand Down
31 changes: 23 additions & 8 deletions apps/sim/tools/google_calendar/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { generateId } from '@sim/utils/id'
import { isRecordLike } from '@sim/utils/object'
import type {
GoogleCalendarApiEventResponse,
GoogleCalendarEventDetails,
Expand Down Expand Up @@ -104,13 +105,14 @@ const MAX_REMINDER_OVERRIDES = 5
const MAX_REMINDER_MINUTES = 40320

/**
* Parse reminder overrides from a JSON string or array of `{ method, minutes }` objects.
* Returns `undefined` when nothing usable was provided. An empty array (`[]`) means
* "no reminders" and is returned as-is so callers can turn the calendar default off.
* Parse the reminders input into the Calendar `reminders` setting. Accepts a JSON string or
* value that is either an array of `{ method, minutes }` overrides (custom reminders; `[]`
* turns reminders off) or the API's own `{ useDefault, overrides }` object, so a previous
* event's reminders can be copied as-is. Returns `undefined` when nothing usable was provided.
*/
function parseReminderOverrides(
reminders: string | GoogleCalendarReminderOverride[] | undefined
): GoogleCalendarReminderOverride[] | undefined {
function parseReminders(
reminders: unknown
): { useDefault: boolean; overrides?: GoogleCalendarReminderOverride[] } | undefined {
if (reminders === undefined || reminders === null) return undefined

let parsed: unknown = reminders
Expand All @@ -124,6 +126,19 @@ function parseReminderOverrides(
}
}

if (isRecordLike(parsed) && !Array.isArray(parsed)) {
if (typeof parsed.useDefault !== 'boolean') {
throw new Error('Reminders object needs a boolean useDefault, like {"useDefault":true}')
}
if (parsed.useDefault) return { useDefault: true }
const overrides = parsed.overrides === undefined ? [] : parsed.overrides
return { useDefault: false, overrides: validateReminderOverrides(overrides) }
Comment thread
waleedlatif1 marked this conversation as resolved.
Resolved
}

return { useDefault: false, overrides: validateReminderOverrides(parsed) }
}

function validateReminderOverrides(parsed: unknown): GoogleCalendarReminderOverride[] {
if (!Array.isArray(parsed)) {
throw new Error('Reminders must be an array of { method, minutes } objects')
}
Expand Down Expand Up @@ -168,8 +183,8 @@ export function applyEventOptions(
if (params.transparency) body.transparency = params.transparency
const colorId = params.colorId?.toString().trim()
if (colorId) body.colorId = colorId
const overrides = parseReminderOverrides(params.reminders)
if (overrides) body.reminders = { useDefault: false, overrides }
const reminders = parseReminders(params.reminders)
if (reminders) body.reminders = reminders
}

/** Fields shared by every event-shaped output beyond the core id/summary/time/people fields. */
Expand Down
2 changes: 1 addition & 1 deletion apps/sim/tools/google_docs/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export function resolveTabId(tabId: unknown): string | undefined {
* string (agent tool-calls). Returns `undefined` when absent or not finite.
*/
export function parseOptionalNumber(value: unknown): number | undefined {
if (value === undefined || value === null) return undefined
if (value === undefined || value === null || typeof value === 'boolean') return undefined
if (typeof value === 'string' && value.trim() === '') return undefined
const parsed = Number(value)
return Number.isFinite(parsed) ? parsed : undefined
Expand Down
5 changes: 3 additions & 2 deletions apps/sim/tools/google_drive/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export const searchTool: ToolConfig<GoogleDriveSearchParams, GoogleDriveSearchRe
required: false,
visibility: 'user-or-llm',
description:
'Comma-separated sort keys: createdTime, folder, modifiedByMeTime, modifiedTime, name, name_natural, quotaBytesUsed, recency, sharedWithMeTime, starred, viewedByMeTime. Append " desc" to reverse (e.g., "modifiedTime desc")',
'Comma-separated sort keys: createdTime, folder, modifiedByMeTime, modifiedTime, name, name_natural, quotaBytesUsed, recency, sharedWithMeTime, starred, viewedByMeTime. Append " desc" to reverse (e.g., "modifiedTime desc"). Ignored for fullText queries, which Drive always orders by relevance',
},
driveId: {
type: 'string',
Expand All @@ -84,7 +84,8 @@ export const searchTool: ToolConfig<GoogleDriveSearchParams, GoogleDriveSearchRe
url.searchParams.append('supportsAllDrives', 'true')
url.searchParams.append('includeItemsFromAllDrives', 'true')
const orderBy = params.orderBy?.trim()
if (orderBy) {
const queryWithoutLiterals = (params.query ?? '').replace(/'(?:[^'\\]|\\.)*'/g, "''")
if (orderBy && !/\bfullText\b/.test(queryWithoutLiterals)) {
url.searchParams.append('orderBy', orderBy)
}

Expand Down
11 changes: 8 additions & 3 deletions apps/sim/tools/google_sheets/read.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,11 @@ export const readTool: ToolConfig<GoogleSheetsToolParams, GoogleSheetsReadRespon
// Keep a generous column/row bound to avoid huge payloads
if (!params.range) {
const defaultRange = 'A1:Z1000'
return `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${encodeURIComponent(defaultRange)}`
return `https://sheets.googleapis.com/v4/spreadsheets/${safeUrlPathSegment(spreadsheetId, 'spreadsheetId')}/values/${encodeURIComponent(defaultRange)}`
}

// Otherwise, get values from the specified range
return `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values/${encodeURIComponent(params.range)}`
return `https://sheets.googleapis.com/v4/spreadsheets/${safeUrlPathSegment(spreadsheetId, 'spreadsheetId')}/values/${encodeURIComponent(params.range)}`
},
method: 'GET',
headers: (params) => {
Expand Down Expand Up @@ -193,7 +193,12 @@ export const readV2Tool: ToolConfig<GoogleSheetsV2ToolParams, GoogleSheetsV2Read
throw new Error('Sheet name is required')
}

if (params.majorDimension === 'COLUMNS' && params.filterColumn?.trim()) {
if (
params.majorDimension === 'COLUMNS' &&
params.filterColumn?.trim() &&
params.filterValue !== undefined &&
params.filterValue !== ''
Comment thread
waleedlatif1 marked this conversation as resolved.
Resolved
) {
throw new Error(
'Filtering requires majorDimension ROWS; remove majorDimension or the filter'
)
Expand Down
2 changes: 1 addition & 1 deletion apps/sim/tools/google_tasks/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ interface TaskLink {

/** Builds the `lists/{tasklist}` path segment, defaulting to the primary list. */
export function taskListPath(taskListId: string | undefined): string {
return `lists/${safeUrlPathSegment(taskListId || '@default', 'taskListId', { allowSeparators: true })}`
return `lists/${safeUrlPathSegment(String(taskListId ?? '').trim() || '@default', 'taskListId', { allowSeparators: true })}`
}

/** Builds the `lists/{tasklist}/tasks/{task}` path. */
Expand Down
1 change: 1 addition & 0 deletions apps/sim/triggers/googleforms/webhook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ export const googleFormsWebhookTrigger: TriggerConfig = {
type: 'string',
description:
'Respondent email address (only when the form collects emails and the forwarder script sends it)',
optional: true,
},
answers: { type: 'object', description: 'Normalized map of question -> answer' },
raw: { type: 'object', description: 'Original payload (when enabled)' },
Expand Down
Loading