fix(google-vault): error handling improvement and more params (#2735)

* new error throw and improvement

* fixed critical issues

* restore error thorwing

* restore

* added handler for vault

* updated docs

* restored

* removed google vault from executor

* updated translations

* updated docs

* fixed inputs and outputs

---------

Co-authored-by: aadamgough <adam@sim.ai>
Co-authored-by: waleed <walif6@gmail.com>
This commit is contained in:
Adam Gough
2026-01-16 17:59:17 -08:00
committed by GitHub
co-authored by aadamgough waleed
parent 3768c6379c
commit a8d81097fc
12 changed files with 635 additions and 155 deletions
@@ -36,43 +36,47 @@ Connect Google Vault to create exports, list exports, and manage holds within ma
### `google_vault_create_matters_export`
Create an export in a matter
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `matterId` | string | Yes | The matter ID |
| `exportName` | string | Yes | Name for the export \(avoid special characters\) |
| `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
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `matters` | json | Array of matter objects \(for list_matters\) |
| `exports` | json | Array of export objects \(for list_matters_export\) |
| `holds` | json | Array of hold objects \(for list_matters_holds\) |
| `matter` | json | Created matter object \(for create_matters\) |
| `export` | json | Created export object \(for create_matters_export\) |
| `hold` | json | Created hold object \(for create_matters_holds\) |
| `file` | json | Downloaded export file \(UserFile\) from execution files |
| `nextPageToken` | string | Token for fetching next page of results \(for list operations\) |
| `export` | json | Created export object |
### `google_vault_list_matters_export`
List exports for a matter
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `matterId` | string | Yes | The matter ID |
| `pageSize` | number | No | Number of exports to return per page |
| `pageToken` | string | No | Token for pagination |
| `exportId` | string | No | Optional export ID to fetch a specific export |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `matters` | json | Array of matter objects \(for list_matters\) |
| `exports` | json | Array of export objects \(for list_matters_export\) |
| `holds` | json | Array of hold objects \(for list_matters_holds\) |
| `matter` | json | Created matter object \(for create_matters\) |
| `export` | json | Created export object \(for create_matters_export\) |
| `hold` | json | Created hold object \(for create_matters_holds\) |
| `file` | json | Downloaded export file \(UserFile\) from execution files |
| `nextPageToken` | string | Token for fetching next page of results \(for list operations\) |
| `exports` | json | Array of export objects |
| `export` | json | Single export object \(when exportId is provided\) |
| `nextPageToken` | string | Token for fetching next page of results |
### `google_vault_download_export_file`
@@ -82,10 +86,10 @@ Download a single file from a Google Vault export (GCS object)
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `matterId` | string | Yes | No description |
| `bucketName` | string | Yes | No description |
| `objectName` | string | Yes | No description |
| `fileName` | string | No | No description |
| `matterId` | string | Yes | The matter ID |
| `bucketName` | string | Yes | GCS bucket name from cloudStorageSink.files.bucketName |
| `objectName` | string | Yes | GCS object name from cloudStorageSink.files.objectName |
| `fileName` | string | No | Optional filename override for the downloaded file |
#### Output
@@ -95,82 +99,84 @@ Download a single file from a Google Vault export (GCS object)
### `google_vault_create_matters_holds`
Create a hold in a matter
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `matterId` | string | Yes | The matter ID |
| `holdName` | string | Yes | Name for the hold |
| `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
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `matters` | json | Array of matter objects \(for list_matters\) |
| `exports` | json | Array of export objects \(for list_matters_export\) |
| `holds` | json | Array of hold objects \(for list_matters_holds\) |
| `matter` | json | Created matter object \(for create_matters\) |
| `export` | json | Created export object \(for create_matters_export\) |
| `hold` | json | Created hold object \(for create_matters_holds\) |
| `file` | json | Downloaded export file \(UserFile\) from execution files |
| `nextPageToken` | string | Token for fetching next page of results \(for list operations\) |
| `hold` | json | Created hold object |
### `google_vault_list_matters_holds`
List holds for a matter
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `matterId` | string | Yes | The matter ID |
| `pageSize` | number | No | Number of holds to return per page |
| `pageToken` | string | No | Token for pagination |
| `holdId` | string | No | Optional hold ID to fetch a specific hold |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `matters` | json | Array of matter objects \(for list_matters\) |
| `exports` | json | Array of export objects \(for list_matters_export\) |
| `holds` | json | Array of hold objects \(for list_matters_holds\) |
| `matter` | json | Created matter object \(for create_matters\) |
| `export` | json | Created export object \(for create_matters_export\) |
| `hold` | json | Created hold object \(for create_matters_holds\) |
| `file` | json | Downloaded export file \(UserFile\) from execution files |
| `nextPageToken` | string | Token for fetching next page of results \(for list operations\) |
| `holds` | json | Array of hold objects |
| `hold` | json | Single hold object \(when holdId is provided\) |
| `nextPageToken` | string | Token for fetching next page of results |
### `google_vault_create_matters`
Create a new matter in Google Vault
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `name` | string | Yes | Name for the new matter |
| `description` | string | No | Optional description for the matter |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `matters` | json | Array of matter objects \(for list_matters\) |
| `exports` | json | Array of export objects \(for list_matters_export\) |
| `holds` | json | Array of hold objects \(for list_matters_holds\) |
| `matter` | json | Created matter object \(for create_matters\) |
| `export` | json | Created export object \(for create_matters_export\) |
| `hold` | json | Created hold object \(for create_matters_holds\) |
| `file` | json | Downloaded export file \(UserFile\) from execution files |
| `nextPageToken` | string | Token for fetching next page of results \(for list operations\) |
| `matter` | json | Created matter object |
### `google_vault_list_matters`
List matters, or get a specific matter if matterId is provided
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `pageSize` | number | No | Number of matters to return per page |
| `pageToken` | string | No | Token for pagination |
| `matterId` | string | No | Optional matter ID to fetch a specific matter |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `matters` | json | Array of matter objects \(for list_matters\) |
| `exports` | json | Array of export objects \(for list_matters_export\) |
| `holds` | json | Array of hold objects \(for list_matters_holds\) |
| `matter` | json | Created matter object \(for create_matters\) |
| `export` | json | Created export object \(for create_matters_export\) |
| `hold` | json | Created hold object \(for create_matters_holds\) |
| `file` | json | Downloaded export file \(UserFile\) from execution files |
| `nextPageToken` | string | Token for fetching next page of results \(for list operations\) |
| `matters` | json | Array of matter objects |
| `matter` | json | Single matter object \(when matterId is provided\) |
| `nextPageToken` | string | Token for fetching next page of results |
+211 -7
View File
@@ -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',
},
},
{
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)
{
id: 'holdStartTime',
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: 'holdEndTime',
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")...',
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: 'holdTerms',
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',
@@ -277,10 +438,14 @@ Return ONLY the description text - no explanations, no quotes, no extra text.`,
}
},
params: (params) => {
const { credential, ...rest } = params
const { credential, holdStartTime, holdEndTime, holdTerms, ...rest } = params
return {
...rest,
credential,
// Map hold-specific fields to their tool parameter names
...(holdStartTime && { startTime: holdStartTime }),
...(holdEndTime && { endTime: holdEndTime }),
...(holdTerms && { terms: holdTerms }),
}
},
},
@@ -296,9 +461,28 @@ 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' },
holdStartTime: {
type: 'string',
description: 'Start time for hold date filtering (ISO 8601 format, MAIL/GROUPS only)',
},
holdEndTime: {
type: 'string',
description: 'End time for hold date filtering (ISO 8601 format, MAIL/GROUPS only)',
},
holdTerms: {
type: 'string',
description: 'Search query terms for hold (MAIL/GROUPS only)',
},
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' },
@@ -316,12 +500,32 @@ Return ONLY the description text - no explanations, no quotes, no extra text.`,
description: { type: 'string', description: 'Matter description' },
},
outputs: {
matters: { type: 'json', description: 'Array of matter objects (for list_matters)' },
exports: { type: 'json', description: 'Array of export objects (for list_matters_export)' },
holds: { type: 'json', description: 'Array of hold objects (for list_matters_holds)' },
matter: { type: 'json', description: 'Created matter object (for create_matters)' },
export: { type: 'json', description: 'Created export object (for create_matters_export)' },
hold: { type: 'json', description: 'Created hold object (for create_matters_holds)' },
matters: {
type: 'json',
description: 'Array of matter objects (for list_matters without matterId)',
},
exports: {
type: 'json',
description: 'Array of export objects (for list_matters_export without exportId)',
},
holds: {
type: 'json',
description: 'Array of hold objects (for list_matters_holds without holdId)',
},
matter: {
type: 'json',
description: 'Single matter object (for create_matters or list_matters with matterId)',
},
export: {
type: 'json',
description:
'Single export object (for create_matters_export or list_matters_export with exportId)',
},
hold: {
type: 'json',
description:
'Single hold object (for create_matters_holds or list_matters_holds with holdId)',
},
file: { type: 'json', description: 'Downloaded export file (UserFile) from execution files' },
nextPageToken: {
type: 'string',
+29
View File
@@ -0,0 +1,29 @@
import { AgentBlockHandler } from '@/executor/handlers/agent/agent-handler'
import { ApiBlockHandler } from '@/executor/handlers/api/api-handler'
import { ConditionBlockHandler } from '@/executor/handlers/condition/condition-handler'
import { EvaluatorBlockHandler } from '@/executor/handlers/evaluator/evaluator-handler'
import { FunctionBlockHandler } from '@/executor/handlers/function/function-handler'
import { GenericBlockHandler } from '@/executor/handlers/generic/generic-handler'
import { HumanInTheLoopBlockHandler } from '@/executor/handlers/human-in-the-loop/human-in-the-loop-handler'
import { ResponseBlockHandler } from '@/executor/handlers/response/response-handler'
import { RouterBlockHandler } from '@/executor/handlers/router/router-handler'
import { TriggerBlockHandler } from '@/executor/handlers/trigger/trigger-handler'
import { VariablesBlockHandler } from '@/executor/handlers/variables/variables-handler'
import { WaitBlockHandler } from '@/executor/handlers/wait/wait-handler'
import { WorkflowBlockHandler } from '@/executor/handlers/workflow/workflow-handler'
export {
AgentBlockHandler,
ApiBlockHandler,
ConditionBlockHandler,
EvaluatorBlockHandler,
FunctionBlockHandler,
GenericBlockHandler,
ResponseBlockHandler,
HumanInTheLoopBlockHandler,
RouterBlockHandler,
TriggerBlockHandler,
VariablesBlockHandler,
WaitBlockHandler,
WorkflowBlockHandler,
}
+23 -13
View File
@@ -1,15 +1,9 @@
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',
id: 'google_vault_create_matters',
name: 'Vault Create Matter',
description: 'Create a new matter in Google Vault',
version: '1.0',
@@ -20,9 +14,24 @@ export const createMattersTool: ToolConfig<GoogleVaultCreateMattersParams> = {
},
params: {
accessToken: { type: 'string', required: true, visibility: 'hidden' },
name: { type: 'string', required: true, visibility: 'user-only' },
description: { type: 'string', required: false, visibility: 'user-only' },
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'OAuth access token',
},
name: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Name for the new matter',
},
description: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Optional description for the matter',
},
},
request: {
@@ -38,7 +47,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 } }
},
@@ -1,11 +1,10 @@
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)',
id: 'google_vault_create_matters_export',
name: 'Vault Create Export',
description: 'Create an export in a matter',
version: '1.0',
@@ -15,9 +14,24 @@ export const createMattersExportTool: ToolConfig<GoogleVaultCreateMattersExportP
},
params: {
accessToken: { type: 'string', required: true, visibility: 'hidden' },
matterId: { type: 'string', required: true, visibility: 'user-only' },
exportName: { type: 'string', required: true, visibility: 'user-only' },
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'OAuth access token',
},
matterId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'The matter ID',
},
exportName: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Name for the export (avoid special characters)',
},
corpus: {
type: 'string',
required: true,
@@ -36,6 +50,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: {
@@ -46,7 +78,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)) {
@@ -75,7 +106,6 @@ export const createMattersExportTool: ToolConfig<GoogleVaultCreateMattersExportP
terms: params.terms || undefined,
startTime: params.startTime || undefined,
endTime: params.endTime || undefined,
timeZone: params.timeZone || undefined,
...scope,
}
@@ -89,7 +119,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 } }
},
@@ -1,11 +1,10 @@
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)',
id: 'google_vault_create_matters_holds',
name: 'Vault Create Hold',
description: 'Create a hold in a matter',
version: '1.0',
@@ -15,9 +14,24 @@ export const createMattersHoldsTool: ToolConfig<GoogleVaultCreateMattersHoldsPar
},
params: {
accessToken: { type: 'string', required: true, visibility: 'hidden' },
matterId: { type: 'string', required: true, visibility: 'user-only' },
holdName: { type: 'string', required: true, visibility: 'user-only' },
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'OAuth access token',
},
matterId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'The matter ID',
},
holdName: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Name for the hold',
},
corpus: {
type: 'string',
required: true,
@@ -36,6 +50,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: {
@@ -46,13 +84,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)) {
@@ -66,12 +102,29 @@ 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 } }
}
return body
},
},
@@ -79,7 +132,8 @@ export const createMattersHoldsTool: ToolConfig<GoogleVaultCreateMattersHoldsPar
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 } }
},
@@ -1,17 +1,8 @@
import { createLogger } from '@sim/logger'
import type { GoogleVaultDownloadExportFileParams } from '@/tools/google_vault/types'
import { enhanceGoogleVaultError } from '@/tools/google_vault/utils'
import type { ToolConfig } from '@/tools/types'
const logger = createLogger('GoogleVaultDownloadExportFileTool')
interface DownloadParams {
accessToken: string
matterId: string
bucketName: string
objectName: string
fileName?: string
}
export const downloadExportFileTool: ToolConfig<DownloadParams> = {
export const downloadExportFileTool: ToolConfig<GoogleVaultDownloadExportFileParams> = {
id: 'google_vault_download_export_file',
name: 'Vault Download Export File',
description: 'Download a single file from a Google Vault export (GCS object)',
@@ -23,28 +14,51 @@ export const downloadExportFileTool: ToolConfig<DownloadParams> = {
},
params: {
accessToken: { type: 'string', required: true, visibility: 'hidden' },
matterId: { type: 'string', required: true, visibility: 'user-only' },
bucketName: { type: 'string', required: true, visibility: 'user-only' },
objectName: { type: 'string', required: true, visibility: 'user-only' },
fileName: { type: 'string', required: false, visibility: 'user-only' },
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'OAuth access token',
},
matterId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'The matter ID',
},
bucketName: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GCS bucket name from cloudStorageSink.files.bucketName',
},
objectName: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GCS object name from cloudStorageSink.files.objectName',
},
fileName: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Optional filename override for the downloaded file',
},
},
request: {
url: (params) => {
const bucket = encodeURIComponent(params.bucketName)
const object = encodeURIComponent(params.objectName)
// Use GCS media endpoint directly; framework will prefetch token and inject accessToken
return `https://storage.googleapis.com/storage/v1/b/${bucket}/o/${object}?alt=media`
},
method: 'GET',
headers: (params) => ({
// Access token is injected by the tools framework when 'credential' is present
Authorization: `Bearer ${params.accessToken}`,
}),
},
transformResponse: async (response: Response, params?: DownloadParams) => {
transformResponse: async (response: Response, params?: GoogleVaultDownloadExportFileParams) => {
if (!response.ok) {
let details: any
try {
@@ -57,10 +71,11 @@ export const downloadExportFileTool: ToolConfig<DownloadParams> = {
details = undefined
}
}
throw new Error(details?.error || `Failed to download Vault export file (${response.status})`)
const errorMessage =
details?.error || `Failed to download Vault export file (${response.status})`
throw new Error(enhanceGoogleVaultError(errorMessage))
}
// Since we're just doing a HEAD request to verify access, we need to fetch the actual file
if (!params?.accessToken || !params?.bucketName || !params?.objectName) {
throw new Error('Missing required parameters for download')
}
@@ -69,7 +84,6 @@ export const downloadExportFileTool: ToolConfig<DownloadParams> = {
const object = encodeURIComponent(params.objectName)
const downloadUrl = `https://storage.googleapis.com/storage/v1/b/${bucket}/o/${object}?alt=media`
// Fetch the actual file content
const downloadResponse = await fetch(downloadUrl, {
method: 'GET',
headers: {
@@ -79,7 +93,8 @@ export const downloadExportFileTool: ToolConfig<DownloadParams> = {
if (!downloadResponse.ok) {
const errorText = await downloadResponse.text().catch(() => '')
throw new Error(`Failed to download file: ${errorText || downloadResponse.statusText}`)
const errorMessage = `Failed to download file: ${errorText || downloadResponse.statusText}`
throw new Error(enhanceGoogleVaultError(errorMessage))
}
const contentType = downloadResponse.headers.get('content-type') || 'application/octet-stream'
@@ -104,7 +119,6 @@ export const downloadExportFileTool: ToolConfig<DownloadParams> = {
}
}
// Get the file as an array buffer and convert to Buffer
const arrayBuffer = await downloadResponse.arrayBuffer()
const buffer = Buffer.from(arrayBuffer)
+29 -13
View File
@@ -1,14 +1,9 @@
import type { GoogleVaultListMattersParams } from '@/tools/google_vault/types'
import { enhanceGoogleVaultError } from '@/tools/google_vault/utils'
import type { ToolConfig } from '@/tools/types'
export interface GoogleVaultListMattersParams {
accessToken: string
pageSize?: number
pageToken?: string
matterId?: string // Optional get for a specific matter
}
export const listMattersTool: ToolConfig<GoogleVaultListMattersParams> = {
id: 'list_matters',
id: 'google_vault_list_matters',
name: 'Vault List Matters',
description: 'List matters, or get a specific matter if matterId is provided',
version: '1.0',
@@ -19,10 +14,30 @@ export const listMattersTool: ToolConfig<GoogleVaultListMattersParams> = {
},
params: {
accessToken: { type: 'string', required: true, visibility: 'hidden' },
pageSize: { type: 'number', required: false, visibility: 'user-only' },
pageToken: { type: 'string', required: false, visibility: 'hidden' },
matterId: { type: 'string', required: false, visibility: 'user-only' },
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'OAuth access token',
},
pageSize: {
type: 'number',
required: false,
visibility: 'user-only',
description: 'Number of matters to return per page',
},
pageToken: {
type: 'string',
required: false,
visibility: 'hidden',
description: 'Token for pagination',
},
matterId: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Optional matter ID to fetch a specific matter',
},
},
request: {
@@ -47,7 +62,8 @@ export const listMattersTool: ToolConfig<GoogleVaultListMattersParams> = {
transformResponse: async (response: Response, params?: GoogleVaultListMattersParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error?.message || 'Failed to list matters')
const errorMessage = data.error?.message || 'Failed to list matters'
throw new Error(enhanceGoogleVaultError(errorMessage))
}
if (params?.matterId) {
return { success: true, output: { matter: data } }
@@ -1,9 +1,10 @@
import type { GoogleVaultListMattersExportParams } from '@/tools/google_vault/types'
import { enhanceGoogleVaultError } from '@/tools/google_vault/utils'
import type { ToolConfig } from '@/tools/types'
export const listMattersExportTool: ToolConfig<GoogleVaultListMattersExportParams> = {
id: 'list_matters_export',
name: 'Vault List Exports (by Matter)',
id: 'google_vault_list_matters_export',
name: 'Vault List Exports',
description: 'List exports for a matter',
version: '1.0',
@@ -13,11 +14,36 @@ export const listMattersExportTool: ToolConfig<GoogleVaultListMattersExportParam
},
params: {
accessToken: { type: 'string', required: true, visibility: 'hidden' },
matterId: { type: 'string', required: true, visibility: 'user-only' },
pageSize: { type: 'number', required: false, visibility: 'user-only' },
pageToken: { type: 'string', required: false, visibility: 'hidden' },
exportId: { type: 'string', required: false, visibility: 'user-only' },
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'OAuth access token',
},
matterId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'The matter ID',
},
pageSize: {
type: 'number',
required: false,
visibility: 'user-only',
description: 'Number of exports to return per page',
},
pageToken: {
type: 'string',
required: false,
visibility: 'hidden',
description: 'Token for pagination',
},
exportId: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Optional export ID to fetch a specific export',
},
},
request: {
@@ -42,7 +68,8 @@ export const listMattersExportTool: ToolConfig<GoogleVaultListMattersExportParam
transformResponse: async (response: Response, params?: GoogleVaultListMattersExportParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error?.message || 'Failed to list exports')
const errorMessage = data.error?.message || 'Failed to list exports'
throw new Error(enhanceGoogleVaultError(errorMessage))
}
if (params?.exportId) {
return { success: true, output: { export: data } }
@@ -1,9 +1,10 @@
import type { GoogleVaultListMattersHoldsParams } from '@/tools/google_vault/types'
import { enhanceGoogleVaultError } from '@/tools/google_vault/utils'
import type { ToolConfig } from '@/tools/types'
export const listMattersHoldsTool: ToolConfig<GoogleVaultListMattersHoldsParams> = {
id: 'list_matters_holds',
name: 'Vault List Holds (by Matter)',
id: 'google_vault_list_matters_holds',
name: 'Vault List Holds',
description: 'List holds for a matter',
version: '1.0',
@@ -13,11 +14,36 @@ export const listMattersHoldsTool: ToolConfig<GoogleVaultListMattersHoldsParams>
},
params: {
accessToken: { type: 'string', required: true, visibility: 'hidden' },
matterId: { type: 'string', required: true, visibility: 'user-only' },
pageSize: { type: 'number', required: false, visibility: 'user-only' },
pageToken: { type: 'string', required: false, visibility: 'hidden' },
holdId: { type: 'string', required: false, visibility: 'user-only' },
accessToken: {
type: 'string',
required: true,
visibility: 'hidden',
description: 'OAuth access token',
},
matterId: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'The matter ID',
},
pageSize: {
type: 'number',
required: false,
visibility: 'user-only',
description: 'Number of holds to return per page',
},
pageToken: {
type: 'string',
required: false,
visibility: 'hidden',
description: 'Token for pagination',
},
holdId: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Optional hold ID to fetch a specific hold',
},
},
request: {
@@ -42,7 +68,8 @@ export const listMattersHoldsTool: ToolConfig<GoogleVaultListMattersHoldsParams>
transformResponse: async (response: Response, params?: GoogleVaultListMattersHoldsParams) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error?.message || 'Failed to list holds')
const errorMessage = data.error?.message || 'Failed to list holds'
throw new Error(enhanceGoogleVaultError(errorMessage))
}
if (params?.holdId) {
return { success: true, output: { hold: data } }
+29 -8
View File
@@ -5,31 +5,48 @@ export interface GoogleVaultCommonParams {
matterId: string
}
// Exports
export interface GoogleVaultCreateMattersParams {
accessToken: string
name: string
description?: string
}
export interface GoogleVaultListMattersParams {
accessToken: string
pageSize?: number
pageToken?: string
matterId?: string
}
export interface GoogleVaultDownloadExportFileParams {
accessToken: string
matterId: string
bucketName: string
objectName: string
fileName?: string
}
export interface GoogleVaultCreateMattersExportParams extends GoogleVaultCommonParams {
exportName: string
corpus: GoogleVaultCorpus
accountEmails?: string // Comma-separated list or array handled in the tool
accountEmails?: string
orgUnitId?: string
terms?: string
startTime?: string
endTime?: string
timeZone?: string
includeSharedDrives?: boolean
}
export interface GoogleVaultListMattersExportParams extends GoogleVaultCommonParams {
pageSize?: number
pageToken?: string
exportId?: string // Short input to fetch a specific export
exportId?: string
}
export interface GoogleVaultListMattersExportResponse extends ToolResponse {
output: any
}
// Holds
// Simplified: default to BASIC_HOLD by omission in requests
export type GoogleVaultHoldView = 'BASIC_HOLD' | 'FULL_HOLD'
export type GoogleVaultCorpus = 'MAIL' | 'DRIVE' | 'GROUPS' | 'HANGOUTS_CHAT' | 'VOICE'
@@ -37,14 +54,18 @@ export type GoogleVaultCorpus = 'MAIL' | 'DRIVE' | 'GROUPS' | 'HANGOUTS_CHAT' |
export interface GoogleVaultCreateMattersHoldsParams extends GoogleVaultCommonParams {
holdName: string
corpus: GoogleVaultCorpus
accountEmails?: string // Comma-separated list or array handled in the tool
accountEmails?: string
orgUnitId?: string
terms?: string
startTime?: string
endTime?: string
includeSharedDrives?: boolean
}
export interface GoogleVaultListMattersHoldsParams extends GoogleVaultCommonParams {
pageSize?: number
pageToken?: string
holdId?: string // Short input to fetch a specific hold
holdId?: string
}
export interface GoogleVaultListMattersHoldsResponse extends ToolResponse {
+41
View File
@@ -0,0 +1,41 @@
/**
* Google Vault Error Enhancement Utilities
*
* Provides user-friendly error messages for common Google Vault authentication
* and credential issues, particularly RAPT (reauthentication policy) errors.
*/
/**
* Detects if an error message indicates a credential/reauthentication issue
*/
function isCredentialRefreshError(errorMessage: string): boolean {
const lowerMessage = errorMessage.toLowerCase()
return (
lowerMessage.includes('invalid_rapt') ||
lowerMessage.includes('reauth related error') ||
(lowerMessage.includes('invalid_grant') && lowerMessage.includes('rapt')) ||
lowerMessage.includes('failed to refresh token') ||
(lowerMessage.includes('failed to fetch access token') && lowerMessage.includes('401'))
)
}
/**
* Enhances Google Vault error messages with actionable guidance
*
* For credential/reauthentication errors (RAPT errors), provides specific
* instructions for resolving the issue through Google Admin Console settings.
*/
export function enhanceGoogleVaultError(errorMessage: string): string {
if (isCredentialRefreshError(errorMessage)) {
return (
`Google Vault authentication failed (likely due to reauthentication policy). ` +
`To resolve this, try disconnecting and reconnecting your Google Vault credential ` +
`in the Credentials settings. If the issue persists, ask your Google Workspace ` +
`administrator to disable "Reauthentication policy" for Sim Studio in the Google ` +
`Admin Console (Security > Access and data control > Context-Aware Access > ` +
`Reauthentication policy), or exempt Sim Studio from reauthentication requirements. ` +
`Learn more: https://support.google.com/a/answer/9368756`
)
}
return errorMessage
}