Skip to content
Open
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
7 changes: 7 additions & 0 deletions apps/docs/content/docs/en/tools/google_vault.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ Create an export in a matter
| `corpus` | string | Yes | Data corpus to export \(MAIL, DRIVE, GROUPS, HANGOUTS_CHAT, VOICE\) |
| `accountEmails` | string | No | Comma-separated list of user emails to scope export |
| `orgUnitId` | string | No | Organization unit ID to scope export \(alternative to emails\) |
| `startTime` | string | No | Start time for date filtering \(ISO 8601 format, e.g., 2024-01-01T00:00:00Z\) |
| `endTime` | string | No | End time for date filtering \(ISO 8601 format, e.g., 2024-12-31T23:59:59Z\) |
| `terms` | string | No | Search query terms to filter exported content |

#### Output

Expand Down Expand Up @@ -91,6 +94,10 @@ Create a hold in a matter
| `corpus` | string | Yes | Data corpus to hold \(MAIL, DRIVE, GROUPS, HANGOUTS_CHAT, VOICE\) |
| `accountEmails` | string | No | Comma-separated list of user emails to put on hold |
| `orgUnitId` | string | No | Organization unit ID to put on hold \(alternative to accounts\) |
| `terms` | string | No | Search terms to filter held content \(for MAIL and GROUPS corpus\) |
| `startTime` | string | No | Start time for date filtering \(ISO 8601 format, for MAIL and GROUPS corpus\) |
| `endTime` | string | No | End time for date filtering \(ISO 8601 format, for MAIL and GROUPS corpus\) |
| `includeSharedDrives` | boolean | No | Include files in shared drives \(for DRIVE corpus\) |

#### Output

Expand Down
168 changes: 168 additions & 0 deletions apps/sim/blocks/blocks/google_vault.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,167 @@ Return ONLY the hold name - no explanations, no quotes, no extra text.`,
placeholder: 'Org Unit ID (alternative to emails)',
condition: { field: 'operation', value: ['create_matters_holds', 'create_matters_export'] },
},
// Date filtering for exports (works with all corpus types)
{
id: 'startTime',
title: 'Start Time',
type: 'short-input',
placeholder: 'YYYY-MM-DDTHH:mm:ssZ',
condition: { field: 'operation', value: 'create_matters_export' },
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp in GMT based on the user's description for Google Vault date filtering.
The timestamp should be in the format: YYYY-MM-DDTHH:mm:ssZ (UTC timezone).
Note: Google Vault rounds times to 12 AM on the specified date.
Examples:
- "yesterday" -> Calculate yesterday's date at 00:00:00Z
- "last week" -> Calculate 7 days ago at 00:00:00Z
- "beginning of this month" -> Calculate the 1st of current month at 00:00:00Z
- "January 1, 2024" -> 2024-01-01T00:00:00Z

Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the start date (e.g., "last month", "January 1, 2024")...',
generationType: 'timestamp',
},
},
Comment on lines +162 to +184
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate field ID startTime defined. This field already exists for create_matters_export operation (lines 162-184) and is redefined for create_matters_holds operation (lines 186-208). The UI form system may not handle duplicate field IDs correctly, potentially causing configuration conflicts or only one field being recognized.

Consider using unique field IDs like export_startTime and hold_startTime, or refactor to use a single field definition with more complex conditions if the behavior should be identical.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/sim/blocks/blocks/google_vault.ts
Line: 162:184

Comment:
Duplicate field ID `startTime` defined. This field already exists for create_matters_export operation (lines 162-184) and is redefined for create_matters_holds operation (lines 186-208). The UI form system may not handle duplicate field IDs correctly, potentially causing configuration conflicts or only one field being recognized.

Consider using unique field IDs like `export_startTime` and `hold_startTime`, or refactor to use a single field definition with more complex conditions if the behavior should be identical.

How can I resolve this? If you propose a fix, please make it concise.

{
id: 'endTime',
title: 'End Time',
type: 'short-input',
placeholder: 'YYYY-MM-DDTHH:mm:ssZ',
condition: { field: 'operation', value: 'create_matters_export' },
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp in GMT based on the user's description for Google Vault date filtering.
The timestamp should be in the format: YYYY-MM-DDTHH:mm:ssZ (UTC timezone).
Note: Google Vault rounds times to 12 AM on the specified date.
Examples:
- "now" -> Current timestamp
- "today" -> Today's date at 23:59:59Z
- "end of last month" -> Last day of previous month at 23:59:59Z
- "December 31, 2024" -> 2024-12-31T23:59:59Z

Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the end date (e.g., "today", "end of last quarter")...',
generationType: 'timestamp',
},
},
// Date filtering for holds (only works with MAIL and GROUPS corpus)
{
Comment on lines +186 to +208
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate field ID endTime defined. This field is already defined for create_matters_export (lines 186-208) and is redefined for create_matters_holds (lines 210-232). Similar to startTime, duplicate IDs may cause issues with the form rendering system.

Consider using unique field IDs to avoid potential conflicts.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/sim/blocks/blocks/google_vault.ts
Line: 186:208

Comment:
Duplicate field ID `endTime` defined. This field is already defined for create_matters_export (lines 186-208) and is redefined for create_matters_holds (lines 210-232). Similar to `startTime`, duplicate IDs may cause issues with the form rendering system.

Consider using unique field IDs to avoid potential conflicts.

How can I resolve this? If you propose a fix, please make it concise.

id: 'startTime',
title: 'Start Time',
type: 'short-input',
placeholder: 'YYYY-MM-DDTHH:mm:ssZ',
condition: {
field: 'operation',
value: 'create_matters_holds',
and: { field: 'corpus', value: ['MAIL', 'GROUPS'] },
},
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp in GMT based on the user's description for Google Vault date filtering.
The timestamp should be in the format: YYYY-MM-DDTHH:mm:ssZ (UTC timezone).
Note: Google Vault rounds times to 12 AM on the specified date.
Examples:
- "yesterday" -> Calculate yesterday's date at 00:00:00Z
- "last week" -> Calculate 7 days ago at 00:00:00Z
- "beginning of this month" -> Calculate the 1st of current month at 00:00:00Z
- "January 1, 2024" -> 2024-01-01T00:00:00Z

Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the start date (e.g., "last month", "January 1, 2024")...',
generationType: 'timestamp',
},
},
{
id: 'endTime',
title: 'End Time',
type: 'short-input',
placeholder: 'YYYY-MM-DDTHH:mm:ssZ',
condition: {
field: 'operation',
value: 'create_matters_holds',
and: { field: 'corpus', value: ['MAIL', 'GROUPS'] },
},
wandConfig: {
enabled: true,
prompt: `Generate an ISO 8601 timestamp in GMT based on the user's description for Google Vault date filtering.
The timestamp should be in the format: YYYY-MM-DDTHH:mm:ssZ (UTC timezone).
Note: Google Vault rounds times to 12 AM on the specified date.
Examples:
- "now" -> Current timestamp
- "today" -> Today's date at 23:59:59Z
- "end of last month" -> Last day of previous month at 23:59:59Z
- "December 31, 2024" -> 2024-12-31T23:59:59Z

Return ONLY the timestamp string - no explanations, no quotes, no extra text.`,
placeholder: 'Describe the end date (e.g., "today", "end of last quarter")...',
Comment on lines +234 to +256
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate field ID terms defined. This field appears twice - once for exports (lines 234-256) and once for holds (lines 258-278). Like the date fields, duplicate field IDs may cause form rendering or data binding issues.

Consider using unique identifiers for each operation's search terms field.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/sim/blocks/blocks/google_vault.ts
Line: 234:256

Comment:
Duplicate field ID `terms` defined. This field appears twice - once for exports (lines 234-256) and once for holds (lines 258-278). Like the date fields, duplicate field IDs may cause form rendering or data binding issues.

Consider using unique identifiers for each operation's search terms field.

How can I resolve this? If you propose a fix, please make it concise.

generationType: 'timestamp',
},
},
// Search terms for exports (works with all corpus types)
{
id: 'terms',
title: 'Search Terms',
type: 'long-input',
placeholder: 'Enter search query (e.g., from:user@example.com subject:confidential)',
condition: { field: 'operation', value: 'create_matters_export' },
wandConfig: {
enabled: true,
prompt: `Generate a Google Vault search query based on the user's description.
The query can use Gmail-style search operators for MAIL corpus:
- from:user@example.com - emails from specific sender
- to:user@example.com - emails to specific recipient
- subject:keyword - emails with keyword in subject
- has:attachment - emails with attachments
- filename:pdf - emails with PDF attachments
- before:YYYY/MM/DD - emails before date
- after:YYYY/MM/DD - emails after date

For DRIVE corpus, use Drive search operators:
- owner:user@example.com - files owned by user
- type:document - specific file types

Return ONLY the search query - no explanations, no quotes, no extra text.`,
placeholder: 'Describe what content to search for...',
},
},
// Search terms for holds (only works with MAIL and GROUPS corpus)
{
id: 'terms',
title: 'Search Terms',
type: 'long-input',
placeholder: 'Enter search query (e.g., from:user@example.com subject:confidential)',
condition: {
field: 'operation',
value: 'create_matters_holds',
and: { field: 'corpus', value: ['MAIL', 'GROUPS'] },
},
wandConfig: {
enabled: true,
prompt: `Generate a Google Vault search query based on the user's description.
The query can use Gmail-style search operators:
- from:user@example.com - emails from specific sender
- to:user@example.com - emails to specific recipient
- subject:keyword - emails with keyword in subject
- has:attachment - emails with attachments
- filename:pdf - emails with PDF attachments

Return ONLY the search query - no explanations, no quotes, no extra text.`,
placeholder: 'Describe what content to search for...',
},
},
// Drive-specific option for holds
{
id: 'includeSharedDrives',
title: 'Include Shared Drives',
type: 'switch',
condition: {
field: 'operation',
value: 'create_matters_holds',
and: { field: 'corpus', value: 'DRIVE' },
},
},
{
id: 'exportId',
title: 'Export ID',
Expand Down Expand Up @@ -296,9 +457,16 @@ Return ONLY the description text - no explanations, no quotes, no extra text.`,
corpus: { type: 'string', description: 'Data corpus (MAIL, DRIVE, GROUPS, etc.)' },
accountEmails: { type: 'string', description: 'Comma-separated account emails' },
orgUnitId: { type: 'string', description: 'Organization unit ID' },
startTime: { type: 'string', description: 'Start time for date filtering (ISO 8601 format)' },
endTime: { type: 'string', description: 'End time for date filtering (ISO 8601 format)' },
terms: { type: 'string', description: 'Search query terms' },

// Create hold inputs
holdName: { type: 'string', description: 'Name for the hold' },
includeSharedDrives: {
type: 'boolean',
description: 'Include files in shared drives (for DRIVE corpus holds)',
},

// Download export file inputs
bucketName: { type: 'string', description: 'GCS bucket name from export' },
Expand Down
13 changes: 4 additions & 9 deletions apps/sim/tools/google_vault/create_matters.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
import type { GoogleVaultCreateMattersParams } from '@/tools/google_vault/types'
import { enhanceGoogleVaultError } from '@/tools/google_vault/utils'
import type { ToolConfig } from '@/tools/types'

export interface GoogleVaultCreateMattersParams {
accessToken: string
name: string
description?: string
}

// matters.create
// POST https://vault.googleapis.com/v1/matters
export const createMattersTool: ToolConfig<GoogleVaultCreateMattersParams> = {
id: 'create_matters',
name: 'Vault Create Matter',
Expand Down Expand Up @@ -38,7 +32,8 @@ export const createMattersTool: ToolConfig<GoogleVaultCreateMattersParams> = {
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error?.message || 'Failed to create matter')
const errorMessage = data.error?.message || 'Failed to create matter'
throw new Error(enhanceGoogleVaultError(errorMessage))
}
return { success: true, output: { matter: data } }
},
Expand Down
26 changes: 21 additions & 5 deletions apps/sim/tools/google_vault/create_matters_export.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import type { GoogleVaultCreateMattersExportParams } from '@/tools/google_vault/types'
import { enhanceGoogleVaultError } from '@/tools/google_vault/utils'
import type { ToolConfig } from '@/tools/types'

// matters.exports.create
// POST https://vault.googleapis.com/v1/matters/{matterId}/exports
export const createMattersExportTool: ToolConfig<GoogleVaultCreateMattersExportParams> = {
id: 'create_matters_export',
name: 'Vault Create Export (by Matter)',
Expand Down Expand Up @@ -36,6 +35,24 @@ export const createMattersExportTool: ToolConfig<GoogleVaultCreateMattersExportP
visibility: 'user-only',
description: 'Organization unit ID to scope export (alternative to emails)',
},
startTime: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Start time for date filtering (ISO 8601 format, e.g., 2024-01-01T00:00:00Z)',
},
endTime: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'End time for date filtering (ISO 8601 format, e.g., 2024-12-31T23:59:59Z)',
},
terms: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Search query terms to filter exported content',
},
},

request: {
Expand All @@ -46,7 +63,6 @@ export const createMattersExportTool: ToolConfig<GoogleVaultCreateMattersExportP
'Content-Type': 'application/json',
}),
body: (params) => {
// Handle accountEmails - can be string (comma-separated) or array
let emails: string[] = []
if (params.accountEmails) {
if (Array.isArray(params.accountEmails)) {
Expand Down Expand Up @@ -75,7 +91,6 @@ export const createMattersExportTool: ToolConfig<GoogleVaultCreateMattersExportP
terms: params.terms || undefined,
startTime: params.startTime || undefined,
endTime: params.endTime || undefined,
timeZone: params.timeZone || undefined,
...scope,
}

Expand All @@ -89,7 +104,8 @@ export const createMattersExportTool: ToolConfig<GoogleVaultCreateMattersExportP
transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error?.message || 'Failed to create export')
const errorMessage = data.error?.message || 'Failed to create export'
throw new Error(enhanceGoogleVaultError(errorMessage))
}
return { success: true, output: { export: data } }
},
Expand Down
51 changes: 45 additions & 6 deletions apps/sim/tools/google_vault/create_matters_holds.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import type { GoogleVaultCreateMattersHoldsParams } from '@/tools/google_vault/types'
import { enhanceGoogleVaultError } from '@/tools/google_vault/utils'
import type { ToolConfig } from '@/tools/types'

// matters.holds.create
// POST https://vault.googleapis.com/v1/matters/{matterId}/holds
export const createMattersHoldsTool: ToolConfig<GoogleVaultCreateMattersHoldsParams> = {
id: 'create_matters_holds',
name: 'Vault Create Hold (by Matter)',
Expand Down Expand Up @@ -36,6 +35,30 @@ export const createMattersHoldsTool: ToolConfig<GoogleVaultCreateMattersHoldsPar
visibility: 'user-only',
description: 'Organization unit ID to put on hold (alternative to accounts)',
},
terms: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Search terms to filter held content (for MAIL and GROUPS corpus)',
},
startTime: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Start time for date filtering (ISO 8601 format, for MAIL and GROUPS corpus)',
},
endTime: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'End time for date filtering (ISO 8601 format, for MAIL and GROUPS corpus)',
},
includeSharedDrives: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Include files in shared drives (for DRIVE corpus)',
},
},

request: {
Expand All @@ -46,13 +69,11 @@ export const createMattersHoldsTool: ToolConfig<GoogleVaultCreateMattersHoldsPar
'Content-Type': 'application/json',
}),
body: (params) => {
// Build Hold body. One of accounts or orgUnit must be provided.
const body: any = {
name: params.holdName,
corpus: params.corpus,
}

// Handle accountEmails - can be string (comma-separated) or array
let emails: string[] = []
if (params.accountEmails) {
if (Array.isArray(params.accountEmails)) {
Expand All @@ -66,20 +87,38 @@ export const createMattersHoldsTool: ToolConfig<GoogleVaultCreateMattersHoldsPar
}

if (emails.length > 0) {
// Google Vault expects HeldAccount objects with 'email' or 'accountId'. Use 'email' here.
body.accounts = emails.map((email: string) => ({ email }))
} else if (params.orgUnitId) {
body.orgUnit = { orgUnitId: params.orgUnitId }
}

if (params.corpus === 'MAIL' || params.corpus === 'GROUPS') {
const hasQueryParams = params.terms || params.startTime || params.endTime
if (hasQueryParams) {
const queryObj: any = {}
if (params.terms) queryObj.terms = params.terms
if (params.startTime) queryObj.startTime = params.startTime
if (params.endTime) queryObj.endTime = params.endTime

if (params.corpus === 'MAIL') {
body.query = { mailQuery: queryObj }
} else {
body.query = { groupsQuery: queryObj }
}
}
} else if (params.corpus === 'DRIVE' && params.includeSharedDrives) {
body.query = { driveQuery: { includeSharedDriveFiles: params.includeSharedDrives } }
}
Comment on lines +109 to +111
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition params.includeSharedDrives only adds the driveQuery when truthy. If explicitly set to false, no query is added. Consider whether false should explicitly set includeSharedDriveFiles: false in the API request.

Suggested change
} else if (params.corpus === 'DRIVE' && params.includeSharedDrives) {
body.query = { driveQuery: { includeSharedDriveFiles: params.includeSharedDrives } }
}
} else if (params.corpus === 'DRIVE' && params.includeSharedDrives !== undefined) {
body.query = { driveQuery: { includeSharedDriveFiles: params.includeSharedDrives } }
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/sim/tools/google_vault/create_matters_holds.ts
Line: 116:118

Comment:
The condition `params.includeSharedDrives` only adds the driveQuery when truthy. If explicitly set to `false`, no query is added. Consider whether `false` should explicitly set `includeSharedDriveFiles: false` in the API request.

```suggestion
      } else if (params.corpus === 'DRIVE' && params.includeSharedDrives !== undefined) {
        body.query = { driveQuery: { includeSharedDriveFiles: params.includeSharedDrives } }
      }
```

How can I resolve this? If you propose a fix, please make it concise.


return body
},
},

transformResponse: async (response: Response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error?.message || 'Failed to create hold')
const errorMessage = data.error?.message || 'Failed to create hold'
throw new Error(enhanceGoogleVaultError(errorMessage))
}
return { success: true, output: { hold: data } }
},
Expand Down
Loading