feat(tools): added download file tool for onedrive, google drive, and slack; added move email tool for gmail and outlook (#1785)

* feat(tools): added download file tool for onedrive, google drive, and slack

* added gmail & outlook move tools, added missing credentials descriptions to modal

* added slack delete/update message, add reaction; added gmail read/unread/label/unarchive; added outlook copy/delete/read/unread

* added threads to slack operations

* added timestamp for slack webhook trigger since api uses timestamp for updating/reacting/deleting

* cleanup

* added file info to slack read messages

* updated slack desc

* fixed downloading for onedrive, slack, and drive

* fix type check

* fix build failure

* cleanup files, fix triggers with attachments, fix integration blocks with include attachment to parse to user files, remove unused code

* fix move files tools

* fix tests

* fix build errors

* fix type error

* fix tests

* remove redundant code and filter out unecessary user file fields

* fix lint error

* remove fields from tag dropdown

* fix file upload via API

* fix pdf parse issue

---------

Co-authored-by: waleed <waleed>
Co-authored-by: Adam Gough <adamgough@Adams-MacBook-Pro.local>
Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
This commit is contained in:
Waleed
2025-11-05 13:00:34 -08:00
committed by GitHub
parent 21774de275
commit cf023e4d22
117 changed files with 6221 additions and 887 deletions

View File

@@ -0,0 +1,85 @@
import type { GmailLabelParams, GmailToolResponse } from '@/tools/gmail/types'
import type { ToolConfig } from '@/tools/types'
export const gmailAddLabelTool: ToolConfig<GmailLabelParams, GmailToolResponse> = {
id: 'gmail_add_label',
name: 'Gmail Add Label',
description: 'Add label(s) to a Gmail message',
version: '1.0.0',
oauth: {
required: true,
provider: 'google-email',
additionalScopes: ['https://www.googleapis.com/auth/gmail.modify'],
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Access token for Gmail API',
},
messageId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the message to add labels to',
},
labelIds: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Comma-separated label IDs to add (e.g., INBOX, Label_123)',
},
},
request: {
url: '/api/tools/gmail/add-label',
method: 'POST',
headers: () => ({
'Content-Type': 'application/json',
}),
body: (params: GmailLabelParams) => ({
accessToken: params.accessToken,
messageId: params.messageId,
labelIds: params.labelIds,
}),
},
transformResponse: async (response) => {
const data = await response.json()
if (!data.success) {
return {
success: false,
output: {
content: data.error || 'Failed to add label(s)',
metadata: {},
},
error: data.error,
}
}
return {
success: true,
output: {
content: data.output.content,
metadata: data.output.metadata,
},
}
},
outputs: {
content: { type: 'string', description: 'Success message' },
metadata: {
type: 'object',
description: 'Email metadata',
properties: {
id: { type: 'string', description: 'Gmail message ID' },
threadId: { type: 'string', description: 'Gmail thread ID' },
labelIds: { type: 'array', items: { type: 'string' }, description: 'Updated email labels' },
},
},
},
}

View File

@@ -0,0 +1,78 @@
import type { GmailMarkReadParams, GmailToolResponse } from '@/tools/gmail/types'
import type { ToolConfig } from '@/tools/types'
export const gmailArchiveTool: ToolConfig<GmailMarkReadParams, GmailToolResponse> = {
id: 'gmail_archive',
name: 'Gmail Archive',
description: 'Archive a Gmail message (remove from inbox)',
version: '1.0.0',
oauth: {
required: true,
provider: 'google-email',
additionalScopes: ['https://www.googleapis.com/auth/gmail.modify'],
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Access token for Gmail API',
},
messageId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the message to archive',
},
},
request: {
url: '/api/tools/gmail/archive',
method: 'POST',
headers: () => ({
'Content-Type': 'application/json',
}),
body: (params: GmailMarkReadParams) => ({
accessToken: params.accessToken,
messageId: params.messageId,
}),
},
transformResponse: async (response) => {
const data = await response.json()
if (!data.success) {
return {
success: false,
output: {
content: data.error || 'Failed to archive email',
metadata: {},
},
error: data.error,
}
}
return {
success: true,
output: {
content: data.output.content,
metadata: data.output.metadata,
},
}
},
outputs: {
content: { type: 'string', description: 'Success message' },
metadata: {
type: 'object',
description: 'Email metadata',
properties: {
id: { type: 'string', description: 'Gmail message ID' },
threadId: { type: 'string', description: 'Gmail thread ID' },
labelIds: { type: 'array', items: { type: 'string' }, description: 'Updated email labels' },
},
},
},
}

View File

@@ -0,0 +1,78 @@
import type { GmailMarkReadParams, GmailToolResponse } from '@/tools/gmail/types'
import type { ToolConfig } from '@/tools/types'
export const gmailDeleteTool: ToolConfig<GmailMarkReadParams, GmailToolResponse> = {
id: 'gmail_delete',
name: 'Gmail Delete',
description: 'Delete a Gmail message (move to trash)',
version: '1.0.0',
oauth: {
required: true,
provider: 'google-email',
additionalScopes: ['https://www.googleapis.com/auth/gmail.modify'],
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Access token for Gmail API',
},
messageId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the message to delete',
},
},
request: {
url: '/api/tools/gmail/delete',
method: 'POST',
headers: () => ({
'Content-Type': 'application/json',
}),
body: (params: GmailMarkReadParams) => ({
accessToken: params.accessToken,
messageId: params.messageId,
}),
},
transformResponse: async (response) => {
const data = await response.json()
if (!data.success) {
return {
success: false,
output: {
content: data.error || 'Failed to delete email',
metadata: {},
},
error: data.error,
}
}
return {
success: true,
output: {
content: data.output.content,
metadata: data.output.metadata,
},
}
},
outputs: {
content: { type: 'string', description: 'Success message' },
metadata: {
type: 'object',
description: 'Email metadata',
properties: {
id: { type: 'string', description: 'Gmail message ID' },
threadId: { type: 'string', description: 'Gmail thread ID' },
labelIds: { type: 'array', items: { type: 'string' }, description: 'Updated email labels' },
},
},
},
}

View File

@@ -1,6 +1,27 @@
import { gmailAddLabelTool } from '@/tools/gmail/add_label'
import { gmailArchiveTool } from '@/tools/gmail/archive'
import { gmailDeleteTool } from '@/tools/gmail/delete'
import { gmailDraftTool } from '@/tools/gmail/draft'
import { gmailMarkReadTool } from '@/tools/gmail/mark_read'
import { gmailMarkUnreadTool } from '@/tools/gmail/mark_unread'
import { gmailMoveTool } from '@/tools/gmail/move'
import { gmailReadTool } from '@/tools/gmail/read'
import { gmailRemoveLabelTool } from '@/tools/gmail/remove_label'
import { gmailSearchTool } from '@/tools/gmail/search'
import { gmailSendTool } from '@/tools/gmail/send'
import { gmailUnarchiveTool } from '@/tools/gmail/unarchive'
export { gmailSendTool, gmailReadTool, gmailSearchTool, gmailDraftTool }
export {
gmailSendTool,
gmailReadTool,
gmailSearchTool,
gmailDraftTool,
gmailMoveTool,
gmailMarkReadTool,
gmailMarkUnreadTool,
gmailArchiveTool,
gmailUnarchiveTool,
gmailDeleteTool,
gmailAddLabelTool,
gmailRemoveLabelTool,
}

View File

@@ -0,0 +1,78 @@
import type { GmailMarkReadParams, GmailToolResponse } from '@/tools/gmail/types'
import type { ToolConfig } from '@/tools/types'
export const gmailMarkReadTool: ToolConfig<GmailMarkReadParams, GmailToolResponse> = {
id: 'gmail_mark_read',
name: 'Gmail Mark as Read',
description: 'Mark a Gmail message as read',
version: '1.0.0',
oauth: {
required: true,
provider: 'google-email',
additionalScopes: ['https://www.googleapis.com/auth/gmail.modify'],
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Access token for Gmail API',
},
messageId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the message to mark as read',
},
},
request: {
url: '/api/tools/gmail/mark-read',
method: 'POST',
headers: () => ({
'Content-Type': 'application/json',
}),
body: (params: GmailMarkReadParams) => ({
accessToken: params.accessToken,
messageId: params.messageId,
}),
},
transformResponse: async (response) => {
const data = await response.json()
if (!data.success) {
return {
success: false,
output: {
content: data.error || 'Failed to mark email as read',
metadata: {},
},
error: data.error,
}
}
return {
success: true,
output: {
content: data.output.content,
metadata: data.output.metadata,
},
}
},
outputs: {
content: { type: 'string', description: 'Success message' },
metadata: {
type: 'object',
description: 'Email metadata',
properties: {
id: { type: 'string', description: 'Gmail message ID' },
threadId: { type: 'string', description: 'Gmail thread ID' },
labelIds: { type: 'array', items: { type: 'string' }, description: 'Updated email labels' },
},
},
},
}

View File

@@ -0,0 +1,78 @@
import type { GmailMarkReadParams, GmailToolResponse } from '@/tools/gmail/types'
import type { ToolConfig } from '@/tools/types'
export const gmailMarkUnreadTool: ToolConfig<GmailMarkReadParams, GmailToolResponse> = {
id: 'gmail_mark_unread',
name: 'Gmail Mark as Unread',
description: 'Mark a Gmail message as unread',
version: '1.0.0',
oauth: {
required: true,
provider: 'google-email',
additionalScopes: ['https://www.googleapis.com/auth/gmail.modify'],
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Access token for Gmail API',
},
messageId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the message to mark as unread',
},
},
request: {
url: '/api/tools/gmail/mark-unread',
method: 'POST',
headers: () => ({
'Content-Type': 'application/json',
}),
body: (params: GmailMarkReadParams) => ({
accessToken: params.accessToken,
messageId: params.messageId,
}),
},
transformResponse: async (response) => {
const data = await response.json()
if (!data.success) {
return {
success: false,
output: {
content: data.error || 'Failed to mark email as unread',
metadata: {},
},
error: data.error,
}
}
return {
success: true,
output: {
content: data.output.content,
metadata: data.output.metadata,
},
}
},
outputs: {
content: { type: 'string', description: 'Success message' },
metadata: {
type: 'object',
description: 'Email metadata',
properties: {
id: { type: 'string', description: 'Gmail message ID' },
threadId: { type: 'string', description: 'Gmail thread ID' },
labelIds: { type: 'array', items: { type: 'string' }, description: 'Updated email labels' },
},
},
},
}

View File

@@ -0,0 +1,92 @@
import type { GmailMoveParams, GmailToolResponse } from '@/tools/gmail/types'
import type { ToolConfig } from '@/tools/types'
export const gmailMoveTool: ToolConfig<GmailMoveParams, GmailToolResponse> = {
id: 'gmail_move',
name: 'Gmail Move',
description: 'Move emails between Gmail labels/folders',
version: '1.0.0',
oauth: {
required: true,
provider: 'google-email',
additionalScopes: ['https://www.googleapis.com/auth/gmail.modify'],
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Access token for Gmail API',
},
messageId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the message to move',
},
addLabelIds: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Comma-separated label IDs to add (e.g., INBOX, Label_123)',
},
removeLabelIds: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Comma-separated label IDs to remove (e.g., INBOX, SPAM)',
},
},
request: {
url: '/api/tools/gmail/move',
method: 'POST',
headers: () => ({
'Content-Type': 'application/json',
}),
body: (params: GmailMoveParams) => ({
accessToken: params.accessToken,
messageId: params.messageId,
addLabelIds: params.addLabelIds,
removeLabelIds: params.removeLabelIds,
}),
},
transformResponse: async (response) => {
const data = await response.json()
if (!data.success) {
return {
success: false,
output: {
content: data.error || 'Failed to move email',
metadata: {},
},
error: data.error,
}
}
return {
success: true,
output: {
content: data.output.content,
metadata: data.output.metadata,
},
}
},
outputs: {
content: { type: 'string', description: 'Success message' },
metadata: {
type: 'object',
description: 'Email metadata',
properties: {
id: { type: 'string', description: 'Gmail message ID' },
threadId: { type: 'string', description: 'Gmail thread ID' },
labelIds: { type: 'array', items: { type: 'string' }, description: 'Updated email labels' },
},
},
},
}

View File

@@ -0,0 +1,85 @@
import type { GmailLabelParams, GmailToolResponse } from '@/tools/gmail/types'
import type { ToolConfig } from '@/tools/types'
export const gmailRemoveLabelTool: ToolConfig<GmailLabelParams, GmailToolResponse> = {
id: 'gmail_remove_label',
name: 'Gmail Remove Label',
description: 'Remove label(s) from a Gmail message',
version: '1.0.0',
oauth: {
required: true,
provider: 'google-email',
additionalScopes: ['https://www.googleapis.com/auth/gmail.modify'],
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Access token for Gmail API',
},
messageId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the message to remove labels from',
},
labelIds: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Comma-separated label IDs to remove (e.g., INBOX, Label_123)',
},
},
request: {
url: '/api/tools/gmail/remove-label',
method: 'POST',
headers: () => ({
'Content-Type': 'application/json',
}),
body: (params: GmailLabelParams) => ({
accessToken: params.accessToken,
messageId: params.messageId,
labelIds: params.labelIds,
}),
},
transformResponse: async (response) => {
const data = await response.json()
if (!data.success) {
return {
success: false,
output: {
content: data.error || 'Failed to remove label(s)',
metadata: {},
},
error: data.error,
}
}
return {
success: true,
output: {
content: data.output.content,
metadata: data.output.metadata,
},
}
},
outputs: {
content: { type: 'string', description: 'Success message' },
metadata: {
type: 'object',
description: 'Email metadata',
properties: {
id: { type: 'string', description: 'Gmail message ID' },
threadId: { type: 'string', description: 'Gmail thread ID' },
labelIds: { type: 'array', items: { type: 'string' }, description: 'Updated email labels' },
},
},
},
}

View File

@@ -33,8 +33,32 @@ export interface GmailSearchParams extends BaseGmailParams {
maxResults?: number
}
// Move operation parameters
export interface GmailMoveParams extends BaseGmailParams {
messageId: string
addLabelIds: string
removeLabelIds?: string
}
// Mark as read/unread parameters (reuses simple messageId pattern)
export interface GmailMarkReadParams extends BaseGmailParams {
messageId: string
}
// Label management parameters
export interface GmailLabelParams extends BaseGmailParams {
messageId: string
labelIds: string
}
// Union type for all Gmail tool parameters
export type GmailToolParams = GmailSendParams | GmailReadParams | GmailSearchParams
export type GmailToolParams =
| GmailSendParams
| GmailReadParams
| GmailSearchParams
| GmailMoveParams
| GmailMarkReadParams
| GmailLabelParams
// Response metadata
interface BaseGmailMetadata {

View File

@@ -0,0 +1,78 @@
import type { GmailMarkReadParams, GmailToolResponse } from '@/tools/gmail/types'
import type { ToolConfig } from '@/tools/types'
export const gmailUnarchiveTool: ToolConfig<GmailMarkReadParams, GmailToolResponse> = {
id: 'gmail_unarchive',
name: 'Gmail Unarchive',
description: 'Unarchive a Gmail message (move back to inbox)',
version: '1.0.0',
oauth: {
required: true,
provider: 'google-email',
additionalScopes: ['https://www.googleapis.com/auth/gmail.modify'],
},
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'Access token for Gmail API',
},
messageId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the message to unarchive',
},
},
request: {
url: '/api/tools/gmail/unarchive',
method: 'POST',
headers: () => ({
'Content-Type': 'application/json',
}),
body: (params: GmailMarkReadParams) => ({
accessToken: params.accessToken,
messageId: params.messageId,
}),
},
transformResponse: async (response) => {
const data = await response.json()
if (!data.success) {
return {
success: false,
output: {
content: data.error || 'Failed to unarchive email',
metadata: {},
},
error: data.error,
}
}
return {
success: true,
output: {
content: data.output.content,
metadata: data.output.metadata,
},
}
},
outputs: {
content: { type: 'string', description: 'Success message' },
metadata: {
type: 'object',
description: 'Email metadata',
properties: {
id: { type: 'string', description: 'Gmail message ID' },
threadId: { type: 'string', description: 'Gmail thread ID' },
labelIds: { type: 'array', items: { type: 'string' }, description: 'Updated email labels' },
},
},
},
}