@alvera-ai/platform-sdk 0.10.0-rc.18 → 0.10.0-rc.19
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/.agent/AGENTS.md +3 -3
- package/.agent/cookbook/_setup/accounts_receivable.md +1 -1
- package/.agent/cookbook/_setup/foundation.md +1 -1
- package/.agent/cookbook/_setup/healthcare.md +1 -1
- package/.agent/cookbook/_setup/payment_risk.md +1 -1
- package/.agent/cookbook/contact-us-triage-with-llm.md +10 -10
- package/.agent/cookbook/score-leads-with-llm-categorization.md +10 -10
- package/.agent/data_activation_clients.md +2 -2
- package/.agent/data_sources.md +4 -4
- package/.agent/datalakes.md +12 -15
- package/.agent/debugging.md +3 -3
- package/.agent/errors.md +67 -106
- package/.agent/{custom_datasets.md → generic_tables.md} +16 -17
- package/.agent/interoperability_contracts.md +1 -1
- package/.agent/mdm.md +2 -2
- package/.agent/mutations.md +5 -6
- package/.agent/tools.md +15 -16
- package/.agent/type_naming.md +8 -12
- package/README.md +6 -0
- package/dist/index.d.mts +54 -25
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +14 -24
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/.agent/AGENTS.md
CHANGED
|
@@ -66,7 +66,7 @@ corpus page documents the surface you already know:
|
|
|
66
66
|
| Host | The platform itself | This corpus + `account_management.md` |
|
|
67
67
|
| Client | Data Activation Client | `data_activation_clients.md` |
|
|
68
68
|
| Server | Agentic Workflow | `workflows.md` |
|
|
69
|
-
| Resources | Datasets (per data domain) | `
|
|
69
|
+
| Resources | Datasets (per data domain) | `generic_tables.md`, `mdm.md` |
|
|
70
70
|
| Prompts | Interoperability Contracts | `interoperability_contracts.md`, `templates.md` |
|
|
71
71
|
| Sampling | Dataset event triggers | `data_activation_clients.md` §6.4 logs |
|
|
72
72
|
| Tools | Tool Calls (polymorphic body)| `tools.md` |
|
|
@@ -159,7 +159,7 @@ clobbers earlier instances' auth.
|
|
|
159
159
|
|
|
160
160
|
DATA ACTIVATION (how data flows in)
|
|
161
161
|
───────────────
|
|
162
|
-
|
|
162
|
+
generic_tables.md Schema-on-write tables.
|
|
163
163
|
interoperability_contracts.md Liquid template mappings.
|
|
164
164
|
data_activation_clients.md Ingestion pipelines — both the
|
|
165
165
|
binding row (CRUD) and the
|
|
@@ -192,7 +192,7 @@ wire name (snake_case):
|
|
|
192
192
|
api.actionStatusUpdaters action_status_updaters.md
|
|
193
193
|
api.connectedApps connected_apps.md
|
|
194
194
|
api.mdm mdm.md
|
|
195
|
-
api.genericTables
|
|
195
|
+
api.genericTables generic_tables.md
|
|
196
196
|
api.interoperabilityContracts interoperability_contracts.md
|
|
197
197
|
api.dataActivationClients data_activation_clients.md
|
|
198
198
|
api.workflows workflows.md
|
|
@@ -87,7 +87,7 @@ ctx.sarahTenantlessApi = createIsolatedPlatformApi({
|
|
|
87
87
|
const tenantResp = await ctx.sarahTenantlessApi.tenants.create({
|
|
88
88
|
name: `Cookbook Accounts Receivable ${runSuffix}`,
|
|
89
89
|
})
|
|
90
|
-
tenantSlug = tenantResp.data.
|
|
90
|
+
tenantSlug = tenantResp.data.slug!
|
|
91
91
|
```
|
|
92
92
|
|
|
93
93
|
## 003 — sarah signs in tenant-scoped — the canonical client
|
|
@@ -85,7 +85,7 @@ ctx.sarahTenantlessApi = createIsolatedPlatformApi({
|
|
|
85
85
|
const tenantResp = await ctx.sarahTenantlessApi.tenants.create({
|
|
86
86
|
name: `Cookbook Foundation ${runSuffix}`,
|
|
87
87
|
})
|
|
88
|
-
tenantSlug = tenantResp.data.
|
|
88
|
+
tenantSlug = tenantResp.data.slug!
|
|
89
89
|
```
|
|
90
90
|
|
|
91
91
|
## 003 — sarah signs in tenant-scoped — the canonical client
|
|
@@ -85,7 +85,7 @@ ctx.sarahTenantlessApi = createIsolatedPlatformApi({
|
|
|
85
85
|
const tenantResp = await ctx.sarahTenantlessApi.tenants.create({
|
|
86
86
|
name: `Cookbook Healthcare ${runSuffix}`,
|
|
87
87
|
})
|
|
88
|
-
tenantSlug = tenantResp.data.
|
|
88
|
+
tenantSlug = tenantResp.data.slug!
|
|
89
89
|
```
|
|
90
90
|
|
|
91
91
|
## 003 — sarah signs in tenant-scoped — the canonical client
|
|
@@ -87,7 +87,7 @@ ctx.sarahTenantlessApi = createIsolatedPlatformApi({
|
|
|
87
87
|
const tenantResp = await ctx.sarahTenantlessApi.tenants.create({
|
|
88
88
|
name: `Cookbook Payment Risk ${runSuffix}`,
|
|
89
89
|
})
|
|
90
|
-
tenantSlug = tenantResp.data.
|
|
90
|
+
tenantSlug = tenantResp.data.slug!
|
|
91
91
|
```
|
|
92
92
|
|
|
93
93
|
## 003 — sarah signs in tenant-scoped — the canonical client
|
|
@@ -5,7 +5,7 @@ industry: healthcare
|
|
|
5
5
|
slug: contact-us-triage-with-llm
|
|
6
6
|
vitest_source:
|
|
7
7
|
- integration-tests/tests/healthcare/agent-driven-workflow.test.ts
|
|
8
|
-
- integration-tests/tests/healthcare/
|
|
8
|
+
- integration-tests/tests/healthcare/generic-tables.test.ts
|
|
9
9
|
- integration-tests/tests/healthcare/tools.test.ts
|
|
10
10
|
- integration-tests/tests/healthcare/bootstrap.test.ts
|
|
11
11
|
status: green
|
|
@@ -34,7 +34,7 @@ Three SMS actions are registered against the workflow, one per
|
|
|
34
34
|
bucket; the agent's output picks which one runs.
|
|
35
35
|
|
|
36
36
|
This cookbook walks the **whole** scenario: it provisions the
|
|
37
|
-
|
|
37
|
+
generic table, agent, and workflow, ingests three contact-us
|
|
38
38
|
submissions through the generic-table's auto-provisioned default
|
|
39
39
|
DAC, runs the workflow so the agent classifies each row, and
|
|
40
40
|
verifies the routing row-by-row — every Workflow Execution Log
|
|
@@ -58,7 +58,7 @@ this cookbook starts from there.
|
|
|
58
58
|
| Contact Us Triage AI agent | build |
|
|
59
59
|
| Contact Us Triage workflow | build |
|
|
60
60
|
|
|
61
|
-
The
|
|
61
|
+
The generic table's **default DAC** — auto-provisioned by the
|
|
62
62
|
platform when the generic table is created — is reused for
|
|
63
63
|
ingestion; no separate data source / tool / interop contract / DAC
|
|
64
64
|
is created. The setup file `_setup/healthcare.md` already
|
|
@@ -67,7 +67,7 @@ cookbook starts from there.
|
|
|
67
67
|
|
|
68
68
|
# Walkthrough
|
|
69
69
|
|
|
70
|
-
## 001 — create the Contact Us submissions
|
|
70
|
+
## 001 — create the Contact Us submissions generic table
|
|
71
71
|
|
|
72
72
|
The agent-driven workflow targets a Generic Table dataset
|
|
73
73
|
(`dataset_type: 'generic_table'`) rather than a regulated domain
|
|
@@ -79,7 +79,7 @@ The server-derived `name` is captured — the workflow run in §009
|
|
|
79
79
|
addresses the regulated table as `regulated_<name>`.
|
|
80
80
|
|
|
81
81
|
```typescript
|
|
82
|
-
const
|
|
82
|
+
const genericTableResp = await api.genericTables.create(tenantSlug, datalakeSlug, {
|
|
83
83
|
title: `Cookbook Contact Us ${runSuffix}`,
|
|
84
84
|
description: 'Inbound contact-us form submissions the Triage agent classifies.',
|
|
85
85
|
columns: [
|
|
@@ -125,8 +125,8 @@ const customDatasetResp = await api.genericTables.create(tenantSlug, datalakeSlu
|
|
|
125
125
|
},
|
|
126
126
|
],
|
|
127
127
|
})
|
|
128
|
-
|
|
129
|
-
ctx.gtName =
|
|
128
|
+
genericTableId = genericTableResp.data.id!
|
|
129
|
+
ctx.gtName = genericTableResp.data.name!
|
|
130
130
|
```
|
|
131
131
|
|
|
132
132
|
## 002 — create the SMS tool
|
|
@@ -286,7 +286,7 @@ const workflowResp = await api.workflows.create(tenantSlug, datalakeSlug, {
|
|
|
286
286
|
name: `Cookbook Contact Us Triage Workflow ${runSuffix}`,
|
|
287
287
|
description: 'Triages contact-us submissions into appointment_request/job_inquiry/flag_spam via an LLM agent; one SMS action per bucket.',
|
|
288
288
|
dataset_type: 'generic_table',
|
|
289
|
-
generic_table_id:
|
|
289
|
+
generic_table_id: genericTableId,
|
|
290
290
|
skip_mdm_resolution: true,
|
|
291
291
|
status: 'live',
|
|
292
292
|
filter_config: {
|
|
@@ -593,11 +593,11 @@ end-to-end, not merely provisioned.
|
|
|
593
593
|
- `.agent/workflows.md` — standard and agent-driven workflow
|
|
594
594
|
primitives; `ai_agents`, `context_mapping_config`, and
|
|
595
595
|
agent-output decision interpolation
|
|
596
|
-
- `.agent/
|
|
596
|
+
- `.agent/generic_tables.md` — generic-table creation and the
|
|
597
597
|
auto-provisioned identity contract + default DAC
|
|
598
598
|
- `integration-tests/tests/healthcare/agent-driven-workflow.test.ts` —
|
|
599
599
|
the anchor green test (positive branch §1, §3, §4, §6, §7) these
|
|
600
600
|
snippets are lifted from
|
|
601
|
-
- `integration-tests/tests/healthcare/
|
|
601
|
+
- `integration-tests/tests/healthcare/generic-tables.test.ts`,
|
|
602
602
|
`tools.test.ts` — the per-resource create snippets are lifted
|
|
603
603
|
from
|
|
@@ -5,7 +5,7 @@ industry: foundation
|
|
|
5
5
|
slug: score-leads-with-llm-categorization
|
|
6
6
|
vitest_source:
|
|
7
7
|
- integration-tests/tests/foundation/agent-driven-workflow.test.ts
|
|
8
|
-
- integration-tests/tests/foundation/
|
|
8
|
+
- integration-tests/tests/foundation/generic-tables.test.ts
|
|
9
9
|
- integration-tests/tests/foundation/tools.test.ts
|
|
10
10
|
- integration-tests/tests/foundation/bootstrap.test.ts
|
|
11
11
|
status: green
|
|
@@ -55,13 +55,13 @@ cookbook starts from there.
|
|
|
55
55
|
|
|
56
56
|
| Resource provisioned | Owner |
|
|
57
57
|
|----------------------------------|-------------|
|
|
58
|
-
| Lead Submissions
|
|
58
|
+
| Lead Submissions generic table | build |
|
|
59
59
|
| SMS tool (SNS-backed) | build |
|
|
60
60
|
| LLM tool (Ollama chat completion)| build |
|
|
61
61
|
| Score Lead AI agent | build |
|
|
62
62
|
| Score Lead workflow | build |
|
|
63
63
|
|
|
64
|
-
The
|
|
64
|
+
The generic table's **default DAC** — auto-provisioned by the
|
|
65
65
|
platform when the generic table is created — is reused for ingestion;
|
|
66
66
|
no separate data source / tool / interop contract / DAC is created.
|
|
67
67
|
The setup file `_setup/foundation.md` already provisioned the tenant +
|
|
@@ -69,7 +69,7 @@ datalake + tenant-scoped client; this cookbook starts from there.
|
|
|
69
69
|
|
|
70
70
|
# Walkthrough
|
|
71
71
|
|
|
72
|
-
## 001 — create the Lead Submissions
|
|
72
|
+
## 001 — create the Lead Submissions generic table
|
|
73
73
|
|
|
74
74
|
The agent-driven workflow targets a Generic Table dataset
|
|
75
75
|
(`dataset_type: 'generic_table'`) rather than a regulated domain
|
|
@@ -81,7 +81,7 @@ in production. The server-derived `name` is captured — the workflow
|
|
|
81
81
|
run in §009 addresses the regulated table as `regulated_<name>`.
|
|
82
82
|
|
|
83
83
|
```typescript
|
|
84
|
-
const
|
|
84
|
+
const genericTableResp = await api.genericTables.create(tenantSlug, datalakeSlug, {
|
|
85
85
|
title: `Cookbook Lead Submissions ${runSuffix}`,
|
|
86
86
|
description: 'Inbound lead-capture form rows the Score Lead agent classifies.',
|
|
87
87
|
columns: [
|
|
@@ -135,8 +135,8 @@ const customDatasetResp = await api.genericTables.create(tenantSlug, datalakeSlu
|
|
|
135
135
|
},
|
|
136
136
|
],
|
|
137
137
|
})
|
|
138
|
-
|
|
139
|
-
ctx.gtName =
|
|
138
|
+
genericTableId = genericTableResp.data.id!
|
|
139
|
+
ctx.gtName = genericTableResp.data.name!
|
|
140
140
|
```
|
|
141
141
|
|
|
142
142
|
## 002 — create the SMS tool
|
|
@@ -294,7 +294,7 @@ const workflowResp = await api.workflows.create(tenantSlug, datalakeSlug, {
|
|
|
294
294
|
name: `Cookbook Score Lead Workflow ${runSuffix}`,
|
|
295
295
|
description: 'Classifies lead_submissions into hot/warm/cold/spam via an LLM agent; one SMS action per band.',
|
|
296
296
|
dataset_type: 'generic_table',
|
|
297
|
-
generic_table_id:
|
|
297
|
+
generic_table_id: genericTableId,
|
|
298
298
|
skip_mdm_resolution: true,
|
|
299
299
|
status: 'live',
|
|
300
300
|
filter_config: {
|
|
@@ -590,13 +590,13 @@ provisioned.
|
|
|
590
590
|
- `.agent/workflows.md` — standard and agent-driven workflow
|
|
591
591
|
primitives; `ai_agents`, `context_mapping_config`, and agent-output
|
|
592
592
|
decision interpolation
|
|
593
|
-
- `.agent/
|
|
593
|
+
- `.agent/generic_tables.md` — generic-table creation and the
|
|
594
594
|
auto-provisioned identity contract + default DAC
|
|
595
595
|
- `.agent/ai_sandbox.md` — bounds on the Liquid sandbox the
|
|
596
596
|
context-mapping and decision templates execute inside
|
|
597
597
|
- `integration-tests/tests/foundation/agent-driven-workflow.test.ts` —
|
|
598
598
|
the anchor green test (§1–§5) these snippets are lifted from
|
|
599
|
-
- `integration-tests/tests/foundation/
|
|
599
|
+
- `integration-tests/tests/foundation/generic-tables.test.ts` — §2 the
|
|
600
600
|
Generic Table create snippet is lifted from
|
|
601
601
|
- `integration-tests/tests/foundation/tools.test.ts` — the SMS tool
|
|
602
602
|
create snippet is lifted from
|
|
@@ -89,7 +89,7 @@ A single activation client can bind:
|
|
|
89
89
|
|
|
90
90
|
- one or more `custom` / `system` contracts (e.g. patient +
|
|
91
91
|
appointment for a multi-resource ingestion), OR
|
|
92
|
-
- a single `identity` contract bound to a
|
|
92
|
+
- a single `identity` contract bound to a generic table
|
|
93
93
|
(the auto-created identity contract from
|
|
94
94
|
`interoperability_contracts.md` §5).
|
|
95
95
|
|
|
@@ -489,7 +489,7 @@ first.
|
|
|
489
489
|
Cross-datalake references return 404 on the offending id.
|
|
490
490
|
|
|
491
491
|
4. **Custom-dataset identity contracts get their own
|
|
492
|
-
activation client.** When you ingest into a
|
|
492
|
+
activation client.** When you ingest into a generic table,
|
|
493
493
|
bind the table's auto-created identity contract (see
|
|
494
494
|
`interoperability_contracts.md` §5) to a dedicated activation
|
|
495
495
|
client. Don't mix it with non-identity contracts on the
|
package/.agent/data_sources.md
CHANGED
|
@@ -125,10 +125,10 @@ Standard JSON:API envelopes per `errors.md`. Common rejections:
|
|
|
125
125
|
| `/status` | value not in the typed enum |
|
|
126
126
|
| `/is_default` | another source already marked default |
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
rejections
|
|
131
|
-
|
|
128
|
+
All of these come back as a 422 `AlveraApiError` thrown from the
|
|
129
|
+
SDK on submit — both structural failures (Layer 1) and semantic
|
|
130
|
+
rejections like uniqueness / is_default conflict (Layer 2). The
|
|
131
|
+
SDK does not validate client-side. See `errors.md`.
|
|
132
132
|
|
|
133
133
|
## 5. Lifecycle
|
|
134
134
|
|
package/.agent/datalakes.md
CHANGED
|
@@ -33,11 +33,11 @@ import type {
|
|
|
33
33
|
DatalakeRequestWritable,
|
|
34
34
|
DatalakeResponse,
|
|
35
35
|
} from '@alvera-ai/platform-sdk'
|
|
36
|
-
import { vDatalakeRequestWritable } from '@alvera-ai/platform-sdk'
|
|
37
36
|
```
|
|
38
37
|
|
|
39
|
-
See `type_naming.md` for the `Writable`
|
|
40
|
-
|
|
38
|
+
See `type_naming.md` for the `Writable` convention and the
|
|
39
|
+
universal server-derived field set. (The SDK exports types only —
|
|
40
|
+
there is no runtime validator; validation is server-authoritative.)
|
|
41
41
|
|
|
42
42
|
### Polymorphic cloud-storage embed
|
|
43
43
|
|
|
@@ -322,10 +322,9 @@ The platform returns a standardized JSON:API envelope — see
|
|
|
322
322
|
{ pointer }, title, detail }, ...] }`) and the two-layer
|
|
323
323
|
validation model (structural cast → server-side semantic check).
|
|
324
324
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
narrowing pattern.
|
|
325
|
+
All validation errors come back from the server as a thrown 422
|
|
326
|
+
`AlveraApiError` (the SDK does not validate client-side). See
|
|
327
|
+
`errors.md` for the typed narrowing pattern.
|
|
329
328
|
|
|
330
329
|
Datalake-specific common rejections:
|
|
331
330
|
|
|
@@ -451,11 +450,9 @@ instead.
|
|
|
451
450
|
cloud-storage embeds' `access_key_id` / `secret_access_key` — are
|
|
452
451
|
write-only: present on the request type, absent from the response
|
|
453
452
|
type. A naive spread of `current` into the PUT body is therefore
|
|
454
|
-
missing those 8 required fields. The
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
equivalent envelope from the server (see `errors.md`). Re-supply
|
|
458
|
-
them explicitly from your secret store:
|
|
453
|
+
missing those 8 required fields. The server rejects this with a 422
|
|
454
|
+
`AlveraApiError` pointing at the missing fields (see `errors.md`).
|
|
455
|
+
Re-supply them explicitly from your secret store:
|
|
459
456
|
|
|
460
457
|
```typescript
|
|
461
458
|
const { data: current } = await api.datalakes.get(tenantSlug, datalakeId)
|
|
@@ -591,7 +588,7 @@ for (const name of catalog.datasets) {
|
|
|
591
588
|
|
|
592
589
|
1. **POST does NOT auto-enqueue migration.** The create response
|
|
593
590
|
returns immediately with `status: 'new'`; Datalake-DB-resident
|
|
594
|
-
downstream resources (tools, AI agents,
|
|
591
|
+
downstream resources (tools, AI agents, generic tables,
|
|
595
592
|
interoperability contracts, data activation clients,
|
|
596
593
|
workflows) MUST wait for `ready` (see §5 Lifecycle for the
|
|
597
594
|
storage-location split). The canonical sequence:
|
|
@@ -656,8 +653,8 @@ for (const name of catalog.datasets) {
|
|
|
656
653
|
would have hit the conflict.
|
|
657
654
|
|
|
658
655
|
6. **`iam_role` auth still requires a `*_pass` value structurally.**
|
|
659
|
-
The TypeScript type and the
|
|
660
|
-
|
|
656
|
+
The TypeScript type (and the server's schema) treat the field as
|
|
657
|
+
a required string. Supply a sentinel literal (e.g.
|
|
661
658
|
`"unused-iam-role"`); the platform clears it server-side.
|
|
662
659
|
|
|
663
660
|
7. **`cloud_storage_type` lives inside each embed, not at the
|
package/.agent/debugging.md
CHANGED
|
@@ -45,9 +45,9 @@ HTTP traffic at all — nothing to redact.
|
|
|
45
45
|
|
|
46
46
|
## Safety: the load-bearing piece
|
|
47
47
|
|
|
48
|
-
The SDK never sees secret REFERENCES (
|
|
49
|
-
is a consumer-side convention used by some — CLI
|
|
50
|
-
for instance). By the time a request body reaches the SDK,
|
|
48
|
+
The SDK never sees secret REFERENCES (`<%= name %>` Eta
|
|
49
|
+
syntax is a consumer-side convention used by some — CLI
|
|
50
|
+
manifests, for instance). By the time a request body reaches the SDK,
|
|
51
51
|
credentials are already substituted to their literal values.
|
|
52
52
|
If you enable debug logging without populating
|
|
53
53
|
`redactStrings`, those literals appear in plaintext in your
|
package/.agent/errors.md
CHANGED
|
@@ -1,56 +1,29 @@
|
|
|
1
1
|
# Errors
|
|
2
2
|
|
|
3
|
-
When you call an SDK method,
|
|
3
|
+
When you call an SDK method, two things can happen:
|
|
4
4
|
|
|
5
5
|
1. **Success** — `{ data, meta? }` returned (typed).
|
|
6
|
-
2. **
|
|
7
|
-
|
|
8
|
-
3. **Asynchronous throw** — the SDK threw the server's response
|
|
9
|
-
body verbatim after the HTTP call returned non-2xx.
|
|
6
|
+
2. **Throw** — the SDK throws the server's response body verbatim after
|
|
7
|
+
the HTTP call returned a non-2xx status.
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
The SDK does **not** validate request bodies on the client. Validation is
|
|
10
|
+
server-authoritative: a malformed body is rejected by the platform and
|
|
11
|
+
surfaced to you as a thrown **422** body. There is no synchronous
|
|
12
|
+
client-side validation error to catch — every validation failure comes back
|
|
13
|
+
from the server in the standardized envelope below.
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
> Driving the platform through the `alvera` **CLI** instead of the SDK? Then
|
|
16
|
+
> `alvera plan` runs a local pre-flight check against the spec and reports
|
|
17
|
+
> errors *before* any HTTP call. That's a CLI feature (it validates the spec
|
|
18
|
+
> locally); it is not SDK runtime behavior. SDK callers get errors from the
|
|
19
|
+
> server.
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
(`v<Resource>RequestWritable`). The SDK runs this validator on
|
|
18
|
-
the request body BEFORE issuing the HTTP call. If your input
|
|
19
|
-
fails the validator, the SDK throws a `ValiError` synchronously
|
|
20
|
-
— nothing reaches the platform.
|
|
21
|
+
## The thrown value (post-HTTP)
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const { data } = await api.tools.create(
|
|
27
|
-
tenantSlug, datalakeSlug, body,
|
|
28
|
-
)
|
|
29
|
-
} catch (err) {
|
|
30
|
-
if (err instanceof ValiError) {
|
|
31
|
-
// err.issues is an array of valibot issues; each has
|
|
32
|
-
// .path (a JSON-pointer-like path) and .message
|
|
33
|
-
for (const issue of err.issues) {
|
|
34
|
-
// issue.path is an array of path segments;
|
|
35
|
-
// issue.message is the failure reason.
|
|
36
|
-
}
|
|
37
|
-
return
|
|
38
|
-
}
|
|
39
|
-
throw err // re-throw anything not a ValiError
|
|
40
|
-
}
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
Layer-1 errors (missing required, wrong type, invalid enum,
|
|
44
|
-
format violations) almost always fire here client-side.
|
|
45
|
-
Running the validator client-side avoids wasted HTTP round-
|
|
46
|
-
trips — consumers should NOT bypass it on mutations.
|
|
47
|
-
|
|
48
|
-
## Server-side: SDK throw (post-HTTP)
|
|
49
|
-
|
|
50
|
-
When the platform returns a non-2xx status, **the SDK throws the
|
|
51
|
-
parsed response body verbatim** as the thrown value — not an
|
|
52
|
-
`Error` subclass, just the body object. Consumers narrow it with
|
|
53
|
-
a type guard:
|
|
23
|
+
When the platform returns a non-2xx status, **the SDK throws the parsed
|
|
24
|
+
response body verbatim** as the thrown value — not an `Error` subclass, just
|
|
25
|
+
the body object (decorated with an `_httpStatus` field so you can branch on
|
|
26
|
+
the status). Narrow it with a type guard:
|
|
54
27
|
|
|
55
28
|
```typescript
|
|
56
29
|
try {
|
|
@@ -78,17 +51,13 @@ try {
|
|
|
78
51
|
}
|
|
79
52
|
```
|
|
80
53
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
semantic rules, foreign-key constraints. If you see this
|
|
84
|
-
thrown body and the same input passes valibot, the failure
|
|
85
|
-
is a server-side semantic rule the OpenAPI spec can't
|
|
86
|
-
express.
|
|
54
|
+
The thrown body is typed as `AlveraApiError` (exported from the package root)
|
|
55
|
+
for 422s, and the simpler `ErrorResponse` for some other statuses.
|
|
87
56
|
|
|
88
57
|
## 422 envelope shape
|
|
89
58
|
|
|
90
|
-
The platform standardizes ALL 422 response bodies on a single
|
|
91
|
-
|
|
59
|
+
The platform standardizes ALL 422 response bodies on a single shape —
|
|
60
|
+
regardless of which validation layer fired:
|
|
92
61
|
|
|
93
62
|
```json
|
|
94
63
|
{
|
|
@@ -103,45 +72,41 @@ shape — regardless of which validation layer fired:
|
|
|
103
72
|
}
|
|
104
73
|
```
|
|
105
74
|
|
|
106
|
-
- `errors` is always an ARRAY of error objects (never a
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
level fields; `/nested/field` for embeds) naming the
|
|
110
|
-
offending field on the request body.
|
|
75
|
+
- `errors` is always an ARRAY of error objects (never a field-keyed map).
|
|
76
|
+
- `source.pointer` is a JSON Pointer (`/field_name` for top-level fields;
|
|
77
|
+
`/nested/field` for embeds) naming the offending field on the request body.
|
|
111
78
|
- `detail` is the specific reason; `title` is a brief summary.
|
|
112
|
-
- Multiple errors may share the same `source.pointer` if more
|
|
113
|
-
|
|
79
|
+
- Multiple errors may share the same `source.pointer` if more than one check
|
|
80
|
+
fired on the same field.
|
|
114
81
|
|
|
115
82
|
## Two layers behind the standardized 422
|
|
116
83
|
|
|
117
|
-
The platform validates a request in two layers; both surface
|
|
118
|
-
|
|
84
|
+
The platform validates a request in two layers; both surface through the
|
|
85
|
+
same envelope above:
|
|
119
86
|
|
|
120
87
|
**Layer 1 — OpenAPI cast (structural)**
|
|
121
88
|
|
|
122
|
-
Runs at the plug layer before the controller. Catches:
|
|
123
|
-
required fields, wrong types, invalid enums, unknown keys,
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
89
|
+
Runs at the plug layer (`OpenApiSpex`) before the controller. Catches:
|
|
90
|
+
missing required fields, wrong types, invalid enums, unknown keys, format
|
|
91
|
+
violations (UUID patterns, etc.). This is the *same* OpenAPI schema the SDK's
|
|
92
|
+
types are generated from, so a body that satisfies the TypeScript types
|
|
93
|
+
almost always clears layer 1.
|
|
127
94
|
|
|
128
95
|
**Layer 2 — server-side semantic validator (post-cast)**
|
|
129
96
|
|
|
130
|
-
Runs after the structural cast succeeds. Catches: uniqueness,
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
layers from the JSON.
|
|
97
|
+
Runs after the structural cast succeeds. Catches: uniqueness, cross-field
|
|
98
|
+
rules (e.g. `auth_method = "iam_role"` combined with conflicting credential
|
|
99
|
+
fields), foreign-key constraints, custom platform validations. Layer-2
|
|
100
|
+
errors are normalized into the same envelope shape as layer 1 — consumers
|
|
101
|
+
don't distinguish layers from the JSON.
|
|
136
102
|
|
|
137
|
-
Practical consequence:
|
|
138
|
-
|
|
139
|
-
have — keep the SDK pin fresh).
|
|
103
|
+
Practical consequence: every validation error reaches you as a thrown 422
|
|
104
|
+
body. Read `source.pointer`, fix that field, resubmit.
|
|
140
105
|
|
|
141
106
|
## Concrete examples
|
|
142
107
|
|
|
143
|
-
**Layer 1 — missing required fields** (POST `/agentic-
|
|
144
|
-
|
|
108
|
+
**Layer 1 — missing required fields** (POST `/agentic-workflows` with empty
|
|
109
|
+
body):
|
|
145
110
|
|
|
146
111
|
```json
|
|
147
112
|
{
|
|
@@ -154,8 +119,7 @@ workflows` with empty body):
|
|
|
154
119
|
}
|
|
155
120
|
```
|
|
156
121
|
|
|
157
|
-
**Layer 1 — invalid enum value** (`dataset_type:
|
|
158
|
-
"nonexistent_type"`):
|
|
122
|
+
**Layer 1 — invalid enum value** (`dataset_type: "nonexistent_type"`):
|
|
159
123
|
|
|
160
124
|
```json
|
|
161
125
|
{
|
|
@@ -166,8 +130,8 @@ workflows` with empty body):
|
|
|
166
130
|
}
|
|
167
131
|
```
|
|
168
132
|
|
|
169
|
-
**Layer 2 — semantic rule** (DELETE on a default-flagged data
|
|
170
|
-
|
|
133
|
+
**Layer 2 — semantic rule** (DELETE on a default-flagged data activation
|
|
134
|
+
client):
|
|
171
135
|
|
|
172
136
|
```json
|
|
173
137
|
{
|
|
@@ -178,8 +142,8 @@ activation client):
|
|
|
178
142
|
}
|
|
179
143
|
```
|
|
180
144
|
|
|
181
|
-
**Layer 2 — uniqueness** (POST `/admin/invites` for an
|
|
182
|
-
|
|
145
|
+
**Layer 2 — uniqueness** (POST `/admin/invites` for an already-invited
|
|
146
|
+
email):
|
|
183
147
|
|
|
184
148
|
```json
|
|
185
149
|
{
|
|
@@ -192,10 +156,10 @@ already-invited email):
|
|
|
192
156
|
|
|
193
157
|
## HTTP status codes (under the hood)
|
|
194
158
|
|
|
195
|
-
The SDK abstracts HTTP responses behind typed methods.
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
159
|
+
The SDK abstracts HTTP responses behind typed methods. Consumers mostly see
|
|
160
|
+
typed data or thrown bodies; the thrown body carries `_httpStatus`, and raw
|
|
161
|
+
status is also visible via the SDK's `--debug`-style interceptor. The
|
|
162
|
+
platform maps:
|
|
199
163
|
|
|
200
164
|
| Code | When it fires |
|
|
201
165
|
|------|----------------------------------------------------------------|
|
|
@@ -210,26 +174,23 @@ interceptor. The platform maps:
|
|
|
210
174
|
|
|
211
175
|
## Retry semantics
|
|
212
176
|
|
|
213
|
-
Treat 5xx as infra noise — retry with exponential backoff.
|
|
214
|
-
|
|
215
|
-
|
|
177
|
+
Treat 5xx as infra noise — retry with exponential backoff. Treat 422 as a
|
|
178
|
+
real consumer-correctable error — fix the body and resubmit. **Never retry a
|
|
179
|
+
422 with the same body.**
|
|
216
180
|
|
|
217
181
|
## Mapping `source.pointer` back to your TypeScript type
|
|
218
182
|
|
|
219
|
-
A `source.pointer` like `/regulated_data_db_reader_user` maps
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
carries the embed path:
|
|
183
|
+
A `source.pointer` like `/regulated_data_db_reader_user` maps directly to the
|
|
184
|
+
`regulated_data_db_reader_user` field on `DatalakeRequestWritable`. For
|
|
185
|
+
nested embeds, the pointer carries the embed path:
|
|
223
186
|
`/unregulated_cloud_storage/access_key_id`.
|
|
224
187
|
|
|
225
|
-
For polymorphic embeds, check the discriminator field's
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
TypeScript type — the corpus's resource pages are the source
|
|
235
|
-
of truth for which fields exist on which embed.
|
|
188
|
+
For polymorphic embeds, check the discriminator field's pointer first — if
|
|
189
|
+
`/tool_body_type` (or `/cloud_storage_type`, etc.) is in the errors array,
|
|
190
|
+
fix that BEFORE chasing other fields on the embed. The wrong discriminator
|
|
191
|
+
value cascades into spurious required-list failures on the wrong branch's
|
|
192
|
+
fields. See `mutations.md` for the discriminator naming convention.
|
|
193
|
+
|
|
194
|
+
Each per-resource MD names its `<Resource>RequestWritable` TypeScript type —
|
|
195
|
+
the corpus's resource pages are the source of truth for which fields exist on
|
|
196
|
+
which embed.
|