From 657f11792c7f1af5418921c11f5b6fbbdd2b5bdb Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Wed, 25 Feb 2026 12:09:12 -0800 Subject: [PATCH 1/7] feat(google-tasks): add Google Tasks integration --- apps/docs/components/icons.tsx | 19 ++ apps/docs/components/ui/icon-mapping.ts | 14 +- .../content/docs/en/tools/google_tasks.mdx | 187 ++++++++++++++ apps/docs/content/docs/en/tools/meta.json | 3 +- .../components/oauth-required-modal.tsx | 1 + apps/sim/blocks/blocks/google_tasks.ts | 240 ++++++++++++++++++ apps/sim/blocks/registry.ts | 2 + apps/sim/components/icons.tsx | 19 ++ apps/sim/lib/auth/auth.ts | 41 +++ apps/sim/lib/oauth/oauth.ts | 9 + apps/sim/lib/oauth/types.ts | 2 + apps/sim/tools/google_tasks/create.ts | 132 ++++++++++ apps/sim/tools/google_tasks/delete.ts | 67 +++++ apps/sim/tools/google_tasks/get.ts | 88 +++++++ apps/sim/tools/google_tasks/index.ts | 6 + .../sim/tools/google_tasks/list-task-lists.ts | 88 +++++++ apps/sim/tools/google_tasks/list.ts | 156 ++++++++++++ apps/sim/tools/google_tasks/types.ts | 108 ++++++++ apps/sim/tools/google_tasks/update.ts | 121 +++++++++ apps/sim/tools/registry.ts | 14 + 20 files changed, 1310 insertions(+), 7 deletions(-) create mode 100644 apps/docs/content/docs/en/tools/google_tasks.mdx create mode 100644 apps/sim/blocks/blocks/google_tasks.ts create mode 100644 apps/sim/tools/google_tasks/create.ts create mode 100644 apps/sim/tools/google_tasks/delete.ts create mode 100644 apps/sim/tools/google_tasks/get.ts create mode 100644 apps/sim/tools/google_tasks/index.ts create mode 100644 apps/sim/tools/google_tasks/list-task-lists.ts create mode 100644 apps/sim/tools/google_tasks/list.ts create mode 100644 apps/sim/tools/google_tasks/types.ts create mode 100644 apps/sim/tools/google_tasks/update.ts diff --git a/apps/docs/components/icons.tsx b/apps/docs/components/icons.tsx index 59632354c4..2b389603dd 100644 --- a/apps/docs/components/icons.tsx +++ b/apps/docs/components/icons.tsx @@ -1302,6 +1302,25 @@ export function GoogleCalendarIcon(props: SVGProps) { ) } +export function GoogleTasksIcon(props: SVGProps) { + return ( + + + + + ) +} + export function SupabaseIcon(props: SVGProps) { const id = useId() const gradient0 = `supabase_paint0_${id}` diff --git a/apps/docs/components/ui/icon-mapping.ts b/apps/docs/components/ui/icon-mapping.ts index f0a0e0a454..1a7c9ebb55 100644 --- a/apps/docs/components/ui/icon-mapping.ts +++ b/apps/docs/components/ui/icon-mapping.ts @@ -38,8 +38,8 @@ import { EyeIcon, FirecrawlIcon, FirefliesIcon, - GithubIcon, GitLabIcon, + GithubIcon, GmailIcon, GongIcon, GoogleBigQueryIcon, @@ -53,6 +53,7 @@ import { GoogleMapsIcon, GoogleSheetsIcon, GoogleSlidesIcon, + GoogleTasksIcon, GoogleTranslateIcon, GoogleVaultIcon, GrafanaIcon, @@ -74,9 +75,9 @@ import { LinearIcon, LinkedInIcon, LinkupIcon, + MailServerIcon, MailchimpIcon, MailgunIcon, - MailServerIcon, Mem0Icon, MicrosoftDataverseIcon, MicrosoftExcelIcon, @@ -109,6 +110,8 @@ import { ResendIcon, RevenueCatIcon, S3Icon, + SQSIcon, + STTIcon, SalesforceIcon, SearchIcon, SendgridIcon, @@ -120,19 +123,17 @@ import { SimilarwebIcon, SlackIcon, SmtpIcon, - SQSIcon, SshIcon, - STTIcon, StagehandIcon, StripeIcon, SupabaseIcon, + TTSIcon, TavilyIcon, TelegramIcon, TextractIcon, TinybirdIcon, TranslateIcon, TrelloIcon, - TTSIcon, TwilioIcon, TypeformIcon, UpstashIcon, @@ -143,11 +144,11 @@ import { WhatsAppIcon, WikipediaIcon, WordpressIcon, - xIcon, YouTubeIcon, ZendeskIcon, ZepIcon, ZoomIcon, + xIcon, } from '@/components/icons' type IconComponent = ComponentType> @@ -200,6 +201,7 @@ export const blockTypeToIconMap: Record = { google_search: GoogleIcon, google_sheets_v2: GoogleSheetsIcon, google_slides_v2: GoogleSlidesIcon, + google_tasks: GoogleTasksIcon, google_translate: GoogleTranslateIcon, google_vault: GoogleVaultIcon, grafana: GrafanaIcon, diff --git a/apps/docs/content/docs/en/tools/google_tasks.mdx b/apps/docs/content/docs/en/tools/google_tasks.mdx new file mode 100644 index 0000000000..3e953fa76f --- /dev/null +++ b/apps/docs/content/docs/en/tools/google_tasks.mdx @@ -0,0 +1,187 @@ +--- +title: Google Tasks +description: Manage Google Tasks +--- + +import { BlockInfoCard } from "@/components/ui/block-info-card" + + + +## Usage Instructions + +Integrate Google Tasks into your workflow. Create, read, update, delete, and list tasks and task lists. + + + +## Tools + +### `google_tasks_create` + +Create a new task in a Google Tasks list + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `taskListId` | string | No | Task list ID \(defaults to primary task list "@default"\) | +| `title` | string | Yes | Title of the task \(max 1024 characters\) | +| `notes` | string | No | Notes/description for the task \(max 8192 characters\) | +| `due` | string | No | Due date in RFC 3339 format \(e.g., 2025-06-03T00:00:00.000Z\) | +| `status` | string | No | Task status: "needsAction" or "completed" | +| `parent` | string | No | Parent task ID to create this task as a subtask. Omit for top-level tasks. | +| `previous` | string | No | Previous sibling task ID to position after. Omit to place first among siblings. | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Task ID | +| `title` | string | Task title | +| `notes` | string | Task notes | +| `status` | string | Task status \(needsAction or completed\) | +| `due` | string | Due date | +| `updated` | string | Last modification time | +| `selfLink` | string | URL for the task | +| `webViewLink` | string | Link to task in Google Tasks UI | +| `parent` | string | Parent task ID | +| `position` | string | Position among sibling tasks | +| `completed` | string | Completion date | +| `deleted` | boolean | Whether the task is deleted | + +### `google_tasks_list` + +List all tasks in a Google Tasks list + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `taskListId` | string | No | Task list ID \(defaults to primary task list "@default"\) | +| `maxResults` | number | No | Maximum number of tasks to return \(default 20, max 100\) | +| `pageToken` | string | No | Token for pagination | +| `showCompleted` | boolean | No | Whether to show completed tasks \(default true\) | +| `showDeleted` | boolean | No | Whether to show deleted tasks \(default false\) | +| `showHidden` | boolean | No | Whether to show hidden tasks \(default false\) | +| `dueMin` | string | No | Lower bound for due date filter \(RFC 3339 timestamp\) | +| `dueMax` | string | No | Upper bound for due date filter \(RFC 3339 timestamp\) | +| `completedMin` | string | No | Lower bound for task completion date \(RFC 3339 timestamp\) | +| `completedMax` | string | No | Upper bound for task completion date \(RFC 3339 timestamp\) | +| `updatedMin` | string | No | Lower bound for last modification time \(RFC 3339 timestamp\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `tasks` | json | Array of tasks with id, title, notes, status, due, updated, and more | +| `nextPageToken` | string | Token for retrieving the next page of results | + +### `google_tasks_get` + +Retrieve a specific task by ID from a Google Tasks list + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `taskListId` | string | No | Task list ID \(defaults to primary task list "@default"\) | +| `taskId` | string | Yes | The ID of the task to retrieve | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Task ID | +| `title` | string | Task title | +| `notes` | string | Task notes | +| `status` | string | Task status \(needsAction or completed\) | +| `due` | string | Due date | +| `updated` | string | Last modification time | +| `selfLink` | string | URL for the task | +| `webViewLink` | string | Link to task in Google Tasks UI | +| `parent` | string | Parent task ID | +| `position` | string | Position among sibling tasks | +| `completed` | string | Completion date | +| `deleted` | boolean | Whether the task is deleted | + +### `google_tasks_update` + +Update an existing task in a Google Tasks list + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `taskListId` | string | No | Task list ID \(defaults to primary task list "@default"\) | +| `taskId` | string | Yes | The ID of the task to update | +| `title` | string | No | New title for the task | +| `notes` | string | No | New notes for the task | +| `due` | string | No | New due date in RFC 3339 format | +| `status` | string | No | New status: "needsAction" or "completed" | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Task ID | +| `title` | string | Task title | +| `notes` | string | Task notes | +| `status` | string | Task status \(needsAction or completed\) | +| `due` | string | Due date | +| `updated` | string | Last modification time | +| `selfLink` | string | URL for the task | +| `webViewLink` | string | Link to task in Google Tasks UI | +| `parent` | string | Parent task ID | +| `position` | string | Position among sibling tasks | +| `completed` | string | Completion date | +| `deleted` | boolean | Whether the task is deleted | + +### `google_tasks_delete` + +Delete a task from a Google Tasks list + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `taskListId` | string | No | Task list ID \(defaults to primary task list "@default"\) | +| `taskId` | string | Yes | The ID of the task to delete | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `taskId` | string | Deleted task ID | +| `deleted` | boolean | Whether deletion was successful | + +### `google_tasks_list_task_lists` + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | string | Task ID | +| `title` | string | Task title | +| `notes` | string | Task notes | +| `status` | string | Task status | +| `due` | string | Due date | +| `updated` | string | Last modification time | +| `selfLink` | string | URL for the task | +| `webViewLink` | string | Link to task in Google Tasks UI | +| `parent` | string | Parent task ID | +| `position` | string | Position among sibling tasks | +| `completed` | string | Completion date | +| `deleted` | boolean | Whether the task is deleted | +| `tasks` | json | Array of tasks \(list operation\) | +| `taskLists` | json | Array of task lists \(list_task_lists operation\) | +| `taskId` | string | Deleted task ID \(delete operation\) | +| `nextPageToken` | string | Token for next page of results | + + diff --git a/apps/docs/content/docs/en/tools/meta.json b/apps/docs/content/docs/en/tools/meta.json index f305b90373..15c7d068f9 100644 --- a/apps/docs/content/docs/en/tools/meta.json +++ b/apps/docs/content/docs/en/tools/meta.json @@ -48,6 +48,7 @@ "google_search", "google_sheets", "google_slides", + "google_tasks", "google_translate", "google_vault", "grafana", @@ -148,4 +149,4 @@ "zep", "zoom" ] -} +} \ No newline at end of file diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal.tsx index fd00e34107..c8146a2801 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/credential-selector/components/oauth-required-modal.tsx @@ -40,6 +40,7 @@ const SCOPE_DESCRIPTIONS: Record = { 'https://www.googleapis.com/auth/drive.file': 'View and manage Google Drive files', 'https://www.googleapis.com/auth/drive': 'Access all Google Drive files', 'https://www.googleapis.com/auth/calendar': 'View and manage calendar', + 'https://www.googleapis.com/auth/tasks': 'Create, read, update, and delete Google Tasks', 'https://www.googleapis.com/auth/userinfo.email': 'View email address', 'https://www.googleapis.com/auth/userinfo.profile': 'View basic profile info', 'https://www.googleapis.com/auth/forms.body': 'View and manage Google Forms', diff --git a/apps/sim/blocks/blocks/google_tasks.ts b/apps/sim/blocks/blocks/google_tasks.ts new file mode 100644 index 0000000000..513ff29a35 --- /dev/null +++ b/apps/sim/blocks/blocks/google_tasks.ts @@ -0,0 +1,240 @@ +import { GoogleTasksIcon } from '@/components/icons' +import type { BlockConfig } from '@/blocks/types' +import { AuthMode } from '@/blocks/types' +import type { GoogleTasksResponse } from '@/tools/google_tasks/types' + +export const GoogleTasksBlock: BlockConfig = { + type: 'google_tasks', + name: 'Google Tasks', + description: 'Manage Google Tasks', + longDescription: + 'Integrate Google Tasks into your workflow. Create, read, update, delete, and list tasks and task lists.', + docsLink: 'https://docs.sim.ai/tools/google_tasks', + category: 'tools', + bgColor: '#E0E0E0', + icon: GoogleTasksIcon, + authMode: AuthMode.OAuth, + + subBlocks: [ + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + options: [ + { label: 'Create Task', id: 'create' }, + { label: 'List Tasks', id: 'list' }, + { label: 'Get Task', id: 'get' }, + { label: 'Update Task', id: 'update' }, + { label: 'Delete Task', id: 'delete' }, + { label: 'List Task Lists', id: 'list_task_lists' }, + ], + value: () => 'create', + }, + { + id: 'credential', + title: 'Google Tasks Account', + type: 'oauth-input', + canonicalParamId: 'oauthCredential', + mode: 'basic', + required: true, + serviceId: 'google-tasks', + requiredScopes: ['https://www.googleapis.com/auth/tasks'], + placeholder: 'Select Google Tasks account', + }, + { + id: 'manualCredential', + title: 'Google Tasks Account', + type: 'short-input', + canonicalParamId: 'oauthCredential', + mode: 'advanced', + placeholder: 'Enter credential ID', + required: true, + }, + + // Task List ID - shown for all task operations (not list_task_lists) + { + id: 'taskListId', + title: 'Task List ID', + type: 'short-input', + placeholder: 'Task list ID (leave empty for default list)', + condition: { field: 'operation', value: 'list_task_lists', not: true }, + }, + + // Create Task Fields + { + id: 'title', + title: 'Title', + type: 'short-input', + placeholder: 'Buy groceries', + condition: { field: 'operation', value: 'create' }, + required: { field: 'operation', value: 'create' }, + }, + { + id: 'notes', + title: 'Notes', + type: 'long-input', + placeholder: 'Task notes or description', + condition: { field: 'operation', value: 'create' }, + }, + { + id: 'due', + title: 'Due Date', + type: 'short-input', + placeholder: '2025-06-03T00:00:00.000Z', + condition: { field: 'operation', value: 'create' }, + }, + { + id: 'status', + title: 'Status', + type: 'dropdown', + condition: { field: 'operation', value: 'create' }, + options: [ + { label: 'Needs Action', id: 'needsAction' }, + { label: 'Completed', id: 'completed' }, + ], + }, + + // Get/Update/Delete Task Fields - Task ID + { + id: 'taskId', + title: 'Task ID', + type: 'short-input', + placeholder: 'Task ID', + condition: { field: 'operation', value: ['get', 'update', 'delete'] }, + required: { field: 'operation', value: ['get', 'update', 'delete'] }, + }, + + // Update Task Fields + { + id: 'title', + title: 'New Title', + type: 'short-input', + placeholder: 'Updated task title', + condition: { field: 'operation', value: 'update' }, + }, + { + id: 'notes', + title: 'New Notes', + type: 'long-input', + placeholder: 'Updated task notes', + condition: { field: 'operation', value: 'update' }, + }, + { + id: 'due', + title: 'New Due Date', + type: 'short-input', + placeholder: '2025-06-03T00:00:00.000Z', + condition: { field: 'operation', value: 'update' }, + }, + { + id: 'status', + title: 'New Status', + type: 'dropdown', + condition: { field: 'operation', value: 'update' }, + options: [ + { label: 'Needs Action', id: 'needsAction' }, + { label: 'Completed', id: 'completed' }, + ], + }, + + // List Tasks Fields + { + id: 'maxResults', + title: 'Max Results', + type: 'short-input', + placeholder: '20', + condition: { field: 'operation', value: ['list', 'list_task_lists'] }, + }, + { + id: 'showCompleted', + title: 'Show Completed', + type: 'dropdown', + condition: { field: 'operation', value: 'list' }, + options: [ + { label: 'Yes', id: 'true' }, + { label: 'No', id: 'false' }, + ], + }, + ], + + tools: { + access: [ + 'google_tasks_create', + 'google_tasks_list', + 'google_tasks_get', + 'google_tasks_update', + 'google_tasks_delete', + 'google_tasks_list_task_lists', + ], + config: { + tool: (params) => { + switch (params.operation) { + case 'create': + return 'google_tasks_create' + case 'list': + return 'google_tasks_list' + case 'get': + return 'google_tasks_get' + case 'update': + return 'google_tasks_update' + case 'delete': + return 'google_tasks_delete' + case 'list_task_lists': + return 'google_tasks_list_task_lists' + default: + throw new Error(`Invalid Google Tasks operation: ${params.operation}`) + } + }, + params: (params) => { + const { oauthCredential, operation, showCompleted, maxResults, ...rest } = params + + const processedParams: Record = { ...rest } + + if (maxResults && typeof maxResults === 'string') { + processedParams.maxResults = Number.parseInt(maxResults, 10) + } + + if (showCompleted !== undefined) { + processedParams.showCompleted = showCompleted === 'true' + } + + return { + oauthCredential, + ...processedParams, + } + }, + }, + }, + + inputs: { + operation: { type: 'string', description: 'Operation to perform' }, + oauthCredential: { type: 'string', description: 'Google Tasks access token' }, + taskListId: { type: 'string', description: 'Task list identifier' }, + title: { type: 'string', description: 'Task title' }, + notes: { type: 'string', description: 'Task notes' }, + due: { type: 'string', description: 'Task due date' }, + status: { type: 'string', description: 'Task status' }, + taskId: { type: 'string', description: 'Task identifier' }, + maxResults: { type: 'string', description: 'Maximum number of results' }, + showCompleted: { type: 'string', description: 'Whether to show completed tasks' }, + }, + + outputs: { + id: { type: 'string', description: 'Task ID' }, + title: { type: 'string', description: 'Task title' }, + notes: { type: 'string', description: 'Task notes' }, + status: { type: 'string', description: 'Task status' }, + due: { type: 'string', description: 'Due date' }, + updated: { type: 'string', description: 'Last modification time' }, + selfLink: { type: 'string', description: 'URL for the task' }, + webViewLink: { type: 'string', description: 'Link to task in Google Tasks UI' }, + parent: { type: 'string', description: 'Parent task ID' }, + position: { type: 'string', description: 'Position among sibling tasks' }, + completed: { type: 'string', description: 'Completion date' }, + deleted: { type: 'boolean', description: 'Whether the task is deleted' }, + tasks: { type: 'json', description: 'Array of tasks (list operation)' }, + taskLists: { type: 'json', description: 'Array of task lists (list_task_lists operation)' }, + taskId: { type: 'string', description: 'Deleted task ID (delete operation)' }, + nextPageToken: { type: 'string', description: 'Token for next page of results' }, + }, +} diff --git a/apps/sim/blocks/registry.ts b/apps/sim/blocks/registry.ts index 3cebd9de50..002f107c99 100644 --- a/apps/sim/blocks/registry.ts +++ b/apps/sim/blocks/registry.ts @@ -50,6 +50,7 @@ import { GoogleDocsBlock } from '@/blocks/blocks/google_docs' import { GoogleDriveBlock } from '@/blocks/blocks/google_drive' import { GoogleFormsBlock } from '@/blocks/blocks/google_forms' import { GoogleGroupsBlock } from '@/blocks/blocks/google_groups' +import { GoogleTasksBlock } from '@/blocks/blocks/google_tasks' import { GoogleMapsBlock } from '@/blocks/blocks/google_maps' import { GoogleSheetsBlock, GoogleSheetsV2Block } from '@/blocks/blocks/google_sheets' import { GoogleSlidesBlock, GoogleSlidesV2Block } from '@/blocks/blocks/google_slides' @@ -236,6 +237,7 @@ export const registry: Record = { google_forms: GoogleFormsBlock, google_groups: GoogleGroupsBlock, google_maps: GoogleMapsBlock, + google_tasks: GoogleTasksBlock, google_translate: GoogleTranslateBlock, gong: GongBlock, google_search: GoogleSearchBlock, diff --git a/apps/sim/components/icons.tsx b/apps/sim/components/icons.tsx index 59632354c4..2b389603dd 100644 --- a/apps/sim/components/icons.tsx +++ b/apps/sim/components/icons.tsx @@ -1302,6 +1302,25 @@ export function GoogleCalendarIcon(props: SVGProps) { ) } +export function GoogleTasksIcon(props: SVGProps) { + return ( + + + + + ) +} + export function SupabaseIcon(props: SVGProps) { const id = useId() const gradient0 = `supabase_paint0_${id}` diff --git a/apps/sim/lib/auth/auth.ts b/apps/sim/lib/auth/auth.ts index e33537c4df..4812c275ad 100644 --- a/apps/sim/lib/auth/auth.ts +++ b/apps/sim/lib/auth/auth.ts @@ -487,6 +487,7 @@ export const auth = betterAuth({ 'google-bigquery', 'google-vault', 'google-groups', + 'google-tasks', 'vertex-ai', 'github-repo', 'microsoft-dataverse', @@ -1191,6 +1192,46 @@ export const auth = betterAuth({ }, }, + { + providerId: 'google-tasks', + clientId: env.GOOGLE_CLIENT_ID as string, + clientSecret: env.GOOGLE_CLIENT_SECRET as string, + discoveryUrl: 'https://accounts.google.com/.well-known/openid-configuration', + accessType: 'offline', + scopes: [ + 'https://www.googleapis.com/auth/userinfo.email', + 'https://www.googleapis.com/auth/userinfo.profile', + 'https://www.googleapis.com/auth/tasks', + ], + prompt: 'consent', + redirectURI: `${getBaseUrl()}/api/auth/oauth2/callback/google-tasks`, + getUserInfo: async (tokens) => { + try { + const response = await fetch('https://openidconnect.googleapis.com/v1/userinfo', { + headers: { Authorization: `Bearer ${tokens.accessToken}` }, + }) + if (!response.ok) { + logger.error('Failed to fetch Google user info', { status: response.status }) + throw new Error(`Failed to fetch Google user info: ${response.statusText}`) + } + const profile = await response.json() + const now = new Date() + return { + id: `${profile.sub}-${crypto.randomUUID()}`, + name: profile.name || 'Google User', + email: profile.email, + image: profile.picture || undefined, + emailVerified: profile.email_verified || false, + createdAt: now, + updatedAt: now, + } + } catch (error) { + logger.error('Error in Google getUserInfo', { error }) + throw error + } + }, + }, + { providerId: 'vertex-ai', clientId: env.GOOGLE_CLIENT_ID as string, diff --git a/apps/sim/lib/oauth/oauth.ts b/apps/sim/lib/oauth/oauth.ts index c193ac1fac..2b9a96aca8 100644 --- a/apps/sim/lib/oauth/oauth.ts +++ b/apps/sim/lib/oauth/oauth.ts @@ -16,6 +16,7 @@ import { GoogleGroupsIcon, GoogleIcon, GoogleSheetsIcon, + GoogleTasksIcon, HubspotIcon, JiraIcon, LinearIcon, @@ -128,6 +129,14 @@ export const OAUTH_PROVIDERS: Record = { baseProviderIcon: GoogleIcon, scopes: ['https://www.googleapis.com/auth/bigquery'], }, + 'google-tasks': { + name: 'Google Tasks', + description: 'Create, manage, and organize tasks with Google Tasks.', + providerId: 'google-tasks', + icon: GoogleTasksIcon, + baseProviderIcon: GoogleIcon, + scopes: ['https://www.googleapis.com/auth/tasks'], + }, 'google-vault': { name: 'Google Vault', description: 'Search, export, and manage matters/holds via Google Vault.', diff --git a/apps/sim/lib/oauth/types.ts b/apps/sim/lib/oauth/types.ts index 70229738ab..0da86f06fd 100644 --- a/apps/sim/lib/oauth/types.ts +++ b/apps/sim/lib/oauth/types.ts @@ -8,6 +8,7 @@ export type OAuthProvider = | 'google-sheets' | 'google-calendar' | 'google-bigquery' + | 'google-tasks' | 'google-vault' | 'google-forms' | 'google-groups' @@ -54,6 +55,7 @@ export type OAuthService = | 'google-sheets' | 'google-calendar' | 'google-bigquery' + | 'google-tasks' | 'google-vault' | 'google-forms' | 'google-groups' diff --git a/apps/sim/tools/google_tasks/create.ts b/apps/sim/tools/google_tasks/create.ts new file mode 100644 index 0000000000..6cd9257eaa --- /dev/null +++ b/apps/sim/tools/google_tasks/create.ts @@ -0,0 +1,132 @@ +import type { ToolConfig } from '@/tools/types' +import { TASKS_API_BASE } from './types' +import type { GoogleTasksCreateParams, GoogleTasksResponse } from './types' + +export const createTool: ToolConfig = { + id: 'google_tasks_create', + name: 'Google Tasks Create Task', + description: 'Create a new task in a Google Tasks list', + version: '1.0.0', + + oauth: { + required: true, + provider: 'google-tasks', + }, + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Google Tasks OAuth access token', + }, + taskListId: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Task list ID (defaults to primary task list "@default")', + }, + title: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Title of the task (max 1024 characters)', + }, + notes: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Notes/description for the task (max 8192 characters)', + }, + due: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Due date in RFC 3339 format (e.g., 2025-06-03T00:00:00.000Z)', + }, + status: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Task status: "needsAction" or "completed"', + }, + parent: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Parent task ID to create this task as a subtask. Omit for top-level tasks.', + }, + previous: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Previous sibling task ID to position after. Omit to place first among siblings.', + }, + }, + + request: { + url: (params) => { + const taskListId = params.taskListId || '@default' + const queryParams = new URLSearchParams() + if (params.parent) queryParams.set('parent', params.parent) + if (params.previous) queryParams.set('previous', params.previous) + const qs = queryParams.toString() + return `${TASKS_API_BASE}/lists/${encodeURIComponent(taskListId)}/tasks${qs ? `?${qs}` : ''}` + }, + method: 'POST', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: Record = { + title: params.title, + } + if (params.notes) body.notes = params.notes + if (params.due) body.due = params.due + if (params.status) body.status = params.status + return body + }, + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + + if (!response.ok) { + throw new Error(data.error?.message ?? 'Failed to create task') + } + + return { + success: true, + output: { + id: data.id ?? null, + title: data.title ?? null, + notes: data.notes ?? null, + status: data.status ?? null, + due: data.due ?? null, + updated: data.updated ?? null, + selfLink: data.selfLink ?? null, + webViewLink: data.webViewLink ?? null, + parent: data.parent ?? null, + position: data.position ?? null, + completed: data.completed ?? null, + deleted: data.deleted ?? null, + }, + } + }, + + outputs: { + id: { type: 'string', description: 'Task ID' }, + title: { type: 'string', description: 'Task title' }, + notes: { type: 'string', description: 'Task notes', optional: true }, + status: { type: 'string', description: 'Task status (needsAction or completed)' }, + due: { type: 'string', description: 'Due date', optional: true }, + updated: { type: 'string', description: 'Last modification time' }, + selfLink: { type: 'string', description: 'URL for the task' }, + webViewLink: { type: 'string', description: 'Link to task in Google Tasks UI', optional: true }, + parent: { type: 'string', description: 'Parent task ID', optional: true }, + position: { type: 'string', description: 'Position among sibling tasks' }, + completed: { type: 'string', description: 'Completion date', optional: true }, + deleted: { type: 'boolean', description: 'Whether the task is deleted', optional: true }, + }, +} diff --git a/apps/sim/tools/google_tasks/delete.ts b/apps/sim/tools/google_tasks/delete.ts new file mode 100644 index 0000000000..c9c1df0ddf --- /dev/null +++ b/apps/sim/tools/google_tasks/delete.ts @@ -0,0 +1,67 @@ +import type { ToolConfig } from '@/tools/types' +import { TASKS_API_BASE } from './types' +import type { GoogleTasksDeleteParams, GoogleTasksDeleteResponse } from './types' + +export const deleteTool: ToolConfig = { + id: 'google_tasks_delete', + name: 'Google Tasks Delete Task', + description: 'Delete a task from a Google Tasks list', + version: '1.0.0', + + oauth: { + required: true, + provider: 'google-tasks', + }, + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Google Tasks OAuth access token', + }, + taskListId: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Task list ID (defaults to primary task list "@default")', + }, + taskId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the task to delete', + }, + }, + + request: { + url: (params) => { + const taskListId = params.taskListId || '@default' + return `${TASKS_API_BASE}/lists/${encodeURIComponent(taskListId)}/tasks/${encodeURIComponent(params.taskId)}` + }, + method: 'DELETE', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + }), + }, + + transformResponse: async (response: Response) => { + if (response.status === 204 || response.ok) { + return { + success: true, + output: { + taskId: '', + deleted: true, + }, + } + } + + const data = await response.json() + throw new Error(data.error?.message ?? 'Failed to delete task') + }, + + outputs: { + taskId: { type: 'string', description: 'Deleted task ID' }, + deleted: { type: 'boolean', description: 'Whether deletion was successful' }, + }, +} diff --git a/apps/sim/tools/google_tasks/get.ts b/apps/sim/tools/google_tasks/get.ts new file mode 100644 index 0000000000..047f8ce539 --- /dev/null +++ b/apps/sim/tools/google_tasks/get.ts @@ -0,0 +1,88 @@ +import type { ToolConfig } from '@/tools/types' +import { TASKS_API_BASE } from './types' +import type { GoogleTasksGetParams, GoogleTasksResponse } from './types' + +export const getTool: ToolConfig = { + id: 'google_tasks_get', + name: 'Google Tasks Get Task', + description: 'Retrieve a specific task by ID from a Google Tasks list', + version: '1.0.0', + + oauth: { + required: true, + provider: 'google-tasks', + }, + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Google Tasks OAuth access token', + }, + taskListId: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Task list ID (defaults to primary task list "@default")', + }, + taskId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the task to retrieve', + }, + }, + + request: { + url: (params) => { + const taskListId = params.taskListId || '@default' + return `${TASKS_API_BASE}/lists/${encodeURIComponent(taskListId)}/tasks/${encodeURIComponent(params.taskId)}` + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + + if (!response.ok) { + throw new Error(data.error?.message ?? 'Failed to get task') + } + + return { + success: true, + output: { + id: data.id ?? null, + title: data.title ?? null, + notes: data.notes ?? null, + status: data.status ?? null, + due: data.due ?? null, + updated: data.updated ?? null, + selfLink: data.selfLink ?? null, + webViewLink: data.webViewLink ?? null, + parent: data.parent ?? null, + position: data.position ?? null, + completed: data.completed ?? null, + deleted: data.deleted ?? null, + }, + } + }, + + outputs: { + id: { type: 'string', description: 'Task ID' }, + title: { type: 'string', description: 'Task title' }, + notes: { type: 'string', description: 'Task notes', optional: true }, + status: { type: 'string', description: 'Task status (needsAction or completed)' }, + due: { type: 'string', description: 'Due date', optional: true }, + updated: { type: 'string', description: 'Last modification time' }, + selfLink: { type: 'string', description: 'URL for the task' }, + webViewLink: { type: 'string', description: 'Link to task in Google Tasks UI', optional: true }, + parent: { type: 'string', description: 'Parent task ID', optional: true }, + position: { type: 'string', description: 'Position among sibling tasks' }, + completed: { type: 'string', description: 'Completion date', optional: true }, + deleted: { type: 'boolean', description: 'Whether the task is deleted', optional: true }, + }, +} diff --git a/apps/sim/tools/google_tasks/index.ts b/apps/sim/tools/google_tasks/index.ts new file mode 100644 index 0000000000..fee7f4fa5e --- /dev/null +++ b/apps/sim/tools/google_tasks/index.ts @@ -0,0 +1,6 @@ +export { createTool as googleTasksCreateTool } from './create' +export { deleteTool as googleTasksDeleteTool } from './delete' +export { getTool as googleTasksGetTool } from './get' +export { listTool as googleTasksListTool } from './list' +export { listTaskListsTool as googleTasksListTaskListsTool } from './list-task-lists' +export { updateTool as googleTasksUpdateTool } from './update' diff --git a/apps/sim/tools/google_tasks/list-task-lists.ts b/apps/sim/tools/google_tasks/list-task-lists.ts new file mode 100644 index 0000000000..b7c9959375 --- /dev/null +++ b/apps/sim/tools/google_tasks/list-task-lists.ts @@ -0,0 +1,88 @@ +import type { ToolConfig } from '@/tools/types' +import { TASKS_API_BASE } from './types' +import type { GoogleTasksListTaskListsParams, GoogleTasksListTaskListsResponse } from './types' + +export const listTaskListsTool: ToolConfig< + GoogleTasksListTaskListsParams, + GoogleTasksListTaskListsResponse +> = { + id: 'google_tasks_list_task_lists', + name: 'Google Tasks List Task Lists', + description: 'Retrieve all task lists for the authenticated user', + version: '1.0.0', + + oauth: { + required: true, + provider: 'google-tasks', + }, + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Google Tasks OAuth access token', + }, + maxResults: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Maximum number of task lists to return (default 1000, max 1000)', + }, + pageToken: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Token for pagination', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.maxResults) queryParams.set('maxResults', String(params.maxResults)) + if (params.pageToken) queryParams.set('pageToken', params.pageToken) + const qs = queryParams.toString() + return `${TASKS_API_BASE}/users/@me/lists${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + + if (!response.ok) { + throw new Error(data.error?.message ?? 'Failed to list task lists') + } + + const items = data.items ?? [] + + return { + success: true, + output: { + taskLists: items.map((item: Record) => ({ + id: (item.id as string) ?? null, + title: (item.title as string) ?? null, + updated: (item.updated as string) ?? null, + selfLink: (item.selfLink as string) ?? null, + })), + nextPageToken: data.nextPageToken ?? null, + }, + } + }, + + outputs: { + taskLists: { + type: 'json', + description: 'Array of task lists with id, title, updated, and selfLink', + }, + nextPageToken: { + type: 'string', + description: 'Token for retrieving the next page of results', + optional: true, + }, + }, +} diff --git a/apps/sim/tools/google_tasks/list.ts b/apps/sim/tools/google_tasks/list.ts new file mode 100644 index 0000000000..336d7cec1a --- /dev/null +++ b/apps/sim/tools/google_tasks/list.ts @@ -0,0 +1,156 @@ +import type { ToolConfig } from '@/tools/types' +import { TASKS_API_BASE } from './types' +import type { GoogleTasksListParams, GoogleTasksListResponse } from './types' + +export const listTool: ToolConfig = { + id: 'google_tasks_list', + name: 'Google Tasks List Tasks', + description: 'List all tasks in a Google Tasks list', + version: '1.0.0', + + oauth: { + required: true, + provider: 'google-tasks', + }, + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Google Tasks OAuth access token', + }, + taskListId: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Task list ID (defaults to primary task list "@default")', + }, + maxResults: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Maximum number of tasks to return (default 20, max 100)', + }, + pageToken: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Token for pagination', + }, + showCompleted: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Whether to show completed tasks (default true)', + }, + showDeleted: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Whether to show deleted tasks (default false)', + }, + showHidden: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Whether to show hidden tasks (default false)', + }, + dueMin: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Lower bound for due date filter (RFC 3339 timestamp)', + }, + dueMax: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Upper bound for due date filter (RFC 3339 timestamp)', + }, + completedMin: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Lower bound for task completion date (RFC 3339 timestamp)', + }, + completedMax: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Upper bound for task completion date (RFC 3339 timestamp)', + }, + updatedMin: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Lower bound for last modification time (RFC 3339 timestamp)', + }, + }, + + request: { + url: (params) => { + const taskListId = params.taskListId || '@default' + const queryParams = new URLSearchParams() + if (params.maxResults) queryParams.set('maxResults', String(params.maxResults)) + if (params.pageToken) queryParams.set('pageToken', params.pageToken) + if (params.showCompleted !== undefined) + queryParams.set('showCompleted', String(params.showCompleted)) + if (params.showDeleted !== undefined) + queryParams.set('showDeleted', String(params.showDeleted)) + if (params.showHidden !== undefined) + queryParams.set('showHidden', String(params.showHidden)) + if (params.dueMin) queryParams.set('dueMin', params.dueMin) + if (params.dueMax) queryParams.set('dueMax', params.dueMax) + if (params.completedMin) queryParams.set('completedMin', params.completedMin) + if (params.completedMax) queryParams.set('completedMax', params.completedMax) + if (params.updatedMin) queryParams.set('updatedMin', params.updatedMin) + const qs = queryParams.toString() + return `${TASKS_API_BASE}/lists/${encodeURIComponent(taskListId)}/tasks${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + }), + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + + if (!response.ok) { + throw new Error(data.error?.message ?? 'Failed to list tasks') + } + + const items = data.items ?? [] + + return { + success: true, + output: { + tasks: items.map((item: Record) => ({ + id: (item.id as string) ?? null, + title: (item.title as string) ?? null, + notes: (item.notes as string) ?? null, + status: (item.status as string) ?? null, + due: (item.due as string) ?? null, + updated: (item.updated as string) ?? null, + webViewLink: (item.webViewLink as string) ?? null, + parent: (item.parent as string) ?? null, + position: (item.position as string) ?? null, + })), + nextPageToken: data.nextPageToken ?? null, + }, + } + }, + + outputs: { + tasks: { + type: 'json', + description: 'Array of tasks with id, title, notes, status, due, updated, and more', + }, + nextPageToken: { + type: 'string', + description: 'Token for retrieving the next page of results', + optional: true, + }, + }, +} diff --git a/apps/sim/tools/google_tasks/types.ts b/apps/sim/tools/google_tasks/types.ts new file mode 100644 index 0000000000..e2e5337a67 --- /dev/null +++ b/apps/sim/tools/google_tasks/types.ts @@ -0,0 +1,108 @@ +import type { ToolResponse } from '@/tools/types' + +export const TASKS_API_BASE = 'https://tasks.googleapis.com/tasks/v1' + +export interface BaseGoogleTasksParams { + accessToken: string +} + +export interface GoogleTasksListTaskListsParams extends BaseGoogleTasksParams { + maxResults?: number + pageToken?: string +} + +export interface GoogleTasksCreateParams extends BaseGoogleTasksParams { + taskListId?: string + title: string + notes?: string + due?: string + status?: string + parent?: string + previous?: string +} + +export interface GoogleTasksListParams extends BaseGoogleTasksParams { + taskListId?: string + maxResults?: number + pageToken?: string + showCompleted?: boolean + showDeleted?: boolean + showHidden?: boolean + dueMin?: string + dueMax?: string + completedMin?: string + completedMax?: string + updatedMin?: string +} + +export interface GoogleTasksGetParams extends BaseGoogleTasksParams { + taskListId?: string + taskId: string +} + +export interface GoogleTasksUpdateParams extends BaseGoogleTasksParams { + taskListId?: string + taskId: string + title?: string + notes?: string + due?: string + status?: string +} + +export interface GoogleTasksDeleteParams extends BaseGoogleTasksParams { + taskListId?: string + taskId: string +} + +export interface GoogleTasksResponse extends ToolResponse { + output: { + id: string | null + title: string | null + notes: string | null + status: string | null + due: string | null + updated: string | null + selfLink: string | null + webViewLink: string | null + parent: string | null + position: string | null + completed: string | null + deleted: boolean | null + } +} + +export interface GoogleTasksListResponse extends ToolResponse { + output: { + tasks: Array<{ + id: string | null + title: string | null + notes: string | null + status: string | null + due: string | null + updated: string | null + webViewLink: string | null + parent: string | null + position: string | null + }> + nextPageToken: string | null + } +} + +export interface GoogleTasksListTaskListsResponse extends ToolResponse { + output: { + taskLists: Array<{ + id: string | null + title: string | null + updated: string | null + selfLink: string | null + }> + nextPageToken: string | null + } +} + +export interface GoogleTasksDeleteResponse extends ToolResponse { + output: { + taskId: string + deleted: boolean + } +} diff --git a/apps/sim/tools/google_tasks/update.ts b/apps/sim/tools/google_tasks/update.ts new file mode 100644 index 0000000000..1736b02438 --- /dev/null +++ b/apps/sim/tools/google_tasks/update.ts @@ -0,0 +1,121 @@ +import type { ToolConfig } from '@/tools/types' +import { TASKS_API_BASE } from './types' +import type { GoogleTasksUpdateParams, GoogleTasksResponse } from './types' + +export const updateTool: ToolConfig = { + id: 'google_tasks_update', + name: 'Google Tasks Update Task', + description: 'Update an existing task in a Google Tasks list', + version: '1.0.0', + + oauth: { + required: true, + provider: 'google-tasks', + }, + + params: { + accessToken: { + type: 'string', + required: true, + visibility: 'hidden', + description: 'Google Tasks OAuth access token', + }, + taskListId: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Task list ID (defaults to primary task list "@default")', + }, + taskId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the task to update', + }, + title: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'New title for the task', + }, + notes: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'New notes for the task', + }, + due: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'New due date in RFC 3339 format', + }, + status: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'New status: "needsAction" or "completed"', + }, + }, + + request: { + url: (params) => { + const taskListId = params.taskListId || '@default' + return `${TASKS_API_BASE}/lists/${encodeURIComponent(taskListId)}/tasks/${encodeURIComponent(params.taskId)}` + }, + method: 'PATCH', + headers: (params) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + }), + body: (params) => { + const body: Record = {} + if (params.title !== undefined) body.title = params.title + if (params.notes !== undefined) body.notes = params.notes + if (params.due !== undefined) body.due = params.due + if (params.status !== undefined) body.status = params.status + return body + }, + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + + if (!response.ok) { + throw new Error(data.error?.message ?? 'Failed to update task') + } + + return { + success: true, + output: { + id: data.id ?? null, + title: data.title ?? null, + notes: data.notes ?? null, + status: data.status ?? null, + due: data.due ?? null, + updated: data.updated ?? null, + selfLink: data.selfLink ?? null, + webViewLink: data.webViewLink ?? null, + parent: data.parent ?? null, + position: data.position ?? null, + completed: data.completed ?? null, + deleted: data.deleted ?? null, + }, + } + }, + + outputs: { + id: { type: 'string', description: 'Task ID' }, + title: { type: 'string', description: 'Task title' }, + notes: { type: 'string', description: 'Task notes', optional: true }, + status: { type: 'string', description: 'Task status (needsAction or completed)' }, + due: { type: 'string', description: 'Due date', optional: true }, + updated: { type: 'string', description: 'Last modification time' }, + selfLink: { type: 'string', description: 'URL for the task' }, + webViewLink: { type: 'string', description: 'Link to task in Google Tasks UI', optional: true }, + parent: { type: 'string', description: 'Parent task ID', optional: true }, + position: { type: 'string', description: 'Position among sibling tasks' }, + completed: { type: 'string', description: 'Completion date', optional: true }, + deleted: { type: 'boolean', description: 'Whether the task is deleted', optional: true }, + }, +} diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index 4c964a9380..f8fe215d22 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -740,6 +740,14 @@ import { googleMapsTimezoneTool, googleMapsValidateAddressTool, } from '@/tools/google_maps' +import { + googleTasksCreateTool, + googleTasksDeleteTool, + googleTasksGetTool, + googleTasksListTool, + googleTasksListTaskListsTool, + googleTasksUpdateTool, +} from '@/tools/google_tasks' import { googleSheetsAppendTool, googleSheetsAppendV2Tool, @@ -2942,6 +2950,12 @@ export const tools: Record = { google_maps_speed_limits: googleMapsSpeedLimitsTool, google_maps_timezone: googleMapsTimezoneTool, google_maps_validate_address: googleMapsValidateAddressTool, + google_tasks_create: googleTasksCreateTool, + google_tasks_delete: googleTasksDeleteTool, + google_tasks_get: googleTasksGetTool, + google_tasks_list: googleTasksListTool, + google_tasks_list_task_lists: googleTasksListTaskListsTool, + google_tasks_update: googleTasksUpdateTool, google_translate_detect: googleTranslateDetectTool, google_translate_text: googleTranslateTool, google_sheets_read: googleSheetsReadTool, From 98a19ed7aceb9e91d722035d1c4ad12ab7a1d30f Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Wed, 25 Feb 2026 13:04:04 -0800 Subject: [PATCH 2/7] fix(google-tasks): return actual taskId in delete response --- apps/sim/tools/google_tasks/delete.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/sim/tools/google_tasks/delete.ts b/apps/sim/tools/google_tasks/delete.ts index c9c1df0ddf..d2a300065c 100644 --- a/apps/sim/tools/google_tasks/delete.ts +++ b/apps/sim/tools/google_tasks/delete.ts @@ -45,12 +45,12 @@ export const deleteTool: ToolConfig { + transformResponse: async (response: Response, params) => { if (response.status === 204 || response.ok) { return { success: true, output: { - taskId: '', + taskId: params?.taskId || '', deleted: true, }, } From e48561c7faa17ca64556dfc0a4b05fcf539cd1f7 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Wed, 25 Feb 2026 13:16:55 -0800 Subject: [PATCH 3/7] fix(google-tasks): use absolute imports and fix registry order --- apps/sim/blocks/registry.ts | 2 +- apps/sim/tools/google_tasks/create.ts | 4 ++-- apps/sim/tools/google_tasks/delete.ts | 4 ++-- apps/sim/tools/google_tasks/get.ts | 4 ++-- apps/sim/tools/google_tasks/index.ts | 12 ++++++------ apps/sim/tools/google_tasks/list-task-lists.ts | 4 ++-- apps/sim/tools/google_tasks/list.ts | 4 ++-- apps/sim/tools/google_tasks/update.ts | 4 ++-- 8 files changed, 19 insertions(+), 19 deletions(-) diff --git a/apps/sim/blocks/registry.ts b/apps/sim/blocks/registry.ts index 002f107c99..bc0dd29c56 100644 --- a/apps/sim/blocks/registry.ts +++ b/apps/sim/blocks/registry.ts @@ -50,8 +50,8 @@ import { GoogleDocsBlock } from '@/blocks/blocks/google_docs' import { GoogleDriveBlock } from '@/blocks/blocks/google_drive' import { GoogleFormsBlock } from '@/blocks/blocks/google_forms' import { GoogleGroupsBlock } from '@/blocks/blocks/google_groups' -import { GoogleTasksBlock } from '@/blocks/blocks/google_tasks' import { GoogleMapsBlock } from '@/blocks/blocks/google_maps' +import { GoogleTasksBlock } from '@/blocks/blocks/google_tasks' import { GoogleSheetsBlock, GoogleSheetsV2Block } from '@/blocks/blocks/google_sheets' import { GoogleSlidesBlock, GoogleSlidesV2Block } from '@/blocks/blocks/google_slides' import { GoogleTranslateBlock } from '@/blocks/blocks/google_translate' diff --git a/apps/sim/tools/google_tasks/create.ts b/apps/sim/tools/google_tasks/create.ts index 6cd9257eaa..1e46bf39d6 100644 --- a/apps/sim/tools/google_tasks/create.ts +++ b/apps/sim/tools/google_tasks/create.ts @@ -1,6 +1,6 @@ import type { ToolConfig } from '@/tools/types' -import { TASKS_API_BASE } from './types' -import type { GoogleTasksCreateParams, GoogleTasksResponse } from './types' +import { TASKS_API_BASE } from '@/tools/google_tasks/types' +import type { GoogleTasksCreateParams, GoogleTasksResponse } from '@/tools/google_tasks/types' export const createTool: ToolConfig = { id: 'google_tasks_create', diff --git a/apps/sim/tools/google_tasks/delete.ts b/apps/sim/tools/google_tasks/delete.ts index d2a300065c..7b99c2577a 100644 --- a/apps/sim/tools/google_tasks/delete.ts +++ b/apps/sim/tools/google_tasks/delete.ts @@ -1,6 +1,6 @@ import type { ToolConfig } from '@/tools/types' -import { TASKS_API_BASE } from './types' -import type { GoogleTasksDeleteParams, GoogleTasksDeleteResponse } from './types' +import { TASKS_API_BASE } from '@/tools/google_tasks/types' +import type { GoogleTasksDeleteParams, GoogleTasksDeleteResponse } from '@/tools/google_tasks/types' export const deleteTool: ToolConfig = { id: 'google_tasks_delete', diff --git a/apps/sim/tools/google_tasks/get.ts b/apps/sim/tools/google_tasks/get.ts index 047f8ce539..8c04b4c54a 100644 --- a/apps/sim/tools/google_tasks/get.ts +++ b/apps/sim/tools/google_tasks/get.ts @@ -1,6 +1,6 @@ import type { ToolConfig } from '@/tools/types' -import { TASKS_API_BASE } from './types' -import type { GoogleTasksGetParams, GoogleTasksResponse } from './types' +import { TASKS_API_BASE } from '@/tools/google_tasks/types' +import type { GoogleTasksGetParams, GoogleTasksResponse } from '@/tools/google_tasks/types' export const getTool: ToolConfig = { id: 'google_tasks_get', diff --git a/apps/sim/tools/google_tasks/index.ts b/apps/sim/tools/google_tasks/index.ts index fee7f4fa5e..f8a32c3fb4 100644 --- a/apps/sim/tools/google_tasks/index.ts +++ b/apps/sim/tools/google_tasks/index.ts @@ -1,6 +1,6 @@ -export { createTool as googleTasksCreateTool } from './create' -export { deleteTool as googleTasksDeleteTool } from './delete' -export { getTool as googleTasksGetTool } from './get' -export { listTool as googleTasksListTool } from './list' -export { listTaskListsTool as googleTasksListTaskListsTool } from './list-task-lists' -export { updateTool as googleTasksUpdateTool } from './update' +export { createTool as googleTasksCreateTool } from '@/tools/google_tasks/create' +export { deleteTool as googleTasksDeleteTool } from '@/tools/google_tasks/delete' +export { getTool as googleTasksGetTool } from '@/tools/google_tasks/get' +export { listTool as googleTasksListTool } from '@/tools/google_tasks/list' +export { listTaskListsTool as googleTasksListTaskListsTool } from '@/tools/google_tasks/list-task-lists' +export { updateTool as googleTasksUpdateTool } from '@/tools/google_tasks/update' diff --git a/apps/sim/tools/google_tasks/list-task-lists.ts b/apps/sim/tools/google_tasks/list-task-lists.ts index b7c9959375..64a3198188 100644 --- a/apps/sim/tools/google_tasks/list-task-lists.ts +++ b/apps/sim/tools/google_tasks/list-task-lists.ts @@ -1,6 +1,6 @@ import type { ToolConfig } from '@/tools/types' -import { TASKS_API_BASE } from './types' -import type { GoogleTasksListTaskListsParams, GoogleTasksListTaskListsResponse } from './types' +import { TASKS_API_BASE } from '@/tools/google_tasks/types' +import type { GoogleTasksListTaskListsParams, GoogleTasksListTaskListsResponse } from '@/tools/google_tasks/types' export const listTaskListsTool: ToolConfig< GoogleTasksListTaskListsParams, diff --git a/apps/sim/tools/google_tasks/list.ts b/apps/sim/tools/google_tasks/list.ts index 336d7cec1a..c6fa136bb8 100644 --- a/apps/sim/tools/google_tasks/list.ts +++ b/apps/sim/tools/google_tasks/list.ts @@ -1,6 +1,6 @@ import type { ToolConfig } from '@/tools/types' -import { TASKS_API_BASE } from './types' -import type { GoogleTasksListParams, GoogleTasksListResponse } from './types' +import { TASKS_API_BASE } from '@/tools/google_tasks/types' +import type { GoogleTasksListParams, GoogleTasksListResponse } from '@/tools/google_tasks/types' export const listTool: ToolConfig = { id: 'google_tasks_list', diff --git a/apps/sim/tools/google_tasks/update.ts b/apps/sim/tools/google_tasks/update.ts index 1736b02438..c796f8feb5 100644 --- a/apps/sim/tools/google_tasks/update.ts +++ b/apps/sim/tools/google_tasks/update.ts @@ -1,6 +1,6 @@ import type { ToolConfig } from '@/tools/types' -import { TASKS_API_BASE } from './types' -import type { GoogleTasksUpdateParams, GoogleTasksResponse } from './types' +import { TASKS_API_BASE } from '@/tools/google_tasks/types' +import type { GoogleTasksUpdateParams, GoogleTasksResponse } from '@/tools/google_tasks/types' export const updateTool: ToolConfig = { id: 'google_tasks_update', From 7d32cd504ba059a4aad1e8152d51dfc4b94b5663 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Wed, 25 Feb 2026 13:41:48 -0800 Subject: [PATCH 4/7] fix(google-tasks): rename list-task-lists to list_task_lists for doc generator --- .../content/docs/en/tools/google_tasks.mdx | 22 +++++-------------- apps/sim/tools/google_tasks/index.ts | 2 +- ...{list-task-lists.ts => list_task_lists.ts} | 0 3 files changed, 7 insertions(+), 17 deletions(-) rename apps/sim/tools/google_tasks/{list-task-lists.ts => list_task_lists.ts} (100%) diff --git a/apps/docs/content/docs/en/tools/google_tasks.mdx b/apps/docs/content/docs/en/tools/google_tasks.mdx index 3e953fa76f..192a6dc37b 100644 --- a/apps/docs/content/docs/en/tools/google_tasks.mdx +++ b/apps/docs/content/docs/en/tools/google_tasks.mdx @@ -158,30 +158,20 @@ Delete a task from a Google Tasks list ### `google_tasks_list_task_lists` +Retrieve all task lists for the authenticated user + #### Input | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | +| `maxResults` | number | No | Maximum number of task lists to return \(default 1000, max 1000\) | +| `pageToken` | string | No | Token for pagination | #### Output | Parameter | Type | Description | | --------- | ---- | ----------- | -| `id` | string | Task ID | -| `title` | string | Task title | -| `notes` | string | Task notes | -| `status` | string | Task status | -| `due` | string | Due date | -| `updated` | string | Last modification time | -| `selfLink` | string | URL for the task | -| `webViewLink` | string | Link to task in Google Tasks UI | -| `parent` | string | Parent task ID | -| `position` | string | Position among sibling tasks | -| `completed` | string | Completion date | -| `deleted` | boolean | Whether the task is deleted | -| `tasks` | json | Array of tasks \(list operation\) | -| `taskLists` | json | Array of task lists \(list_task_lists operation\) | -| `taskId` | string | Deleted task ID \(delete operation\) | -| `nextPageToken` | string | Token for next page of results | +| `taskLists` | json | Array of task lists with id, title, updated, and selfLink | +| `nextPageToken` | string | Token for retrieving the next page of results | diff --git a/apps/sim/tools/google_tasks/index.ts b/apps/sim/tools/google_tasks/index.ts index f8a32c3fb4..07e0406abc 100644 --- a/apps/sim/tools/google_tasks/index.ts +++ b/apps/sim/tools/google_tasks/index.ts @@ -2,5 +2,5 @@ export { createTool as googleTasksCreateTool } from '@/tools/google_tasks/create export { deleteTool as googleTasksDeleteTool } from '@/tools/google_tasks/delete' export { getTool as googleTasksGetTool } from '@/tools/google_tasks/get' export { listTool as googleTasksListTool } from '@/tools/google_tasks/list' -export { listTaskListsTool as googleTasksListTaskListsTool } from '@/tools/google_tasks/list-task-lists' +export { listTaskListsTool as googleTasksListTaskListsTool } from '@/tools/google_tasks/list_task_lists' export { updateTool as googleTasksUpdateTool } from '@/tools/google_tasks/update' diff --git a/apps/sim/tools/google_tasks/list-task-lists.ts b/apps/sim/tools/google_tasks/list_task_lists.ts similarity index 100% rename from apps/sim/tools/google_tasks/list-task-lists.ts rename to apps/sim/tools/google_tasks/list_task_lists.ts From 3961cea1d0030ec109b3e6c65c09e44575de4d66 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Wed, 25 Feb 2026 21:40:23 -0800 Subject: [PATCH 5/7] improvement(google-tasks): destructure task and taskList outputs with typed schemas --- apps/sim/tools/google_tasks/list.ts | 63 ++++++++++++++++++- .../sim/tools/google_tasks/list_task_lists.ts | 18 +++++- apps/sim/tools/google_tasks/types.ts | 5 ++ 3 files changed, 81 insertions(+), 5 deletions(-) diff --git a/apps/sim/tools/google_tasks/list.ts b/apps/sim/tools/google_tasks/list.ts index c6fa136bb8..167875aaaa 100644 --- a/apps/sim/tools/google_tasks/list.ts +++ b/apps/sim/tools/google_tasks/list.ts @@ -132,10 +132,21 @@ export const listTool: ToolConfig>).map((link) => ({ + type: link.type ?? '', + description: link.description ?? '', + link: link.link ?? '', + })) + : [], })), nextPageToken: data.nextPageToken ?? null, }, @@ -144,8 +155,56 @@ export const listTool: ToolConfig }> nextPageToken: string | null } From 411cfc39814c067ef7d0caca51e54d8e86de70fb Mon Sep 17 00:00:00 2001 From: waleed Date: Wed, 25 Feb 2026 21:44:58 -0800 Subject: [PATCH 6/7] ran lint --- apps/docs/components/icons.tsx | 6 +----- apps/docs/components/ui/icon-mapping.ts | 12 ++++++------ apps/docs/content/docs/en/tools/meta.json | 2 +- apps/sim/blocks/registry.ts | 2 +- apps/sim/components/icons.tsx | 6 +----- apps/sim/tools/google_tasks/create.ts | 7 ++++--- apps/sim/tools/google_tasks/delete.ts | 4 ++-- apps/sim/tools/google_tasks/get.ts | 4 ++-- apps/sim/tools/google_tasks/list.ts | 7 +++---- apps/sim/tools/google_tasks/list_task_lists.ts | 7 +++++-- apps/sim/tools/google_tasks/update.ts | 4 ++-- apps/sim/tools/registry.ts | 16 ++++++++-------- 12 files changed, 36 insertions(+), 41 deletions(-) diff --git a/apps/docs/components/icons.tsx b/apps/docs/components/icons.tsx index 2b389603dd..8d8d161ecb 100644 --- a/apps/docs/components/icons.tsx +++ b/apps/docs/components/icons.tsx @@ -1304,11 +1304,7 @@ export function GoogleCalendarIcon(props: SVGProps) { export function GoogleTasksIcon(props: SVGProps) { return ( - + > diff --git a/apps/docs/content/docs/en/tools/meta.json b/apps/docs/content/docs/en/tools/meta.json index 15c7d068f9..cc1a22ca00 100644 --- a/apps/docs/content/docs/en/tools/meta.json +++ b/apps/docs/content/docs/en/tools/meta.json @@ -149,4 +149,4 @@ "zep", "zoom" ] -} \ No newline at end of file +} diff --git a/apps/sim/blocks/registry.ts b/apps/sim/blocks/registry.ts index bc0dd29c56..8d3ef317f6 100644 --- a/apps/sim/blocks/registry.ts +++ b/apps/sim/blocks/registry.ts @@ -51,9 +51,9 @@ import { GoogleDriveBlock } from '@/blocks/blocks/google_drive' import { GoogleFormsBlock } from '@/blocks/blocks/google_forms' import { GoogleGroupsBlock } from '@/blocks/blocks/google_groups' import { GoogleMapsBlock } from '@/blocks/blocks/google_maps' -import { GoogleTasksBlock } from '@/blocks/blocks/google_tasks' import { GoogleSheetsBlock, GoogleSheetsV2Block } from '@/blocks/blocks/google_sheets' import { GoogleSlidesBlock, GoogleSlidesV2Block } from '@/blocks/blocks/google_slides' +import { GoogleTasksBlock } from '@/blocks/blocks/google_tasks' import { GoogleTranslateBlock } from '@/blocks/blocks/google_translate' import { GoogleVaultBlock } from '@/blocks/blocks/google_vault' import { GrafanaBlock } from '@/blocks/blocks/grafana' diff --git a/apps/sim/components/icons.tsx b/apps/sim/components/icons.tsx index 2b389603dd..8d8d161ecb 100644 --- a/apps/sim/components/icons.tsx +++ b/apps/sim/components/icons.tsx @@ -1304,11 +1304,7 @@ export function GoogleCalendarIcon(props: SVGProps) { export function GoogleTasksIcon(props: SVGProps) { return ( - + = { id: 'google_tasks_create', @@ -60,7 +60,8 @@ export const createTool: ToolConfig = { id: 'google_tasks_delete', diff --git a/apps/sim/tools/google_tasks/get.ts b/apps/sim/tools/google_tasks/get.ts index 8c04b4c54a..60b5bf74d2 100644 --- a/apps/sim/tools/google_tasks/get.ts +++ b/apps/sim/tools/google_tasks/get.ts @@ -1,6 +1,6 @@ -import type { ToolConfig } from '@/tools/types' -import { TASKS_API_BASE } from '@/tools/google_tasks/types' import type { GoogleTasksGetParams, GoogleTasksResponse } from '@/tools/google_tasks/types' +import { TASKS_API_BASE } from '@/tools/google_tasks/types' +import type { ToolConfig } from '@/tools/types' export const getTool: ToolConfig = { id: 'google_tasks_get', diff --git a/apps/sim/tools/google_tasks/list.ts b/apps/sim/tools/google_tasks/list.ts index 167875aaaa..1277b70ec2 100644 --- a/apps/sim/tools/google_tasks/list.ts +++ b/apps/sim/tools/google_tasks/list.ts @@ -1,6 +1,6 @@ -import type { ToolConfig } from '@/tools/types' -import { TASKS_API_BASE } from '@/tools/google_tasks/types' import type { GoogleTasksListParams, GoogleTasksListResponse } from '@/tools/google_tasks/types' +import { TASKS_API_BASE } from '@/tools/google_tasks/types' +import type { ToolConfig } from '@/tools/types' export const listTool: ToolConfig = { id: 'google_tasks_list', @@ -98,8 +98,7 @@ export const listTool: ToolConfig = { id: 'google_tasks_update', diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index f8fe215d22..c6ddacafde 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -740,14 +740,6 @@ import { googleMapsTimezoneTool, googleMapsValidateAddressTool, } from '@/tools/google_maps' -import { - googleTasksCreateTool, - googleTasksDeleteTool, - googleTasksGetTool, - googleTasksListTool, - googleTasksListTaskListsTool, - googleTasksUpdateTool, -} from '@/tools/google_tasks' import { googleSheetsAppendTool, googleSheetsAppendV2Tool, @@ -784,6 +776,14 @@ import { googleSlidesUpdateSlidesPositionTool, googleSlidesWriteTool, } from '@/tools/google_slides' +import { + googleTasksCreateTool, + googleTasksDeleteTool, + googleTasksGetTool, + googleTasksListTaskListsTool, + googleTasksListTool, + googleTasksUpdateTool, +} from '@/tools/google_tasks' import { googleTranslateDetectTool, googleTranslateTool } from '@/tools/google_translate' import { createMattersExportTool, From fd5f6a9c1895b5a26a9294afccec9ca68934db8b Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Wed, 25 Feb 2026 21:49:01 -0800 Subject: [PATCH 7/7] improvement(google-tasks): add wandConfig for due date timestamp generation --- apps/sim/blocks/blocks/google_tasks.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/apps/sim/blocks/blocks/google_tasks.ts b/apps/sim/blocks/blocks/google_tasks.ts index 513ff29a35..850f824d50 100644 --- a/apps/sim/blocks/blocks/google_tasks.ts +++ b/apps/sim/blocks/blocks/google_tasks.ts @@ -82,6 +82,17 @@ export const GoogleTasksBlock: BlockConfig = { type: 'short-input', placeholder: '2025-06-03T00:00:00.000Z', condition: { field: 'operation', value: 'create' }, + wandConfig: { + enabled: true, + prompt: `Generate an RFC 3339 timestamp in UTC based on the user's description. +The timestamp should be in the format: YYYY-MM-DDTHH:MM:SS.000Z (UTC timezone). +Examples: +- "tomorrow" -> Calculate tomorrow's date at 00:00:00.000Z +- "next Friday" -> Calculate the next Friday's date at 00:00:00.000Z +- "June 15" -> 2025-06-15T00:00:00.000Z + +Return ONLY the timestamp - no explanations, no extra text.`, + }, }, { id: 'status', @@ -125,6 +136,17 @@ export const GoogleTasksBlock: BlockConfig = { type: 'short-input', placeholder: '2025-06-03T00:00:00.000Z', condition: { field: 'operation', value: 'update' }, + wandConfig: { + enabled: true, + prompt: `Generate an RFC 3339 timestamp in UTC based on the user's description. +The timestamp should be in the format: YYYY-MM-DDTHH:MM:SS.000Z (UTC timezone). +Examples: +- "tomorrow" -> Calculate tomorrow's date at 00:00:00.000Z +- "next Friday" -> Calculate the next Friday's date at 00:00:00.000Z +- "June 15" -> 2025-06-15T00:00:00.000Z + +Return ONLY the timestamp - no explanations, no extra text.`, + }, }, { id: 'status',