@elevasis/core 0.17.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +82 -1
- package/dist/index.js +291 -171
- package/dist/knowledge/index.d.ts +43 -0
- package/dist/organization-model/index.d.ts +82 -1
- package/dist/organization-model/index.js +291 -171
- package/dist/test-utils/index.d.ts +41 -12
- package/dist/test-utils/index.js +291 -171
- package/package.json +2 -1
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +78 -65
- package/src/auth/multi-tenancy/organizations/__tests__/api-schemas.test.ts +194 -0
- package/src/auth/multi-tenancy/organizations/api-schemas.ts +136 -128
- package/src/business/acquisition/api-schemas.test.ts +100 -2
- package/src/business/acquisition/api-schemas.ts +81 -43
- package/src/business/acquisition/build-templates.test.ts +212 -0
- package/src/business/acquisition/types.ts +21 -38
- package/src/execution/engine/index.ts +436 -434
- package/src/execution/engine/tools/integration/server/adapters/google-calendar/google-calendar-adapter.ts +428 -0
- package/src/execution/engine/tools/integration/server/adapters/google-calendar/index.ts +2 -0
- package/src/execution/engine/tools/lead-service-types.ts +51 -9
- package/src/execution/engine/tools/platform/acquisition/company-tools.ts +7 -6
- package/src/execution/engine/tools/platform/acquisition/contact-tools.ts +6 -5
- package/src/execution/engine/tools/platform/acquisition/types.ts +20 -9
- package/src/execution/engine/tools/registry.ts +700 -698
- package/src/execution/engine/tools/tool-maps.ts +10 -0
- package/src/execution/external/__tests__/api-schemas.test.ts +127 -0
- package/src/integrations/oauth/__tests__/provider-registry.test.ts +7 -6
- package/src/integrations/oauth/provider-registry.ts +74 -61
- package/src/integrations/oauth/server/credentials.ts +43 -39
- package/src/knowledge/__tests__/queries.test.ts +89 -0
- package/src/organization-model/__tests__/icons.test.ts +61 -0
- package/src/organization-model/__tests__/knowledge.test.ts +118 -1
- package/src/organization-model/__tests__/prospecting-ssot.test.ts +94 -0
- package/src/organization-model/defaults.ts +8 -0
- package/src/organization-model/domains/knowledge.ts +9 -0
- package/src/organization-model/domains/prospecting.ts +272 -226
- package/src/organization-model/domains/sales.ts +32 -25
- package/src/organization-model/icons.ts +3 -0
- package/src/organization-model/types.ts +9 -1
- package/src/platform/constants/versions.ts +1 -1
- package/src/platform/utils/__tests__/validation.test.ts +1084 -1083
- package/src/platform/utils/validation.ts +425 -425
- package/src/reference/_generated/contracts.md +78 -65
- package/src/server.ts +6 -0
- package/src/supabase/database.types.ts +6 -12
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
import type { calendar_v3 } from '@googleapis/calendar'
|
|
2
|
+
import type { BaseIntegrationAdapter } from '../../../base-integration-adapter'
|
|
3
|
+
import { ToolingError } from '../../../../types'
|
|
4
|
+
import type { ExecutionContext } from '../../../../../base/types'
|
|
5
|
+
import { getOAuthCredentials } from '../../../../../../../integrations/oauth/server/credentials'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Google Calendar credentials format
|
|
9
|
+
* Stored in credentials table, encrypted
|
|
10
|
+
*/
|
|
11
|
+
interface GoogleCalendarCredentials {
|
|
12
|
+
accessToken: string
|
|
13
|
+
refreshToken?: string
|
|
14
|
+
expiresAt?: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Date/time value from Google Calendar API
|
|
19
|
+
*/
|
|
20
|
+
export interface CalendarDateTime {
|
|
21
|
+
dateTime?: string // ISO 8601 datetime (when time is specified)
|
|
22
|
+
date?: string // YYYY-MM-DD (for all-day events)
|
|
23
|
+
timeZone?: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A Google Calendar event (plausible-use fields only)
|
|
28
|
+
*/
|
|
29
|
+
export interface CalendarEvent {
|
|
30
|
+
id: string
|
|
31
|
+
summary?: string
|
|
32
|
+
description?: string
|
|
33
|
+
location?: string
|
|
34
|
+
start: CalendarDateTime
|
|
35
|
+
end: CalendarDateTime
|
|
36
|
+
status?: 'confirmed' | 'tentative' | 'cancelled'
|
|
37
|
+
htmlLink?: string
|
|
38
|
+
created?: string
|
|
39
|
+
updated?: string
|
|
40
|
+
organizer?: { email?: string; displayName?: string }
|
|
41
|
+
attendees?: Array<{ email?: string; displayName?: string; responseStatus?: string }>
|
|
42
|
+
recurringEventId?: string
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* listEvents parameters.
|
|
47
|
+
*
|
|
48
|
+
* `credentialName` is required when calling the adapter directly (HTTP-handler
|
|
49
|
+
* path). It is ignored on the dispatch path, where credentials are injected
|
|
50
|
+
* by the execution-engine pipeline.
|
|
51
|
+
*/
|
|
52
|
+
interface ListEventsParams {
|
|
53
|
+
organizationId: string
|
|
54
|
+
credentialName?: string
|
|
55
|
+
calendarId?: string
|
|
56
|
+
timeMin?: string
|
|
57
|
+
timeMax?: string
|
|
58
|
+
maxResults?: number
|
|
59
|
+
singleEvents?: boolean
|
|
60
|
+
orderBy?: 'startTime' | 'updated'
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* listEvents result
|
|
65
|
+
*/
|
|
66
|
+
export interface ListEventsResult {
|
|
67
|
+
items: CalendarEvent[]
|
|
68
|
+
nextPageToken?: string
|
|
69
|
+
summary?: string
|
|
70
|
+
timeZone?: string
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* getEvent parameters
|
|
75
|
+
*/
|
|
76
|
+
interface GetEventParams {
|
|
77
|
+
organizationId: string
|
|
78
|
+
credentialName?: string
|
|
79
|
+
calendarId?: string
|
|
80
|
+
eventId: string
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Google Calendar adapter for calendar and event operations via Google Calendar API v3
|
|
85
|
+
*
|
|
86
|
+
* Read-only methods:
|
|
87
|
+
* - listEvents: List events from a calendar
|
|
88
|
+
* - getEvent: Get a single event by ID
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* const adapter = new GoogleCalendarAdapter()
|
|
92
|
+
* const result = await adapter.listEvents({
|
|
93
|
+
* organizationId: 'org-uuid',
|
|
94
|
+
* calendarId: 'primary',
|
|
95
|
+
* maxResults: 10
|
|
96
|
+
* })
|
|
97
|
+
*/
|
|
98
|
+
export class GoogleCalendarAdapter implements BaseIntegrationAdapter {
|
|
99
|
+
readonly name = 'google-calendar'
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Call Google Calendar API method
|
|
103
|
+
* @param method - Method name ('listEvents' | 'getEvent')
|
|
104
|
+
* @param params - Method parameters (must include organizationId)
|
|
105
|
+
* @param credentials - OAuth2 access token
|
|
106
|
+
* @param context - Execution context for logging and tracing
|
|
107
|
+
*/
|
|
108
|
+
async call(
|
|
109
|
+
method: string,
|
|
110
|
+
params: unknown,
|
|
111
|
+
credentials: Record<string, unknown>,
|
|
112
|
+
context?: ExecutionContext
|
|
113
|
+
): Promise<unknown> {
|
|
114
|
+
if (!this.validateCredentials(credentials)) {
|
|
115
|
+
throw new ToolingError('credentials_invalid', 'Invalid Google Calendar credentials', {
|
|
116
|
+
integration: 'google-calendar',
|
|
117
|
+
method
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
const calCreds = credentials as unknown as GoogleCalendarCredentials
|
|
121
|
+
|
|
122
|
+
const client = await this.createClient(calCreds)
|
|
123
|
+
|
|
124
|
+
switch (method) {
|
|
125
|
+
case 'listEvents':
|
|
126
|
+
return this.listEvents(client, params as ListEventsParams, context)
|
|
127
|
+
case 'getEvent':
|
|
128
|
+
return this.getEvent(client, params as GetEventParams, context)
|
|
129
|
+
default:
|
|
130
|
+
throw new ToolingError('method_not_found', `Unknown method: ${method}`, {
|
|
131
|
+
integration: 'google-calendar',
|
|
132
|
+
method
|
|
133
|
+
})
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* List events from a calendar — callable directly (no method dispatch overhead)
|
|
139
|
+
*/
|
|
140
|
+
async listEvents(
|
|
141
|
+
clientOrParams: calendar_v3.Calendar | ListEventsParams,
|
|
142
|
+
paramsOrContext?: ListEventsParams | ExecutionContext,
|
|
143
|
+
maybeContext?: ExecutionContext
|
|
144
|
+
): Promise<ListEventsResult> {
|
|
145
|
+
// Support both direct call (listEvents(params, context)) and
|
|
146
|
+
// dispatch call (listEvents(client, params, context))
|
|
147
|
+
let client: calendar_v3.Calendar
|
|
148
|
+
let params: ListEventsParams
|
|
149
|
+
let context: ExecutionContext | undefined
|
|
150
|
+
|
|
151
|
+
if ('calendarId' in (clientOrParams as object) || 'organizationId' in (clientOrParams as object)) {
|
|
152
|
+
// Called directly: listEvents(params, context?)
|
|
153
|
+
params = clientOrParams as ListEventsParams
|
|
154
|
+
context = paramsOrContext as ExecutionContext | undefined
|
|
155
|
+
if (!params.credentialName) {
|
|
156
|
+
throw new ToolingError('validation_error', 'Missing required field: credentialName', { params })
|
|
157
|
+
}
|
|
158
|
+
const creds = await this.getCredentialsForOrg(params.organizationId, params.credentialName)
|
|
159
|
+
client = await this.createClient(creds)
|
|
160
|
+
} else {
|
|
161
|
+
// Called via dispatch: listEvents(client, params, context?)
|
|
162
|
+
client = clientOrParams as calendar_v3.Calendar
|
|
163
|
+
params = paramsOrContext as ListEventsParams
|
|
164
|
+
context = maybeContext
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return this._listEvents(client, params, context)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Get a single event by ID — callable directly
|
|
172
|
+
*/
|
|
173
|
+
async getEvent(
|
|
174
|
+
clientOrParams: calendar_v3.Calendar | GetEventParams,
|
|
175
|
+
paramsOrContext?: GetEventParams | ExecutionContext,
|
|
176
|
+
maybeContext?: ExecutionContext
|
|
177
|
+
): Promise<CalendarEvent> {
|
|
178
|
+
let client: calendar_v3.Calendar
|
|
179
|
+
let params: GetEventParams
|
|
180
|
+
let context: ExecutionContext | undefined
|
|
181
|
+
|
|
182
|
+
if ('eventId' in (clientOrParams as object) || 'organizationId' in (clientOrParams as object)) {
|
|
183
|
+
// Called directly: getEvent(params, context?)
|
|
184
|
+
params = clientOrParams as GetEventParams
|
|
185
|
+
context = paramsOrContext as ExecutionContext | undefined
|
|
186
|
+
if (!params.credentialName) {
|
|
187
|
+
throw new ToolingError('validation_error', 'Missing required field: credentialName', { params })
|
|
188
|
+
}
|
|
189
|
+
const creds = await this.getCredentialsForOrg(params.organizationId, params.credentialName)
|
|
190
|
+
client = await this.createClient(creds)
|
|
191
|
+
} else {
|
|
192
|
+
// Called via dispatch: getEvent(client, params, context?)
|
|
193
|
+
client = clientOrParams as calendar_v3.Calendar
|
|
194
|
+
params = paramsOrContext as GetEventParams
|
|
195
|
+
context = maybeContext
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return this._getEvent(client, params, context)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Create authenticated Google Calendar client (lazy loads @googleapis/calendar SDK)
|
|
203
|
+
* CRITICAL: Use dynamic import to prevent OOM and module-level crashes
|
|
204
|
+
*/
|
|
205
|
+
private async createClient(creds: GoogleCalendarCredentials): Promise<calendar_v3.Calendar> {
|
|
206
|
+
const { calendar } = await import('@googleapis/calendar')
|
|
207
|
+
const { OAuth2Client } = await import('google-auth-library')
|
|
208
|
+
|
|
209
|
+
// Get client credentials from environment for token refresh
|
|
210
|
+
const { clientId, clientSecret } = getOAuthCredentials('google-calendar')
|
|
211
|
+
|
|
212
|
+
const oauth2Client = new OAuth2Client(clientId, clientSecret)
|
|
213
|
+
oauth2Client.setCredentials({
|
|
214
|
+
access_token: creds.accessToken,
|
|
215
|
+
refresh_token: creds.refreshToken
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
return calendar({ version: 'v3', auth: oauth2Client })
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Retrieve credentials for an organization from the credentials table.
|
|
223
|
+
* Used when the adapter is called directly (not via the dispatch pipeline).
|
|
224
|
+
* The caller passes the credential name explicitly -- names are user-chosen
|
|
225
|
+
* at OAuth-modal time, so they cannot be hardcoded.
|
|
226
|
+
*/
|
|
227
|
+
private async getCredentialsForOrg(
|
|
228
|
+
organizationId: string,
|
|
229
|
+
credentialName: string
|
|
230
|
+
): Promise<GoogleCalendarCredentials> {
|
|
231
|
+
// Dynamic import avoids pulling Supabase into browser bundles
|
|
232
|
+
const { createClient } = await import('@supabase/supabase-js')
|
|
233
|
+
const { decryptCredentialValue } = await import(
|
|
234
|
+
'../../../../../../../auth/multi-tenancy/credentials/server/service'
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
const supabaseUrl = process.env.SUPABASE_URL
|
|
238
|
+
const supabaseKey = process.env.SUPABASE_SERVICE_KEY
|
|
239
|
+
if (!supabaseUrl || !supabaseKey) {
|
|
240
|
+
throw new ToolingError('credentials_invalid', 'Missing Supabase environment variables', { organizationId })
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const supabase = createClient(supabaseUrl, supabaseKey)
|
|
244
|
+
const { data, error } = await supabase
|
|
245
|
+
.from('credentials')
|
|
246
|
+
.select('encrypted_value')
|
|
247
|
+
.eq('organization_id', organizationId)
|
|
248
|
+
.eq('name', credentialName)
|
|
249
|
+
.single()
|
|
250
|
+
|
|
251
|
+
if (error || !data) {
|
|
252
|
+
throw new ToolingError('credentials_invalid', 'Google Calendar credential not found', {
|
|
253
|
+
organizationId,
|
|
254
|
+
credentialName
|
|
255
|
+
})
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return decryptCredentialValue(data.encrypted_value) as unknown as GoogleCalendarCredentials
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Internal list events implementation
|
|
263
|
+
*/
|
|
264
|
+
private async _listEvents(
|
|
265
|
+
client: calendar_v3.Calendar,
|
|
266
|
+
params: ListEventsParams,
|
|
267
|
+
context?: ExecutionContext
|
|
268
|
+
): Promise<ListEventsResult> {
|
|
269
|
+
if (!params.organizationId) {
|
|
270
|
+
throw new ToolingError('validation_error', 'Missing required field: organizationId', { params })
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const calendarId = params.calendarId ?? 'primary'
|
|
274
|
+
const maxResults = Math.min(params.maxResults ?? 10, 250)
|
|
275
|
+
const singleEvents = params.singleEvents ?? true
|
|
276
|
+
const orderBy = params.orderBy ?? 'startTime'
|
|
277
|
+
const timeMin = params.timeMin ?? new Date().toISOString()
|
|
278
|
+
|
|
279
|
+
try {
|
|
280
|
+
const response = await client.events.list({
|
|
281
|
+
calendarId,
|
|
282
|
+
timeMin,
|
|
283
|
+
timeMax: params.timeMax,
|
|
284
|
+
maxResults,
|
|
285
|
+
singleEvents,
|
|
286
|
+
orderBy
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
const items = (response.data.items ?? []).map(this.transformEvent)
|
|
290
|
+
|
|
291
|
+
if (context?.logger) {
|
|
292
|
+
context.logger.info(
|
|
293
|
+
`[GoogleCalendarAdapter] Events listed: organizationId=${context.organizationId} executionId=${context.executionId} calendarId=${calendarId} count=${items.length}`
|
|
294
|
+
)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return {
|
|
298
|
+
items,
|
|
299
|
+
nextPageToken: response.data.nextPageToken ?? undefined,
|
|
300
|
+
summary: response.data.summary ?? undefined,
|
|
301
|
+
timeZone: response.data.timeZone ?? undefined
|
|
302
|
+
}
|
|
303
|
+
} catch (error: any) {
|
|
304
|
+
throw this.handleApiError(error, calendarId)
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Internal get event implementation
|
|
310
|
+
*/
|
|
311
|
+
private async _getEvent(
|
|
312
|
+
client: calendar_v3.Calendar,
|
|
313
|
+
params: GetEventParams,
|
|
314
|
+
context?: ExecutionContext
|
|
315
|
+
): Promise<CalendarEvent> {
|
|
316
|
+
if (!params.organizationId) {
|
|
317
|
+
throw new ToolingError('validation_error', 'Missing required field: organizationId', { params })
|
|
318
|
+
}
|
|
319
|
+
if (!params.eventId) {
|
|
320
|
+
throw new ToolingError('validation_error', 'Missing required field: eventId', { params })
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const calendarId = params.calendarId ?? 'primary'
|
|
324
|
+
|
|
325
|
+
try {
|
|
326
|
+
const response = await client.events.get({
|
|
327
|
+
calendarId,
|
|
328
|
+
eventId: params.eventId
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
const event = this.transformEvent(response.data)
|
|
332
|
+
|
|
333
|
+
if (context?.logger) {
|
|
334
|
+
context.logger.info(
|
|
335
|
+
`[GoogleCalendarAdapter] Event retrieved: organizationId=${context.organizationId} executionId=${context.executionId} calendarId=${calendarId} eventId=${params.eventId}`
|
|
336
|
+
)
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return event
|
|
340
|
+
} catch (error: any) {
|
|
341
|
+
throw this.handleApiError(error, calendarId)
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Transform a raw Google Calendar API event into the typed CalendarEvent shape
|
|
347
|
+
*/
|
|
348
|
+
private transformEvent(raw: calendar_v3.Schema$Event): CalendarEvent {
|
|
349
|
+
return {
|
|
350
|
+
id: raw.id ?? '',
|
|
351
|
+
summary: raw.summary ?? undefined,
|
|
352
|
+
description: raw.description ?? undefined,
|
|
353
|
+
location: raw.location ?? undefined,
|
|
354
|
+
start: {
|
|
355
|
+
dateTime: raw.start?.dateTime ?? undefined,
|
|
356
|
+
date: raw.start?.date ?? undefined,
|
|
357
|
+
timeZone: raw.start?.timeZone ?? undefined
|
|
358
|
+
},
|
|
359
|
+
end: {
|
|
360
|
+
dateTime: raw.end?.dateTime ?? undefined,
|
|
361
|
+
date: raw.end?.date ?? undefined,
|
|
362
|
+
timeZone: raw.end?.timeZone ?? undefined
|
|
363
|
+
},
|
|
364
|
+
status: (raw.status as CalendarEvent['status']) ?? undefined,
|
|
365
|
+
htmlLink: raw.htmlLink ?? undefined,
|
|
366
|
+
created: raw.created ?? undefined,
|
|
367
|
+
updated: raw.updated ?? undefined,
|
|
368
|
+
organizer: raw.organizer
|
|
369
|
+
? {
|
|
370
|
+
email: raw.organizer.email ?? undefined,
|
|
371
|
+
displayName: raw.organizer.displayName ?? undefined
|
|
372
|
+
}
|
|
373
|
+
: undefined,
|
|
374
|
+
attendees: raw.attendees?.map((a) => ({
|
|
375
|
+
email: a.email ?? undefined,
|
|
376
|
+
displayName: a.displayName ?? undefined,
|
|
377
|
+
responseStatus: a.responseStatus ?? undefined
|
|
378
|
+
})),
|
|
379
|
+
recurringEventId: raw.recurringEventId ?? undefined
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Handle Google Calendar API errors with meaningful messages
|
|
385
|
+
*/
|
|
386
|
+
private handleApiError(error: any, calendarId: string): ToolingError {
|
|
387
|
+
const code = error.code || error.status || error.response?.status
|
|
388
|
+
|
|
389
|
+
if (code === 404) {
|
|
390
|
+
return new ToolingError('api_error', `Calendar or event not found: ${calendarId}`, {
|
|
391
|
+
calendarId,
|
|
392
|
+
statusCode: 404
|
|
393
|
+
})
|
|
394
|
+
}
|
|
395
|
+
if (code === 403) {
|
|
396
|
+
return new ToolingError('permission_denied', 'Access denied. Check calendar sharing permissions.', {
|
|
397
|
+
calendarId,
|
|
398
|
+
statusCode: 403
|
|
399
|
+
})
|
|
400
|
+
}
|
|
401
|
+
if (code === 401) {
|
|
402
|
+
return new ToolingError('credentials_invalid', 'Authentication expired. Token refresh required.', {
|
|
403
|
+
calendarId,
|
|
404
|
+
statusCode: 401
|
|
405
|
+
})
|
|
406
|
+
}
|
|
407
|
+
if (code === 429) {
|
|
408
|
+
return new ToolingError('rate_limit_exceeded', 'Rate limited. Retry after delay.', {
|
|
409
|
+
calendarId,
|
|
410
|
+
statusCode: 429
|
|
411
|
+
})
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
return new ToolingError('api_error', `Google Calendar API error: ${error.message}`, {
|
|
415
|
+
calendarId,
|
|
416
|
+
statusCode: code,
|
|
417
|
+
details: error
|
|
418
|
+
})
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Validate credentials structure
|
|
423
|
+
* Required by BaseIntegrationAdapter interface
|
|
424
|
+
*/
|
|
425
|
+
validateCredentials(creds: Record<string, unknown>): boolean {
|
|
426
|
+
return typeof creds.accessToken === 'string' && creds.accessToken.length > 0
|
|
427
|
+
}
|
|
428
|
+
}
|
|
@@ -18,6 +18,7 @@ import type {
|
|
|
18
18
|
ScrapingConfig,
|
|
19
19
|
IcpRubric,
|
|
20
20
|
PipelineConfig,
|
|
21
|
+
ProcessingState,
|
|
21
22
|
ListTelemetry,
|
|
22
23
|
DealDetail
|
|
23
24
|
} from '../../../business/acquisition/types'
|
|
@@ -33,6 +34,7 @@ export type {
|
|
|
33
34
|
ScrapingConfig,
|
|
34
35
|
IcpRubric,
|
|
35
36
|
PipelineConfig,
|
|
37
|
+
ProcessingState,
|
|
36
38
|
ProcessingStageStatus,
|
|
37
39
|
ListTelemetry,
|
|
38
40
|
DealDetail,
|
|
@@ -95,6 +97,8 @@ export interface CreateCompanyParams {
|
|
|
95
97
|
source?: string
|
|
96
98
|
batchId?: string
|
|
97
99
|
verticalResearch?: string
|
|
100
|
+
/** @deprecated Use processingState. Accepted as a no-op compatibility bridge for external tenants. */
|
|
101
|
+
pipelineStatus?: unknown
|
|
98
102
|
}
|
|
99
103
|
|
|
100
104
|
export interface UpdateCompanyParams {
|
|
@@ -108,7 +112,9 @@ export interface UpdateCompanyParams {
|
|
|
108
112
|
locationState?: string
|
|
109
113
|
category?: string
|
|
110
114
|
segment?: string
|
|
111
|
-
|
|
115
|
+
processingState?: ProcessingState
|
|
116
|
+
/** @deprecated Use processingState. Accepted as a no-op compatibility bridge for external tenants. */
|
|
117
|
+
pipelineStatus?: unknown
|
|
112
118
|
enrichmentData?: Record<string, unknown>
|
|
113
119
|
source?: string
|
|
114
120
|
batchId?: string
|
|
@@ -116,7 +122,7 @@ export interface UpdateCompanyParams {
|
|
|
116
122
|
verticalResearch?: string | null
|
|
117
123
|
/** Track A: flat qualification score column (null until a scoring rubric is defined) */
|
|
118
124
|
qualificationScore?: number | null
|
|
119
|
-
/** Track A: flat qualification signals jsonb
|
|
125
|
+
/** Track A: flat qualification signals jsonb */
|
|
120
126
|
qualificationSignals?: Record<string, unknown> | null
|
|
121
127
|
/** Track A: key identifying the rubric used for qualification */
|
|
122
128
|
qualificationRubricKey?: string | null
|
|
@@ -132,13 +138,15 @@ export interface CompanyFilters {
|
|
|
132
138
|
website?: string
|
|
133
139
|
segment?: string
|
|
134
140
|
category?: string
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
|
|
141
|
+
processingState?: ProcessingState
|
|
142
|
+
/** @deprecated Use processingState. Accepted as a no-op compatibility bridge for external tenants. */
|
|
143
|
+
pipelineStatus?: unknown
|
|
144
|
+
/** Exclude companies whose processing state contains this value (PostgREST NOT contains) */
|
|
145
|
+
processingStateNot?: ProcessingState
|
|
138
146
|
batchId?: string
|
|
139
147
|
status?: 'active' | 'invalid'
|
|
140
148
|
includeAll?: boolean
|
|
141
|
-
excludeColumns?: Array<'enrichmentData' | '
|
|
149
|
+
excludeColumns?: Array<'enrichmentData' | 'processingState'>
|
|
142
150
|
limit?: number
|
|
143
151
|
}
|
|
144
152
|
|
|
@@ -154,6 +162,8 @@ export interface CreateContactParams {
|
|
|
154
162
|
source?: string
|
|
155
163
|
sourceId?: string
|
|
156
164
|
batchId?: string
|
|
165
|
+
/** @deprecated Use processingState. Accepted as a no-op compatibility bridge for external tenants. */
|
|
166
|
+
pipelineStatus?: unknown
|
|
157
167
|
}
|
|
158
168
|
|
|
159
169
|
export interface UpdateContactParams {
|
|
@@ -166,7 +176,9 @@ export interface UpdateContactParams {
|
|
|
166
176
|
headline?: string
|
|
167
177
|
filterReason?: string
|
|
168
178
|
openingLine?: string
|
|
169
|
-
|
|
179
|
+
processingState?: ProcessingState
|
|
180
|
+
/** @deprecated Use processingState. Accepted as a no-op compatibility bridge for external tenants. */
|
|
181
|
+
pipelineStatus?: unknown
|
|
170
182
|
enrichmentData?: Record<string, unknown>
|
|
171
183
|
status?: 'active' | 'invalid'
|
|
172
184
|
}
|
|
@@ -178,7 +190,9 @@ export interface ContactFilters {
|
|
|
178
190
|
listId?: string // Filter to contacts in a specific list (via acq_list_members)
|
|
179
191
|
search?: string
|
|
180
192
|
openingLineIsNull?: boolean // Filter to contacts without personalization
|
|
181
|
-
|
|
193
|
+
processingState?: ProcessingState
|
|
194
|
+
/** @deprecated Use processingState. Accepted as a no-op compatibility bridge for external tenants. */
|
|
195
|
+
pipelineStatus?: unknown
|
|
182
196
|
batchId?: string
|
|
183
197
|
contactStatus?: 'active' | 'invalid' // Filter by contact status (soft-delete flag)
|
|
184
198
|
}
|
|
@@ -533,6 +547,20 @@ export interface UpdateContactStageParams {
|
|
|
533
547
|
executionId?: string
|
|
534
548
|
}
|
|
535
549
|
|
|
550
|
+
export interface ListPendingCompanyIdsParams {
|
|
551
|
+
organizationId: string
|
|
552
|
+
listId: string
|
|
553
|
+
stageKey: string
|
|
554
|
+
limit?: number
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
export interface ListPendingContactIdsParams {
|
|
558
|
+
organizationId: string
|
|
559
|
+
listId: string
|
|
560
|
+
stageKey: string
|
|
561
|
+
limit?: number
|
|
562
|
+
}
|
|
563
|
+
|
|
536
564
|
export interface AddCompaniesToListParams {
|
|
537
565
|
organizationId: string
|
|
538
566
|
listId: string
|
|
@@ -593,7 +621,7 @@ export interface BulkImportCompanyEntry {
|
|
|
593
621
|
category?: string
|
|
594
622
|
source?: string
|
|
595
623
|
enrichmentData?: Record<string, unknown>
|
|
596
|
-
|
|
624
|
+
processingState?: ProcessingState
|
|
597
625
|
}
|
|
598
626
|
|
|
599
627
|
export interface BulkImportCompaniesParams {
|
|
@@ -694,6 +722,20 @@ export interface ILeadService {
|
|
|
694
722
|
*/
|
|
695
723
|
updateContactStage(params: UpdateContactStageParams): Promise<void>
|
|
696
724
|
|
|
725
|
+
/**
|
|
726
|
+
* Return the company_ids in acq_list_companies for the given list where
|
|
727
|
+
* processing_state[stageKey].status is NULL or NOT IN ('success', 'no_result').
|
|
728
|
+
* Used by build-pipeline workflows to skip already-processed records.
|
|
729
|
+
*/
|
|
730
|
+
listPendingCompanyIds(params: ListPendingCompanyIdsParams): Promise<string[]>
|
|
731
|
+
|
|
732
|
+
/**
|
|
733
|
+
* Return the contact_ids in acq_list_members for the given list where
|
|
734
|
+
* processing_state[stageKey].status is NULL or NOT IN ('success', 'no_result').
|
|
735
|
+
* Used by build-pipeline workflows to skip already-processed records.
|
|
736
|
+
*/
|
|
737
|
+
listPendingContactIds(params: ListPendingContactIdsParams): Promise<string[]>
|
|
738
|
+
|
|
697
739
|
/**
|
|
698
740
|
* Per-list execution history — reads via the feature-owned
|
|
699
741
|
* acq_list_executions junction, joined to execution_logs for details.
|
|
@@ -35,11 +35,12 @@ function toCompanyOutput(result: any) {
|
|
|
35
35
|
foundedYear: result.foundedYear,
|
|
36
36
|
locationCity: result.locationCity,
|
|
37
37
|
locationState: result.locationState,
|
|
38
|
-
category: result.category,
|
|
39
|
-
categoryPain: result.categoryPain ?? null,
|
|
40
|
-
segment: result.segment,
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
category: result.category,
|
|
39
|
+
categoryPain: result.categoryPain ?? null,
|
|
40
|
+
segment: result.segment,
|
|
41
|
+
processingState: result.processingState,
|
|
42
|
+
pipelineStatus: null,
|
|
43
|
+
enrichmentData: result.enrichmentData,
|
|
43
44
|
tags: result.tags,
|
|
44
45
|
source: result.source,
|
|
45
46
|
batchId: result.batchId ?? null,
|
|
@@ -199,7 +200,7 @@ export function createAcqCompanyGetTool(): Tool {
|
|
|
199
200
|
export function createAcqCompanyListTool(): Tool {
|
|
200
201
|
return {
|
|
201
202
|
name: 'acq_company_list',
|
|
202
|
-
description: 'List companies with optional filters for category, segment, or
|
|
203
|
+
description: 'List companies with optional filters for category, segment, or processing state.',
|
|
203
204
|
inputSchema: ListCompaniesInputSchema,
|
|
204
205
|
outputSchema: CompanyArrayOutputSchema,
|
|
205
206
|
execute: async ({ input, executionContext }) => {
|
|
@@ -43,11 +43,12 @@ function toContactOutput(result: any) {
|
|
|
43
43
|
title: result.title,
|
|
44
44
|
headline: result.headline,
|
|
45
45
|
filterReason: result.filterReason,
|
|
46
|
-
openingLine: result.openingLine,
|
|
47
|
-
source: result.source,
|
|
48
|
-
sourceId: result.sourceId,
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
openingLine: result.openingLine,
|
|
47
|
+
source: result.source,
|
|
48
|
+
sourceId: result.sourceId,
|
|
49
|
+
processingState: result.processingState,
|
|
50
|
+
pipelineStatus: null,
|
|
51
|
+
enrichmentData: result.enrichmentData,
|
|
51
52
|
tags: result.tags,
|
|
52
53
|
batchId: result.batchId ?? null,
|
|
53
54
|
status: result.status ?? 'active',
|