@elevasis/sdk 1.24.0 → 1.26.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.
Files changed (66) hide show
  1. package/dist/cli.cjs +875 -834
  2. package/dist/index.d.ts +4857 -4547
  3. package/dist/index.js +564 -2338
  4. package/dist/node/index.d.ts +693 -1356
  5. package/dist/node/index.js +1 -1
  6. package/dist/test-utils/index.d.ts +4186 -4139
  7. package/dist/test-utils/index.js +694 -2769
  8. package/dist/types/worker/adapters/clickup.d.ts +22 -0
  9. package/dist/types/worker/adapters/index.d.ts +1 -0
  10. package/dist/types/worker/index.d.ts +3 -2
  11. package/dist/types/worker/platform.d.ts +2 -2
  12. package/dist/worker/index.js +427 -2803
  13. package/package.json +2 -2
  14. package/reference/_navigation.md +11 -1
  15. package/reference/_reference-manifest.json +70 -0
  16. package/reference/claude-config/rules/organization-model.md +12 -1
  17. package/reference/claude-config/rules/organization-os.md +12 -1
  18. package/reference/claude-config/skills/om/SKILL.md +13 -5
  19. package/reference/claude-config/skills/om/operations/codify-level-a.md +109 -100
  20. package/reference/claude-config/skills/om/operations/customers.md +10 -6
  21. package/reference/claude-config/skills/om/operations/features.md +7 -3
  22. package/reference/claude-config/skills/om/operations/goals.md +10 -6
  23. package/reference/claude-config/skills/om/operations/identity.md +8 -5
  24. package/reference/claude-config/skills/om/operations/labels.md +17 -1
  25. package/reference/claude-config/skills/om/operations/offerings.md +11 -7
  26. package/reference/claude-config/skills/om/operations/roles.md +11 -7
  27. package/reference/claude-config/skills/om/operations/techStack.md +10 -2
  28. package/reference/claude-config/sync-notes/2026-05-20-om-define-helpers.md +32 -0
  29. package/reference/claude-config/sync-notes/2026-05-22-access-model-and-right-panel.md +43 -0
  30. package/reference/claude-config/sync-notes/2026-05-22-lead-gen-tenant-config.md +40 -0
  31. package/reference/claude-config/sync-notes/2026-05-22-org-model-multi-file-split.md +61 -0
  32. package/reference/cli-management.mdx +539 -0
  33. package/reference/cli.mdx +579 -808
  34. package/reference/concepts.mdx +134 -146
  35. package/reference/deployment/api.mdx +296 -297
  36. package/reference/deployment/command-center.mdx +208 -209
  37. package/reference/deployment/index.mdx +194 -195
  38. package/reference/deployment/provided-features.mdx +110 -107
  39. package/reference/deployment/ui-execution.mdx +249 -250
  40. package/reference/framework/index.mdx +111 -195
  41. package/reference/framework/resource-documentation.mdx +90 -0
  42. package/reference/framework/tutorial-system.mdx +135 -135
  43. package/reference/getting-started.mdx +141 -142
  44. package/reference/index.mdx +95 -106
  45. package/reference/packages/ui/src/auth/README.md +6 -6
  46. package/reference/platform-tools/adapters-integration.mdx +300 -301
  47. package/reference/platform-tools/adapters-platform.mdx +552 -553
  48. package/reference/platform-tools/index.mdx +216 -217
  49. package/reference/platform-tools/type-safety.mdx +82 -82
  50. package/reference/resources/index.mdx +348 -349
  51. package/reference/resources/patterns.mdx +446 -449
  52. package/reference/resources/types.mdx +115 -116
  53. package/reference/roadmap.mdx +164 -165
  54. package/reference/rules/organization-model.md +14 -0
  55. package/reference/runtime.mdx +172 -173
  56. package/reference/scaffold/operations/propagation-pipeline.md +1 -1
  57. package/reference/scaffold/recipes/customize-crm-actions.md +45 -46
  58. package/reference/scaffold/recipes/extend-crm.md +253 -255
  59. package/reference/scaffold/recipes/extend-lead-gen.md +130 -77
  60. package/reference/scaffold/recipes/index.md +43 -44
  61. package/reference/scaffold/reference/contracts.md +1275 -1432
  62. package/reference/scaffold/reference/glossary.md +8 -6
  63. package/reference/scaffold/ui/feature-flags-and-gating.md +59 -46
  64. package/reference/scaffold/ui/feature-shell.mdx +11 -11
  65. package/reference/scaffold/ui/recipes.md +24 -24
  66. package/reference/troubleshooting.mdx +222 -223
@@ -1,553 +1,552 @@
1
- ---
2
- title: Platform Adapters
3
- description: Type-safe singleton adapters for built-in platform services -- scheduler, storage, LLM, PDF, approval, notifications, acqDb, list, execution, and email -- no credentials required
4
- loadWhen: "Using platform service adapters (scheduler, storage, llm, pdf, approval, notifications, acqDb, list, execution, email)"
5
- ---
6
-
7
- Platform adapters are singleton objects imported directly from `@elevasis/sdk/worker`. They require no credential argument because the platform injects context server-side. For integration adapters (Attio, Stripe, etc.), see [Integration Adapters](adapters-integration.mdx).
8
-
9
- ```typescript
10
- import {
11
- acqDb, scheduler, storage, pdf, approval,
12
- notifications, llm, list, execution, email,
13
- } from '@elevasis/sdk/worker'
14
- ```
15
-
16
- ---
17
-
18
- ## Quick Reference
19
-
20
- | Import | Methods | Purpose |
21
- | --------------- | ------- | ------------------------------------ |
22
- | `acqDb` | 49 | Acquisition database CRUD and sync |
23
- | `scheduler` | 9 | Task schedule management |
24
- | `storage` | 5 | File upload, download, signed URLs |
25
- | `pdf` | 2 | PDF rendering to storage or buffer |
26
- | `approval` | 2 | Human-in-the-loop approval gates |
27
- | `notifications` | 1 | In-app team notifications |
28
- | `llm` | 1 | LLM inference with structured output |
29
- | `list` | 4 | List-scoped execution and stage ops |
30
- | `execution` | 1 | Trigger nested child executions |
31
- | `email` | 1 | Send email to org members |
32
-
33
- ---
34
-
35
- ## Scheduler Adapter
36
-
37
- Singleton -- 9 methods for task schedule management.
38
-
39
- ```typescript
40
- import { scheduler } from '@elevasis/sdk/worker'
41
- ```
42
-
43
- ### Methods
44
-
45
- | Method | Params | Returns |
46
- | -------------------------------- | ------------------------------ | ---------------------- |
47
- | `createSchedule` | `CreateScheduleInput` | `TaskSchedule` |
48
- | `updateAnchor` | `{ scheduleId, anchorAt }` | `TaskSchedule` |
49
- | `deleteSchedule` | `{ scheduleId }` | `void` |
50
- | `findByIdempotencyKey` | `{ idempotencyKey }` | `TaskSchedule | null` |
51
- | `deleteScheduleByIdempotencyKey` | `{ idempotencyKey }` | `void` |
52
- | `listSchedules` | `{ status?, limit?, offset? }` | `TaskSchedule[]` |
53
- | `getSchedule` | `{ scheduleId }` | `TaskSchedule` |
54
- | `cancelSchedule` | `{ scheduleId }` | `void` |
55
- | `cancelSchedulesByMetadata` | `{ metadata }` | `{ cancelledCount }` |
56
-
57
- ### Examples
58
-
59
- ```typescript
60
- // Create a relative schedule (follow-up sequence)
61
- const schedule = await scheduler.createSchedule({
62
- organizationId: context.organizationId,
63
- name: 'Proposal follow-up',
64
- target: { resourceType: 'workflow', resourceId: 'send-followup' },
65
- scheduleConfig: {
66
- type: 'relative',
67
- anchorAt: '2026-03-15T10:00:00Z',
68
- anchorLabel: 'proposal_sent_date',
69
- items: [
70
- { offset: '+3d', payload: { step: 'first-followup' }, label: 'First follow-up' },
71
- { offset: '+7d', payload: { step: 'second-followup' }, label: 'Second follow-up' },
72
- ],
73
- },
74
- metadata: { dealId: 'deal-123', contactEmail: 'jane@acme.com' },
75
- idempotencyKey: 'proposal-followup-deal-123',
76
- })
77
-
78
- // Reschedule (update anchor)
79
- await scheduler.updateAnchor({
80
- scheduleId: schedule.id,
81
- anchorAt: '2026-03-20T10:00:00Z',
82
- })
83
-
84
- // Cancel all schedules matching metadata
85
- await scheduler.cancelSchedulesByMetadata({
86
- metadata: { dealId: 'deal-123' },
87
- })
88
- ```
89
-
90
- ---
91
-
92
- ## Storage Adapter
93
-
94
- Singleton -- 5 methods for file storage operations. All paths are relative to the organization's storage prefix (injected server-side).
95
-
96
- ```typescript
97
- import { storage } from '@elevasis/sdk/worker'
98
- ```
99
-
100
- ### Methods
101
-
102
- | Method | Params | Returns |
103
- | ----------------- | ----------------------- | ------------------------ |
104
- | `upload` | `StorageUploadInput` | `StorageUploadOutput` |
105
- | `download` | `StorageDownloadInput` | `StorageDownloadOutput` |
106
- | `createSignedUrl` | `StorageSignedUrlInput` | `StorageSignedUrlOutput` |
107
- | `delete` | `StorageDeleteInput` | `StorageDeleteOutput` |
108
- | `list` | `StorageListInput` | `StorageListOutput` |
109
-
110
- ### Examples
111
-
112
- ```typescript
113
- // Upload a PDF
114
- await storage.upload({
115
- bucket: 'acquisition',
116
- path: 'proposals/deal-123/proposal.pdf',
117
- content: base64PdfContent,
118
- contentType: 'application/pdf',
119
- })
120
-
121
- // Generate a signed URL (temporary access)
122
- const { signedUrl } = await storage.createSignedUrl({
123
- bucket: 'acquisition',
124
- path: 'proposals/deal-123/proposal.pdf',
125
- })
126
-
127
- // List files under a prefix
128
- const files = await storage.list({
129
- bucket: 'acquisition',
130
- path: 'proposals/deal-123/',
131
- })
132
- ```
133
-
134
- ---
135
-
136
- ## LLM Adapter
137
-
138
- Singleton -- 1 method with a generic `<T>` for typed structured output. No API keys needed -- keys are resolved server-side from environment variables.
139
-
140
- ```typescript
141
- import { llm } from '@elevasis/sdk/worker'
142
- ```
143
-
144
- ### Method
145
-
146
- | Method | Params | Returns |
147
- | --------------- | -------------------------------------- | -------------------------- |
148
- | `generate<T>` | `Omit<LLMGenerateRequest, 'signal'>` | `LLMGenerateResponse<T>` |
149
-
150
- The `signal` property (AbortSignal) is not serializable over the postMessage boundary. Abort is handled at the worker level.
151
-
152
- ### Example
153
-
154
- ```typescript
155
- interface Classification {
156
- category: 'interested' | 'not-interested' | 'bounced'
157
- confidence: number
158
- summary: string
159
- }
160
-
161
- const response = await llm.generate<Classification>({
162
- provider: 'anthropic',
163
- model: 'claude-sonnet-4-5',
164
- messages: [
165
- { role: 'system', content: 'Classify this email reply.' },
166
- { role: 'user', content: emailText },
167
- ],
168
- responseSchema: {
169
- type: 'object',
170
- properties: {
171
- category: { type: 'string', enum: ['interested', 'not-interested', 'bounced'] },
172
- confidence: { type: 'number', minimum: 0, maximum: 1 },
173
- summary: { type: 'string' },
174
- },
175
- required: ['category', 'confidence', 'summary'],
176
- },
177
- temperature: 0.1,
178
- })
179
-
180
- // response.output is typed as Classification
181
- const { category, confidence, summary } = response.output
182
- ```
183
-
184
- **Supported models:** See the full model table in [Platform Tools](index.mdx#llm-tool).
185
-
186
- See [Adapter Type Safety](type-safety.mdx) for required field enforcement and design rationale.
187
-
188
- ---
189
-
190
- ## PDF Adapter
191
-
192
- Singleton -- 2 methods for rendering PDFDocument structures to files or buffers. No credential required.
193
-
194
- ```typescript
195
- import { pdf } from '@elevasis/sdk/worker'
196
- ```
197
-
198
- ### Methods
199
-
200
- | Method | Params | Returns |
201
- | ---------------- | ------------------------------------------------- | --------------------------- |
202
- | `render` | `{ document, theme?, storage: { bucket, path } }` | `{ success, pdfUrl, size }` |
203
- | `renderToBuffer` | `{ document, theme? }` | `{ buffer }` |
204
-
205
- ### Example
206
-
207
- ```typescript
208
- // Render to storage (returns a URL)
209
- const result = await pdf.render({
210
- document: proposalDocument,
211
- storage: { bucket: 'acquisition', path: 'proposals/deal-123/proposal.pdf' },
212
- })
213
- console.log(result.pdfUrl)
214
-
215
- // Render to buffer (for inline processing)
216
- const { buffer } = await pdf.renderToBuffer({ document: proposalDocument })
217
- ```
218
-
219
- ---
220
-
221
- ## Approval Adapter
222
-
223
- Singleton -- 2 methods for creating and managing human-in-the-loop (HITL) approval tasks. No credential required.
224
-
225
- ```typescript
226
- import { approval } from '@elevasis/sdk/worker'
227
- ```
228
-
229
- ### Methods
230
-
231
- | Method | Params | Returns |
232
- | ------------------ | ---------------------------------------------------------------------------------------- | ------------- |
233
- | `create` | `{ actions, context, description?, priority?, humanCheckpoint?, metadata?, expiresAt? }` | `{ id }` |
234
- | `deleteByMetadata` | `{ metadata, status? }` | `{ deleted }` |
235
-
236
- ### Example
237
-
238
- ```typescript
239
- // Create an approval gate
240
- const task = await approval.create({
241
- actions: [
242
- { id: 'approve', label: 'Approve', type: 'primary' },
243
- { id: 'reject', label: 'Reject', type: 'danger' },
244
- ],
245
- context: { dealId: 'deal-123', proposalUrl: 'https://...' },
246
- description: 'Review proposal for Acme Corp',
247
- humanCheckpoint: 'proposal-review',
248
- metadata: { dealId: 'deal-123' },
249
- })
250
-
251
- // Clean up stale tasks
252
- await approval.deleteByMetadata({
253
- metadata: { dealId: 'deal-123' },
254
- status: 'pending',
255
- })
256
- ```
257
-
258
- ---
259
-
260
- ## Notifications Adapter
261
-
262
- Singleton -- 1 method for sending in-app team notifications. `userId` and `organizationId` are injected server-side.
263
-
264
- ```typescript
265
- import { notifications } from '@elevasis/sdk/worker'
266
- ```
267
-
268
- ### Method
269
-
270
- | Method | Params | Returns |
271
- | -------- | ---------------------- | ------- |
272
- | `create` | `NotificationSDKInput` | `void` |
273
-
274
- `NotificationSDKInput` fields: `category`, `title`, `message`, `actionUrl?`, `metadata?`
275
-
276
- ### Example
277
-
278
- ```typescript
279
- await notifications.create({
280
- category: 'acquisition',
281
- title: 'New lead qualified',
282
- message: 'Acme Corp has been qualified and moved to proposal stage.',
283
- actionUrl: '/deals/deal-123',
284
- })
285
- ```
286
-
287
- ---
288
-
289
- ## AcqDb Adapter
290
-
291
- Singleton -- 41 methods for acquisition database management (lists, companies, contacts, deals, deal-sync, enrichment, social monitoring). `organizationId` is injected server-side -- never pass it.
292
-
293
- ```typescript
294
- import { acqDb } from '@elevasis/sdk/worker'
295
- ```
296
-
297
- ### Methods
298
-
299
- **List operations:**
300
-
301
- | Method | Params | Returns |
302
- | ------------------- | --------------------------------------------------- | ------------------------- |
303
- | `listLists` | none | `AcqList[]` |
304
- | `createList` | `Omit<CreateListParams, 'organizationId'>` | `AcqList` |
305
- | `updateList` | `{ id } & UpdateListParams` | `AcqList` |
306
- | `deleteList` | `{ id }` | `void` |
307
- | `addContactsToList` | `Omit<AddContactsToListParams, 'organizationId'>` | `AddContactsToListResult` |
308
-
309
- **Company operations:**
310
-
311
- | Method | Params | Returns |
312
- | --------------- | ----------------------------------------------- | -------------------- |
313
- | `createCompany` | `Omit<CreateCompanyParams, 'organizationId'>` | `AcqCompany` |
314
- | `upsertCompany` | `Omit<UpsertCompanyParams, 'organizationId'>` | `AcqCompany` |
315
- | `updateCompany` | `{ id } & UpdateCompanyParams` | `AcqCompany` |
316
- | `getCompany` | `{ id }` | `AcqCompany | null` |
317
- | `listCompanies` | `{ filters? }` | `AcqCompany[]` |
318
- | `deleteCompany` | `{ id }` | `void` |
319
-
320
- **Contact operations:**
321
-
322
- | Method | Params | Returns |
323
- | ----------------------------- | ----------------------------------------------------- | ------------------------------- |
324
- | `createContact` | `Omit<CreateContactParams, 'organizationId'>` | `AcqContact` |
325
- | `upsertContact` | `Omit<UpsertContactParams, 'organizationId'>` | `AcqContact` |
326
- | `updateContact` | `{ id } & UpdateContactParams` | `AcqContact` |
327
- | `getContact` | `{ id }` | `AcqContact | null` |
328
- | `getContactByEmail` | `{ email }` | `AcqContact | null` |
329
- | `listContacts` | `{ filters?, pagination? }` | `PaginatedResult<AcqContact>` |
330
- | `deleteContact` | `{ id }` | `void` |
331
- | `bulkImportContacts` | `Omit<BulkImportParams, 'organizationId'>` | `BulkImportResult` |
332
- | `bulkImportCompanies` | `Omit<BulkImportCompaniesParams, 'organizationId'>` | `BulkImportCompaniesResult` |
333
- | `deactivateContactsByCompany` | `{ companyId }` | `{ deactivated: number }` |
334
-
335
- **Deal operations:**
336
-
337
- | Method | Params | Returns |
338
- | ---------------------- | -------------------------------------------- | ----------------- |
339
- | `upsertDeal` | `Omit<UpsertDealParams, 'organizationId'>` | `AcqDeal` |
340
- | `getDealByEmail` | `{ email }` | `AcqDeal | null` |
341
- | `getDealByEnvelopeId` | `{ envelopeId }` | `AcqDeal | null` |
342
- | `updateDealEnvelopeId` | `{ attioDealId, envelopeId }` | `AcqDeal | null` |
343
- | `getDealByAttioId` | `{ attioDealId }` | `AcqDeal | null` |
344
-
345
- **Deal-sync operations:**
346
-
347
- | Method | Params | Returns |
348
- | ------------------------------- | ----------------------------------------------------------- | ------------------------------------- |
349
- | `updateDiscoveryData` | `Omit<UpdateDiscoveryDataParams, 'organizationId'>` | `void` |
350
- | `updateProposalData` | `Omit<UpdateProposalDataParams, 'organizationId'>` | `void` |
351
- | `markProposalSent` | `Omit<MarkProposalSentParams, 'organizationId'>` | `void` |
352
- | `markProposalReviewed` | `Omit<MarkProposalReviewedParams, 'organizationId'>` | `void` |
353
- | `updateCloseLostReason` | `Omit<UpdateCloseLostReasonParams, 'organizationId'>` | `void` |
354
- | `updateFees` | `Omit<UpdateFeesParams, 'organizationId'>` | `void` |
355
- | `syncDealStage` | `Omit<SyncDealStageParams, 'organizationId'>` | `void` |
356
- | `setContactNurture` | `Omit<SetContactNurtureParams, 'organizationId'>` | `void` |
357
- | `cancelSchedulesAndHitlByEmail` | `Omit<..., 'organizationId'>` | `{ schedulesCancelled, hitlDeleted }` |
358
- | `cancelHitlByDealId` | `Omit<..., 'organizationId'>` | `{ hitlDeleted }` |
359
- | `clearDealFields` | `Omit<ClearDealFieldsParams, 'organizationId'>` | `void` |
360
- | `deleteDeal` | `Omit<DeleteDealParams, 'organizationId'>` | `void` |
361
- | `recordReply` | `{ contactEmail, replyText, replySubject?, campaignName? }` | `void` |
362
- | `recordDealActivity` | `{ attioDealId?, contactEmail?, type, ... }` | `void` |
363
-
364
- **Enrichment operations:**
365
-
366
- | Method | Params | Returns |
367
- | --------------------- | -------------------------------------------------------- | ------- |
368
- | `mergeEnrichmentData` | `{ id, table: 'acq_companies' | 'acq_contacts', data }` | `void` |
369
-
370
- **Social monitoring operations:**
371
-
372
- | Method | Params | Returns |
373
- | ------------------- | --------------------------------------------------------------- | ------------------------- |
374
- | `upsertSocialPosts` | `{ posts: Omit<UpsertSocialPostParams, 'organizationId'>[] }` | `UpsertSocialPostsResult` |
375
-
376
- ### Example
377
-
378
- ```typescript
379
- const deal = await acqDb.getDealByEmail({ email: 'jane@acme.com' })
380
- if (!deal) {
381
- await acqDb.upsertDeal({
382
- attioDealId: 'deal-123',
383
- contactEmail: 'jane@acme.com',
384
- })
385
- }
386
-
387
- // Bulk import contacts
388
- await acqDb.bulkImportContacts({
389
- listId: 'list-abc',
390
- contacts: [
391
- { email: 'a@example.com', firstName: 'Alice' },
392
- { email: 'b@example.com', firstName: 'Bob' },
393
- ],
394
- })
395
- ```
396
-
397
- ---
398
-
399
- ## Execution Adapter
400
-
401
- Singleton -- 1 method for triggering another resource (workflow or agent) as a nested child execution. Maximum depth: 5 levels.
402
-
403
- ```typescript
404
- import { execution } from '@elevasis/sdk/worker'
405
- ```
406
-
407
- ### Method
408
-
409
- | Method | Params | Returns |
410
- | --------- | ------------------------ | ------------------------------------------ |
411
- | `trigger` | `{ resourceId, input? }` | `{ success, executionId, output, error? }` |
412
-
413
- ### Example
414
-
415
- ```typescript
416
- const result = await execution.trigger({
417
- resourceId: 'send-welcome-sequence',
418
- input: { userId: newUser.id, email: newUser.email },
419
- })
420
-
421
- if (!result.success) {
422
- throw new Error(`Child workflow failed: ${result.error}`)
423
- }
424
- ```
425
-
426
- ---
427
-
428
- ## List Adapter
429
-
430
- Singleton -- 4 methods for list-scoped lead-gen runtime operations. This is the focused companion to `acqDb` for list-oriented workflows.
431
-
432
- ```typescript
433
- import { list } from '@elevasis/sdk/worker'
434
- ```
435
-
436
- ### Methods
437
-
438
- | Method | Params | Returns |
439
- | ------ | ------ | ------- |
440
- | `getConfig` | `{ listId }` | `ListConfig` |
441
- | `recordExecution` | `{ listId, executionId, payload? }` | `void` |
442
- | `updateCompanyStage` | `{ listId, companyId, stage, metadata? }` | `void` |
443
- | `updateContactStage` | `{ listId, contactId, stage, metadata? }` | `void` |
444
-
445
- ### Example
446
-
447
- ```typescript
448
- const config = await list.getConfig({ listId })
449
-
450
- await list.recordExecution({
451
- listId,
452
- executionId: context.executionId,
453
- payload: { resourceId: context.resourceId, step: 'qualification' },
454
- })
455
-
456
- await list.updateCompanyStage({
457
- listId,
458
- companyId,
459
- stage: 'qualified',
460
- })
461
- ```
462
-
463
- Use `list` when the workflow is explicitly operating on a list pipeline run. Use `acqDb` when you need broader list, company, contact, or deal CRUD.
464
-
465
- ---
466
-
467
- ## Email Adapter
468
-
469
- Singleton -- 1 method for sending platform emails to organization members (from `notifications@elevasis.io`). For client-facing emails, use the Resend or Instantly integration adapters instead.
470
-
471
- ```typescript
472
- import { email } from '@elevasis/sdk/worker'
473
- ```
474
-
475
- ### Method
476
-
477
- | Method | Params | Returns |
478
- | ------ | ------------------------------------------------------------------------------- | --------------------------- |
479
- | `send` | `{ subject, html?, text?, userIds?, targetRole?, targetAll?, replyTo?, tags? }` | `{ sent, failed, errors? }` |
480
-
481
- ### Example
482
-
483
- ```typescript
484
- // Notify all org members
485
- await email.send({
486
- subject: 'New deal closed',
487
- text: 'Acme Corp has signed the contract.',
488
- targetAll: true,
489
- })
490
-
491
- // Notify specific users
492
- await email.send({
493
- subject: 'Action required',
494
- html: '\<p\>Please review the proposal.\</p\>',
495
- userIds: ['user-abc', 'user-def'],
496
- })
497
- ```
498
-
499
- ---
500
-
501
- ## Custom Adapters with `createAdapter`
502
-
503
- The generic `createAdapter` factory is exported for building adapters for any tool not already covered. Define a type map and call the factory.
504
-
505
- ```typescript
506
- import { createAdapter } from '@elevasis/sdk/worker'
507
-
508
- // 1. Define a type map
509
- type MyToolMap = {
510
- doSomething: { params: { input: string }; result: { output: string } }
511
- listItems: { params: { limit?: number }; result: { items: string[] } }
512
- getStatus: { params: Record<string, never>; result: { healthy: boolean } }
513
- }
514
-
515
- // 2. Create the adapter
516
- const myTool = createAdapter<MyToolMap>('my-tool', [
517
- 'doSomething', 'listItems', 'getStatus',
518
- ], 'my-credential') // credential is optional
519
-
520
- // 3. Use it (fully typed)
521
- const result = await myTool.doSomething({ input: 'hello' })
522
- // ^-- { output: string }
523
- ```
524
-
525
- Method names are compile-time checked against the type map keys.
526
-
527
- ---
528
-
529
- ## Error Handling
530
-
531
- All adapter calls throw `PlatformToolError` on failure, with a `code` field and `retryable` flag:
532
-
533
- ```typescript
534
- import { PlatformToolError } from '@elevasis/sdk/worker'
535
-
536
- try {
537
- await scheduler.listSchedules({ status: 'active' })
538
- } catch (err) {
539
- if (err instanceof PlatformToolError) {
540
- console.log(err.message) // Human-readable error
541
- console.log(err.code) // e.g., 'rate_limit_exceeded', 'service_unavailable'
542
- console.log(err.retryable) // true for transient errors
543
- }
544
- }
545
- ```
546
-
547
- Retryable error codes: `rate_limit_exceeded`, `network_error`, `timeout_error`, `api_error`, `service_unavailable`, `server_unavailable`.
548
-
549
- **Timeouts:** LLM calls have a 120s timeout. All other tool calls have a 60s timeout.
550
-
551
- ---
552
-
553
- **Last Updated:** 2026-04-15
1
+ ---
2
+ title: Platform Adapters
3
+ description: Type-safe singleton adapters for built-in platform services -- scheduler, storage, LLM, PDF, approval, notifications, acqDb, list, execution, and email -- no credentials required
4
+ ---
5
+
6
+ Platform adapters are singleton objects imported directly from `@elevasis/sdk/worker`. They require no credential argument because the platform injects context server-side. For integration adapters (Attio, Stripe, etc.), see [Integration Adapters](adapters-integration.mdx).
7
+
8
+ ```typescript
9
+ import {
10
+ acqDb, scheduler, storage, pdf, approval,
11
+ notifications, llm, list, execution, email,
12
+ } from '@elevasis/sdk/worker'
13
+ ```
14
+
15
+ ---
16
+
17
+ ## Quick Reference
18
+
19
+ | Import | Methods | Purpose |
20
+ | --------------- | ------- | ------------------------------------ |
21
+ | `acqDb` | 49 | Acquisition database CRUD and sync |
22
+ | `scheduler` | 9 | Task schedule management |
23
+ | `storage` | 5 | File upload, download, signed URLs |
24
+ | `pdf` | 2 | PDF rendering to storage or buffer |
25
+ | `approval` | 2 | Human-in-the-loop approval gates |
26
+ | `notifications` | 1 | In-app team notifications |
27
+ | `llm` | 1 | LLM inference with structured output |
28
+ | `list` | 4 | List-scoped execution and stage ops |
29
+ | `execution` | 1 | Trigger nested child executions |
30
+ | `email` | 1 | Send email to org members |
31
+
32
+ ---
33
+
34
+ ## Scheduler Adapter
35
+
36
+ Singleton -- 9 methods for task schedule management.
37
+
38
+ ```typescript
39
+ import { scheduler } from '@elevasis/sdk/worker'
40
+ ```
41
+
42
+ ### Methods
43
+
44
+ | Method | Params | Returns |
45
+ | -------------------------------- | ------------------------------ | ---------------------- |
46
+ | `createSchedule` | `CreateScheduleInput` | `TaskSchedule` |
47
+ | `updateAnchor` | `{ scheduleId, anchorAt }` | `TaskSchedule` |
48
+ | `deleteSchedule` | `{ scheduleId }` | `void` |
49
+ | `findByIdempotencyKey` | `{ idempotencyKey }` | `TaskSchedule | null` |
50
+ | `deleteScheduleByIdempotencyKey` | `{ idempotencyKey }` | `void` |
51
+ | `listSchedules` | `{ status?, limit?, offset? }` | `TaskSchedule[]` |
52
+ | `getSchedule` | `{ scheduleId }` | `TaskSchedule` |
53
+ | `cancelSchedule` | `{ scheduleId }` | `void` |
54
+ | `cancelSchedulesByMetadata` | `{ metadata }` | `{ cancelledCount }` |
55
+
56
+ ### Examples
57
+
58
+ ```typescript
59
+ // Create a relative schedule (follow-up sequence)
60
+ const schedule = await scheduler.createSchedule({
61
+ organizationId: context.organizationId,
62
+ name: 'Proposal follow-up',
63
+ target: { resourceType: 'workflow', resourceId: 'send-followup' },
64
+ scheduleConfig: {
65
+ type: 'relative',
66
+ anchorAt: '2026-03-15T10:00:00Z',
67
+ anchorLabel: 'proposal_sent_date',
68
+ items: [
69
+ { offset: '+3d', payload: { step: 'first-followup' }, label: 'First follow-up' },
70
+ { offset: '+7d', payload: { step: 'second-followup' }, label: 'Second follow-up' },
71
+ ],
72
+ },
73
+ metadata: { dealId: 'deal-123', contactEmail: 'jane@acme.com' },
74
+ idempotencyKey: 'proposal-followup-deal-123',
75
+ })
76
+
77
+ // Reschedule (update anchor)
78
+ await scheduler.updateAnchor({
79
+ scheduleId: schedule.id,
80
+ anchorAt: '2026-03-20T10:00:00Z',
81
+ })
82
+
83
+ // Cancel all schedules matching metadata
84
+ await scheduler.cancelSchedulesByMetadata({
85
+ metadata: { dealId: 'deal-123' },
86
+ })
87
+ ```
88
+
89
+ ---
90
+
91
+ ## Storage Adapter
92
+
93
+ Singleton -- 5 methods for file storage operations. All paths are relative to the organization's storage prefix (injected server-side).
94
+
95
+ ```typescript
96
+ import { storage } from '@elevasis/sdk/worker'
97
+ ```
98
+
99
+ ### Methods
100
+
101
+ | Method | Params | Returns |
102
+ | ----------------- | ----------------------- | ------------------------ |
103
+ | `upload` | `StorageUploadInput` | `StorageUploadOutput` |
104
+ | `download` | `StorageDownloadInput` | `StorageDownloadOutput` |
105
+ | `createSignedUrl` | `StorageSignedUrlInput` | `StorageSignedUrlOutput` |
106
+ | `delete` | `StorageDeleteInput` | `StorageDeleteOutput` |
107
+ | `list` | `StorageListInput` | `StorageListOutput` |
108
+
109
+ ### Examples
110
+
111
+ ```typescript
112
+ // Upload a PDF
113
+ await storage.upload({
114
+ bucket: 'acquisition',
115
+ path: 'proposals/deal-123/proposal.pdf',
116
+ content: base64PdfContent,
117
+ contentType: 'application/pdf',
118
+ })
119
+
120
+ // Generate a signed URL (temporary access)
121
+ const { signedUrl } = await storage.createSignedUrl({
122
+ bucket: 'acquisition',
123
+ path: 'proposals/deal-123/proposal.pdf',
124
+ })
125
+
126
+ // List files under a prefix
127
+ const files = await storage.list({
128
+ bucket: 'acquisition',
129
+ path: 'proposals/deal-123/',
130
+ })
131
+ ```
132
+
133
+ ---
134
+
135
+ ## LLM Adapter
136
+
137
+ Singleton -- 1 method with a generic `<T>` for typed structured output. No API keys needed -- keys are resolved server-side from environment variables.
138
+
139
+ ```typescript
140
+ import { llm } from '@elevasis/sdk/worker'
141
+ ```
142
+
143
+ ### Method
144
+
145
+ | Method | Params | Returns |
146
+ | --------------- | -------------------------------------- | -------------------------- |
147
+ | `generate<T>` | `Omit<LLMGenerateRequest, 'signal'>` | `LLMGenerateResponse<T>` |
148
+
149
+ The `signal` property (AbortSignal) is not serializable over the postMessage boundary. Abort is handled at the worker level.
150
+
151
+ ### Example
152
+
153
+ ```typescript
154
+ interface Classification {
155
+ category: 'interested' | 'not-interested' | 'bounced'
156
+ confidence: number
157
+ summary: string
158
+ }
159
+
160
+ const response = await llm.generate<Classification>({
161
+ provider: 'anthropic',
162
+ model: 'claude-sonnet-4-5',
163
+ messages: [
164
+ { role: 'system', content: 'Classify this email reply.' },
165
+ { role: 'user', content: emailText },
166
+ ],
167
+ responseSchema: {
168
+ type: 'object',
169
+ properties: {
170
+ category: { type: 'string', enum: ['interested', 'not-interested', 'bounced'] },
171
+ confidence: { type: 'number', minimum: 0, maximum: 1 },
172
+ summary: { type: 'string' },
173
+ },
174
+ required: ['category', 'confidence', 'summary'],
175
+ },
176
+ temperature: 0.1,
177
+ })
178
+
179
+ // response.output is typed as Classification
180
+ const { category, confidence, summary } = response.output
181
+ ```
182
+
183
+ **Supported models:** See the full model table in [Platform Tools](index.mdx#llm-tool).
184
+
185
+ See [Adapter Type Safety](type-safety.mdx) for required field enforcement and design rationale.
186
+
187
+ ---
188
+
189
+ ## PDF Adapter
190
+
191
+ Singleton -- 2 methods for rendering PDFDocument structures to files or buffers. No credential required.
192
+
193
+ ```typescript
194
+ import { pdf } from '@elevasis/sdk/worker'
195
+ ```
196
+
197
+ ### Methods
198
+
199
+ | Method | Params | Returns |
200
+ | ---------------- | ------------------------------------------------- | --------------------------- |
201
+ | `render` | `{ document, theme?, storage: { bucket, path } }` | `{ success, pdfUrl, size }` |
202
+ | `renderToBuffer` | `{ document, theme? }` | `{ buffer }` |
203
+
204
+ ### Example
205
+
206
+ ```typescript
207
+ // Render to storage (returns a URL)
208
+ const result = await pdf.render({
209
+ document: proposalDocument,
210
+ storage: { bucket: 'acquisition', path: 'proposals/deal-123/proposal.pdf' },
211
+ })
212
+ console.log(result.pdfUrl)
213
+
214
+ // Render to buffer (for inline processing)
215
+ const { buffer } = await pdf.renderToBuffer({ document: proposalDocument })
216
+ ```
217
+
218
+ ---
219
+
220
+ ## Approval Adapter
221
+
222
+ Singleton -- 2 methods for creating and managing human-in-the-loop (HITL) approval tasks. No credential required.
223
+
224
+ ```typescript
225
+ import { approval } from '@elevasis/sdk/worker'
226
+ ```
227
+
228
+ ### Methods
229
+
230
+ | Method | Params | Returns |
231
+ | ------------------ | ---------------------------------------------------------------------------------------- | ------------- |
232
+ | `create` | `{ actions, context, description?, priority?, humanCheckpoint?, metadata?, expiresAt? }` | `{ id }` |
233
+ | `deleteByMetadata` | `{ metadata, status? }` | `{ deleted }` |
234
+
235
+ ### Example
236
+
237
+ ```typescript
238
+ // Create an approval gate
239
+ const task = await approval.create({
240
+ actions: [
241
+ { id: 'approve', label: 'Approve', type: 'primary' },
242
+ { id: 'reject', label: 'Reject', type: 'danger' },
243
+ ],
244
+ context: { dealId: 'deal-123', proposalUrl: 'https://...' },
245
+ description: 'Review proposal for Acme Corp',
246
+ humanCheckpoint: 'proposal-review',
247
+ metadata: { dealId: 'deal-123' },
248
+ })
249
+
250
+ // Clean up stale tasks
251
+ await approval.deleteByMetadata({
252
+ metadata: { dealId: 'deal-123' },
253
+ status: 'pending',
254
+ })
255
+ ```
256
+
257
+ ---
258
+
259
+ ## Notifications Adapter
260
+
261
+ Singleton -- 1 method for sending in-app team notifications. `userId` and `organizationId` are injected server-side.
262
+
263
+ ```typescript
264
+ import { notifications } from '@elevasis/sdk/worker'
265
+ ```
266
+
267
+ ### Method
268
+
269
+ | Method | Params | Returns |
270
+ | -------- | ---------------------- | ------- |
271
+ | `create` | `NotificationSDKInput` | `void` |
272
+
273
+ `NotificationSDKInput` fields: `category`, `title`, `message`, `actionUrl?`, `metadata?`
274
+
275
+ ### Example
276
+
277
+ ```typescript
278
+ await notifications.create({
279
+ category: 'acquisition',
280
+ title: 'New lead qualified',
281
+ message: 'Acme Corp has been qualified and moved to proposal stage.',
282
+ actionUrl: '/deals/deal-123',
283
+ })
284
+ ```
285
+
286
+ ---
287
+
288
+ ## AcqDb Adapter
289
+
290
+ Singleton -- 41 methods for acquisition database management (lists, companies, contacts, deals, deal-sync, enrichment, social monitoring). `organizationId` is injected server-side -- never pass it.
291
+
292
+ ```typescript
293
+ import { acqDb } from '@elevasis/sdk/worker'
294
+ ```
295
+
296
+ ### Methods
297
+
298
+ **List operations:**
299
+
300
+ | Method | Params | Returns |
301
+ | ------------------- | --------------------------------------------------- | ------------------------- |
302
+ | `listLists` | none | `AcqList[]` |
303
+ | `createList` | `Omit<CreateListParams, 'organizationId'>` | `AcqList` |
304
+ | `updateList` | `{ id } & UpdateListParams` | `AcqList` |
305
+ | `deleteList` | `{ id }` | `void` |
306
+ | `addContactsToList` | `Omit<AddContactsToListParams, 'organizationId'>` | `AddContactsToListResult` |
307
+
308
+ **Company operations:**
309
+
310
+ | Method | Params | Returns |
311
+ | --------------- | ----------------------------------------------- | -------------------- |
312
+ | `createCompany` | `Omit<CreateCompanyParams, 'organizationId'>` | `AcqCompany` |
313
+ | `upsertCompany` | `Omit<UpsertCompanyParams, 'organizationId'>` | `AcqCompany` |
314
+ | `updateCompany` | `{ id } & UpdateCompanyParams` | `AcqCompany` |
315
+ | `getCompany` | `{ id }` | `AcqCompany | null` |
316
+ | `listCompanies` | `{ filters? }` | `AcqCompany[]` |
317
+ | `deleteCompany` | `{ id }` | `void` |
318
+
319
+ **Contact operations:**
320
+
321
+ | Method | Params | Returns |
322
+ | ----------------------------- | ----------------------------------------------------- | ------------------------------- |
323
+ | `createContact` | `Omit<CreateContactParams, 'organizationId'>` | `AcqContact` |
324
+ | `upsertContact` | `Omit<UpsertContactParams, 'organizationId'>` | `AcqContact` |
325
+ | `updateContact` | `{ id } & UpdateContactParams` | `AcqContact` |
326
+ | `getContact` | `{ id }` | `AcqContact | null` |
327
+ | `getContactByEmail` | `{ email }` | `AcqContact | null` |
328
+ | `listContacts` | `{ filters?, pagination? }` | `PaginatedResult<AcqContact>` |
329
+ | `deleteContact` | `{ id }` | `void` |
330
+ | `bulkImportContacts` | `Omit<BulkImportParams, 'organizationId'>` | `BulkImportResult` |
331
+ | `bulkImportCompanies` | `Omit<BulkImportCompaniesParams, 'organizationId'>` | `BulkImportCompaniesResult` |
332
+ | `deactivateContactsByCompany` | `{ companyId }` | `{ deactivated: number }` |
333
+
334
+ **Deal operations:**
335
+
336
+ | Method | Params | Returns |
337
+ | ---------------------- | -------------------------------------------- | ----------------- |
338
+ | `upsertDeal` | `Omit<UpsertDealParams, 'organizationId'>` | `AcqDeal` |
339
+ | `getDealByEmail` | `{ email }` | `AcqDeal | null` |
340
+ | `getDealByEnvelopeId` | `{ envelopeId }` | `AcqDeal | null` |
341
+ | `updateDealEnvelopeId` | `{ attioDealId, envelopeId }` | `AcqDeal | null` |
342
+ | `getDealByAttioId` | `{ attioDealId }` | `AcqDeal | null` |
343
+
344
+ **Deal-sync operations:**
345
+
346
+ | Method | Params | Returns |
347
+ | ------------------------------- | ----------------------------------------------------------- | ------------------------------------- |
348
+ | `updateDiscoveryData` | `Omit<UpdateDiscoveryDataParams, 'organizationId'>` | `void` |
349
+ | `updateProposalData` | `Omit<UpdateProposalDataParams, 'organizationId'>` | `void` |
350
+ | `markProposalSent` | `Omit<MarkProposalSentParams, 'organizationId'>` | `void` |
351
+ | `markProposalReviewed` | `Omit<MarkProposalReviewedParams, 'organizationId'>` | `void` |
352
+ | `updateCloseLostReason` | `Omit<UpdateCloseLostReasonParams, 'organizationId'>` | `void` |
353
+ | `updateFees` | `Omit<UpdateFeesParams, 'organizationId'>` | `void` |
354
+ | `syncDealStage` | `Omit<SyncDealStageParams, 'organizationId'>` | `void` |
355
+ | `setContactNurture` | `Omit<SetContactNurtureParams, 'organizationId'>` | `void` |
356
+ | `cancelSchedulesAndHitlByEmail` | `Omit<..., 'organizationId'>` | `{ schedulesCancelled, hitlDeleted }` |
357
+ | `cancelHitlByDealId` | `Omit<..., 'organizationId'>` | `{ hitlDeleted }` |
358
+ | `clearDealFields` | `Omit<ClearDealFieldsParams, 'organizationId'>` | `void` |
359
+ | `deleteDeal` | `Omit<DeleteDealParams, 'organizationId'>` | `void` |
360
+ | `recordReply` | `{ contactEmail, replyText, replySubject?, campaignName? }` | `void` |
361
+ | `recordDealActivity` | `{ attioDealId?, contactEmail?, type, ... }` | `void` |
362
+
363
+ **Enrichment operations:**
364
+
365
+ | Method | Params | Returns |
366
+ | --------------------- | -------------------------------------------------------- | ------- |
367
+ | `mergeEnrichmentData` | `{ id, table: 'acq_companies' | 'acq_contacts', data }` | `void` |
368
+
369
+ **Social monitoring operations:**
370
+
371
+ | Method | Params | Returns |
372
+ | ------------------- | --------------------------------------------------------------- | ------------------------- |
373
+ | `upsertSocialPosts` | `{ posts: Omit<UpsertSocialPostParams, 'organizationId'>[] }` | `UpsertSocialPostsResult` |
374
+
375
+ ### Example
376
+
377
+ ```typescript
378
+ const deal = await acqDb.getDealByEmail({ email: 'jane@acme.com' })
379
+ if (!deal) {
380
+ await acqDb.upsertDeal({
381
+ attioDealId: 'deal-123',
382
+ contactEmail: 'jane@acme.com',
383
+ })
384
+ }
385
+
386
+ // Bulk import contacts
387
+ await acqDb.bulkImportContacts({
388
+ listId: 'list-abc',
389
+ contacts: [
390
+ { email: 'a@example.com', firstName: 'Alice' },
391
+ { email: 'b@example.com', firstName: 'Bob' },
392
+ ],
393
+ })
394
+ ```
395
+
396
+ ---
397
+
398
+ ## Execution Adapter
399
+
400
+ Singleton -- 1 method for triggering another resource (workflow or agent) as a nested child execution. Maximum depth: 5 levels.
401
+
402
+ ```typescript
403
+ import { execution } from '@elevasis/sdk/worker'
404
+ ```
405
+
406
+ ### Method
407
+
408
+ | Method | Params | Returns |
409
+ | --------- | ------------------------ | ------------------------------------------ |
410
+ | `trigger` | `{ resourceId, input? }` | `{ success, executionId, output, error? }` |
411
+
412
+ ### Example
413
+
414
+ ```typescript
415
+ const result = await execution.trigger({
416
+ resourceId: 'send-welcome-sequence',
417
+ input: { userId: newUser.id, email: newUser.email },
418
+ })
419
+
420
+ if (!result.success) {
421
+ throw new Error(`Child workflow failed: ${result.error}`)
422
+ }
423
+ ```
424
+
425
+ ---
426
+
427
+ ## List Adapter
428
+
429
+ Singleton -- 4 methods for list-scoped lead-gen runtime operations. This is the focused companion to `acqDb` for list-oriented workflows.
430
+
431
+ ```typescript
432
+ import { list } from '@elevasis/sdk/worker'
433
+ ```
434
+
435
+ ### Methods
436
+
437
+ | Method | Params | Returns |
438
+ | -------------------- | ----------------------------------------- | ------------ |
439
+ | `getConfig` | `{ listId }` | `ListConfig` |
440
+ | `recordExecution` | `{ listId, executionId, payload? }` | `void` |
441
+ | `updateCompanyStage` | `{ listId, companyId, stage, metadata? }` | `void` |
442
+ | `updateContactStage` | `{ listId, contactId, stage, metadata? }` | `void` |
443
+
444
+ ### Example
445
+
446
+ ```typescript
447
+ const config = await list.getConfig({ listId })
448
+
449
+ await list.recordExecution({
450
+ listId,
451
+ executionId: context.executionId,
452
+ payload: { resourceId: context.resourceId, step: 'qualification' },
453
+ })
454
+
455
+ await list.updateCompanyStage({
456
+ listId,
457
+ companyId,
458
+ stage: 'qualified',
459
+ })
460
+ ```
461
+
462
+ Use `list` when the workflow is explicitly operating on a list pipeline run. Use `acqDb` when you need broader list, company, contact, or deal CRUD.
463
+
464
+ ---
465
+
466
+ ## Email Adapter
467
+
468
+ Singleton -- 1 method for sending platform emails to organization members (from `notifications@elevasis.io`). For client-facing emails, use the Resend or Instantly integration adapters instead.
469
+
470
+ ```typescript
471
+ import { email } from '@elevasis/sdk/worker'
472
+ ```
473
+
474
+ ### Method
475
+
476
+ | Method | Params | Returns |
477
+ | ------ | ------------------------------------------------------------------------------- | --------------------------- |
478
+ | `send` | `{ subject, html?, text?, userIds?, targetRole?, targetAll?, replyTo?, tags? }` | `{ sent, failed, errors? }` |
479
+
480
+ ### Example
481
+
482
+ ```typescript
483
+ // Notify all org members
484
+ await email.send({
485
+ subject: 'New deal closed',
486
+ text: 'Acme Corp has signed the contract.',
487
+ targetAll: true,
488
+ })
489
+
490
+ // Notify specific users
491
+ await email.send({
492
+ subject: 'Action required',
493
+ html: '\<p\>Please review the proposal.\</p\>',
494
+ userIds: ['user-abc', 'user-def'],
495
+ })
496
+ ```
497
+
498
+ ---
499
+
500
+ ## Custom Adapters with `createAdapter`
501
+
502
+ The generic `createAdapter` factory is exported for building adapters for any tool not already covered. Define a type map and call the factory.
503
+
504
+ ```typescript
505
+ import { createAdapter } from '@elevasis/sdk/worker'
506
+
507
+ // 1. Define a type map
508
+ type MyToolMap = {
509
+ doSomething: { params: { input: string }; result: { output: string } }
510
+ listItems: { params: { limit?: number }; result: { items: string[] } }
511
+ getStatus: { params: Record<string, never>; result: { healthy: boolean } }
512
+ }
513
+
514
+ // 2. Create the adapter
515
+ const myTool = createAdapter<MyToolMap>('my-tool', [
516
+ 'doSomething', 'listItems', 'getStatus',
517
+ ], 'my-credential') // credential is optional
518
+
519
+ // 3. Use it (fully typed)
520
+ const result = await myTool.doSomething({ input: 'hello' })
521
+ // ^-- { output: string }
522
+ ```
523
+
524
+ Method names are compile-time checked against the type map keys.
525
+
526
+ ---
527
+
528
+ ## Error Handling
529
+
530
+ All adapter calls throw `PlatformToolError` on failure, with a `code` field and `retryable` flag:
531
+
532
+ ```typescript
533
+ import { PlatformToolError } from '@elevasis/sdk/worker'
534
+
535
+ try {
536
+ await scheduler.listSchedules({ status: 'active' })
537
+ } catch (err) {
538
+ if (err instanceof PlatformToolError) {
539
+ console.log(err.message) // Human-readable error
540
+ console.log(err.code) // e.g., 'rate_limit_exceeded', 'service_unavailable'
541
+ console.log(err.retryable) // true for transient errors
542
+ }
543
+ }
544
+ ```
545
+
546
+ Retryable error codes: `rate_limit_exceeded`, `network_error`, `timeout_error`, `api_error`, `service_unavailable`, `server_unavailable`.
547
+
548
+ **Timeouts:** LLM calls have a 120s timeout. All other tool calls have a 60s timeout.
549
+
550
+ ---
551
+
552
+ **Last Updated:** 2026-04-15