Compare commits

...

9 Commits

Author SHA1 Message Date
waleed
3979d09892 removed google vault from executor 2026-01-08 23:31:12 -08:00
aadamgough
79a93ed630 Restored icon file 2026-01-08 16:20:54 -08:00
aadamgough
58417ebaa8 restored 2026-01-08 16:19:40 -08:00
aadamgough
53cfd70e11 updated docs 2026-01-08 16:18:21 -08:00
aadamgough
59aca33ee3 added handler for vault 2026-01-08 16:17:19 -08:00
aadamgough
a00ec627ec restore 2026-01-08 16:03:12 -08:00
aadamgough
1fee00aae1 restore error thorwing 2026-01-08 16:02:24 -08:00
aadamgough
3beb18b76a fixed critical issues 2026-01-08 15:50:25 -08:00
aadamgough
ff1af41b37 new error throw and improvement 2026-01-08 15:11:50 -08:00
11 changed files with 334 additions and 58 deletions

View File

@@ -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
@@ -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

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: '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")...',
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',
@@ -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' },

View File

@@ -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',
@@ -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 } }
},

View File

@@ -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)',
@@ -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: {
@@ -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)) {
@@ -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,
}
@@ -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 } }
},

View File

@@ -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)',
@@ -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: {
@@ -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)) {
@@ -66,12 +87,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 +117,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 } }
},

View File

@@ -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)',
@@ -34,17 +25,15 @@ export const downloadExportFileTool: ToolConfig<DownloadParams> = {
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 +46,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 +59,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 +68,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 +94,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)

View File

@@ -1,12 +1,7 @@
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',
name: 'Vault List Matters',
@@ -47,7 +42,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 } }

View File

@@ -1,4 +1,5 @@
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> = {
@@ -42,7 +43,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 } }

View File

@@ -1,4 +1,5 @@
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> = {
@@ -42,7 +43,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 } }

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 {

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
}