@alvera-ai/platform-sdk 0.10.0-rc.8 → 0.11.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/.agent/AGENTS.md +499 -0
- package/.agent/account_management.md +456 -0
- package/.agent/action_status_updaters.md +264 -0
- package/.agent/ai_agents.md +462 -0
- package/.agent/ai_sandbox.md +265 -0
- package/.agent/async.md +112 -0
- package/.agent/connected_apps.md +408 -0
- package/.agent/cookbook/_fixtures/README.md +106 -0
- package/.agent/cookbook/_fixtures/accounts_receivable/_customers_accounts_receivable_customer.liquid +32 -0
- package/.agent/cookbook/_fixtures/accounts_receivable/_customers_accounts_receivable_mdm.liquid +20 -0
- package/.agent/cookbook/_fixtures/accounts_receivable/stripe_customers_batch1.csv +5 -0
- package/.agent/cookbook/_fixtures/foundation/_lead_submissions_foundation_generic_table.liquid +33 -0
- package/.agent/cookbook/_fixtures/foundation/_lead_submissions_foundation_legal_entity.liquid +88 -0
- package/.agent/cookbook/_fixtures/foundation/_lead_submissions_foundation_mdm.liquid +48 -0
- package/.agent/cookbook/_fixtures/healthcare/_cahps_appointments_healthcare_appointment.liquid +47 -0
- package/.agent/cookbook/_fixtures/healthcare/_cahps_appointments_healthcare_mdm.liquid +24 -0
- package/.agent/cookbook/_fixtures/healthcare/_cahps_appointments_healthcare_patient.liquid +38 -0
- package/.agent/cookbook/_fixtures/healthcare/memorandum-of-association-01.png +0 -0
- package/.agent/cookbook/_fixtures/healthcare/sample_two_page.pdf +43 -0
- package/.agent/cookbook/_fixtures/payment_risk/_compliance_screenings_payment_risk_compliance_screening.liquid +59 -0
- package/.agent/cookbook/_fixtures/payment_risk/_compliance_screenings_payment_risk_mdm.liquid +36 -0
- package/.agent/cookbook/_fixtures/payment_risk/_payment_accounts_payment_risk_mdm.liquid +30 -0
- package/.agent/cookbook/_fixtures/payment_risk/_payment_accounts_payment_risk_payment_account.liquid +55 -0
- package/.agent/cookbook/_setup/accounts_receivable.md +282 -0
- package/.agent/cookbook/_setup/foundation.md +277 -0
- package/.agent/cookbook/_setup/healthcare.md +279 -0
- package/.agent/cookbook/_setup/payment_risk.md +283 -0
- package/.agent/cookbook/action-status-updaters.md +212 -0
- package/.agent/cookbook/ai-agent-invoke.md +243 -0
- package/.agent/cookbook/appointment-review-sms-workflow.md +761 -0
- package/.agent/cookbook/birthday-greeting-sms-trigger.md +656 -0
- package/.agent/cookbook/bulk-ingest.md +254 -0
- package/.agent/cookbook/contact-us-triage-with-llm.md +622 -0
- package/.agent/cookbook/custom-tables.md +201 -0
- package/.agent/cookbook/dunning-sms-for-delinquent.md +619 -0
- package/.agent/cookbook/invite-team.md +194 -0
- package/.agent/cookbook/kyc-notification-on-account-activation.md +619 -0
- package/.agent/cookbook/rest-fetch.md +246 -0
- package/.agent/cookbook/sanctions-screening-with-agent-review.md +733 -0
- package/.agent/cookbook/score-leads-with-llm-categorization.md +624 -0
- package/.agent/cookbook/system-templates.md +129 -0
- package/.agent/cookbook/triage-prospects-by-priority.md +533 -0
- package/.agent/cookbook/welcome-sms-for-customers.md +607 -0
- package/.agent/data_activation_clients.md +559 -0
- package/.agent/data_sources.md +235 -0
- package/.agent/datalakes.md +714 -0
- package/.agent/debugging.md +137 -0
- package/.agent/errors.md +190 -0
- package/.agent/generic_tables.md +351 -0
- package/.agent/interoperability_contracts.md +417 -0
- package/.agent/mdm.md +293 -0
- package/.agent/mutations.md +126 -0
- package/.agent/templates.md +98 -0
- package/.agent/tool-call-configs.md +90 -0
- package/.agent/tools.md +547 -0
- package/.agent/type_naming.md +129 -0
- package/.agent/workflows.md +617 -0
- package/README.md +178 -0
- package/dist/bin/platform-sdk.d.mts +1 -0
- package/dist/bin/platform-sdk.mjs +106 -0
- package/dist/bin/platform-sdk.mjs.map +1 -0
- package/dist/index.d.mts +1310 -44116
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1194 -7344
- package/dist/index.mjs.map +1 -1
- package/package.json +19 -10
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
# Interoperability contracts
|
|
2
|
+
|
|
3
|
+
An **interoperability contract** is the transformation step between
|
|
4
|
+
a raw inbound row (CSV, JSON, etc.) and a typed datalake resource.
|
|
5
|
+
Each contract carries:
|
|
6
|
+
|
|
7
|
+
- a `resource_type` (the target shape — `patient`, `customer`,
|
|
8
|
+
`payment_account`, `legal_entity`, `generic_table`, etc.)
|
|
9
|
+
- a `template_config` (the Liquid template that renders the inbound
|
|
10
|
+
row into the target shape)
|
|
11
|
+
- an `mdm_input_config` (the Liquid template that renders the
|
|
12
|
+
matching subject identity for entity resolution — see `mdm.md`
|
|
13
|
+
for the full MDM surface, including the `api.mdm.verify` verb
|
|
14
|
+
invoked at workflow action time)
|
|
15
|
+
- an optional `filter_template` (a Liquid pre-filter that decides
|
|
16
|
+
whether a row enters the pipeline at all)
|
|
17
|
+
|
|
18
|
+
SDK namespace: `api.interoperabilityContracts`.
|
|
19
|
+
|
|
20
|
+
The contract is **Datalake-DB-resident** — `create()` requires the
|
|
21
|
+
parent datalake at `status: 'ready'` (see `datalakes.md` §5).
|
|
22
|
+
|
|
23
|
+
## 1. Wire shape
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import type {
|
|
27
|
+
InteroperabilityContractRequestWritable,
|
|
28
|
+
InteroperabilityContractResponse,
|
|
29
|
+
} from '@alvera-ai/platform-sdk'
|
|
30
|
+
|
|
31
|
+
const { data: created } = await api.interoperabilityContracts.create(
|
|
32
|
+
tenantSlug,
|
|
33
|
+
datalakeSlug,
|
|
34
|
+
{
|
|
35
|
+
name: 'Inbound CRM → Customer',
|
|
36
|
+
description: 'Vendor CRM rows → AccountsReceivable Customer',
|
|
37
|
+
resource_type: 'customer',
|
|
38
|
+
filter_template: undefined, // optional
|
|
39
|
+
template_config: { type: 'custom', body: liquidBody },
|
|
40
|
+
mdm_input_config: { type: 'custom', body: mdmBody },
|
|
41
|
+
generic_table_id: null, // see §2
|
|
42
|
+
},
|
|
43
|
+
)
|
|
44
|
+
// created.id, created.slug — server-derived
|
|
45
|
+
// created.type — mirrors template_config.type
|
|
46
|
+
// created.resource_type === body.resource_type
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Both polymorphic embeds use the SAME shared `TemplateConfig`
|
|
50
|
+
shape with discriminator `type`:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
template_config.type 'system' | 'custom' | 'identity' | 'null'
|
|
54
|
+
mdm_input_config.type 'system' | 'custom' | 'identity' | 'null'
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Default for both is `'null'`. The wire accepts all four values
|
|
58
|
+
on each surface; semantic narrowing is a runtime concern, not
|
|
59
|
+
a schema one:
|
|
60
|
+
|
|
61
|
+
- `custom` — caller-supplied `body` string (Liquid template).
|
|
62
|
+
Pair with `body`; leave `path` null.
|
|
63
|
+
- `system` — references a platform-shipped template by
|
|
64
|
+
filesystem path. Pair with `path` (required); leave `body`
|
|
65
|
+
null. See `templates.md`.
|
|
66
|
+
- `identity` — pass-through. The inbound row is forwarded
|
|
67
|
+
unchanged. Used by the auto-created identity contracts for
|
|
68
|
+
generic tables. Leave both `body` and `path` null.
|
|
69
|
+
- `null` — disabled / "row IS the subject" on the mdm surface.
|
|
70
|
+
Leave both `body` and `path` null.
|
|
71
|
+
|
|
72
|
+
### TemplateConfig embed fields
|
|
73
|
+
|
|
74
|
+
Both `template_config` and `mdm_input_config` are instances of
|
|
75
|
+
the shared `TemplateConfig` embed:
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
type required enum — 'system' | 'custom' | 'identity' | 'null' (default 'null')
|
|
79
|
+
body optional string — Liquid template body; REQUIRED iff type === 'custom'
|
|
80
|
+
path optional string — filesystem path; REQUIRED iff type === 'system'
|
|
81
|
+
output_schema server-injected — JSON Schema { type: 'object' } for this surface; not caller-set
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Server validation enforces the body/path pairing — supplying
|
|
85
|
+
`body` with `type: 'system'`, or `path` with `type: 'custom'`,
|
|
86
|
+
returns 422.
|
|
87
|
+
|
|
88
|
+
## 2. Rules the type cannot encode
|
|
89
|
+
|
|
90
|
+
### `generic_table_id` is conditionally required
|
|
91
|
+
|
|
92
|
+
When `resource_type === 'generic_table'`, the body MUST include a
|
|
93
|
+
non-null `generic_table_id` identifying the target table. For
|
|
94
|
+
every other `resource_type`, the field must be `null`. The
|
|
95
|
+
TypeScript type permits either value; the validator and server
|
|
96
|
+
enforce the conditional rule.
|
|
97
|
+
|
|
98
|
+
### `resource_type` is industry-scoped
|
|
99
|
+
|
|
100
|
+
The valid set of `resource_type` values is determined by the
|
|
101
|
+
parent datalake's `data_domain`. Generic concepts like
|
|
102
|
+
`'generic_table'` are accepted everywhere; domain-anchored
|
|
103
|
+
concepts (`'patient'`, `'customer'`, `'payment_account'`,
|
|
104
|
+
`'legal_entity'`, ...) only resolve within their owning industry.
|
|
105
|
+
Don't hardcode a fixed enum across industries — discover the
|
|
106
|
+
valid set at runtime via the platform's domain catalog (see
|
|
107
|
+
`datalakes.md` §5 `.systemDatasets`).
|
|
108
|
+
|
|
109
|
+
### `filter_template` has INVERTED semantics
|
|
110
|
+
|
|
111
|
+
This is the single highest-value gotcha on this surface. A
|
|
112
|
+
`filter_template` is a Liquid template that decides whether a
|
|
113
|
+
row enters the pipeline:
|
|
114
|
+
|
|
115
|
+
- **Empty render → PASS** (row enters the pipeline).
|
|
116
|
+
- **Non-empty render → SKIP** (row is dropped).
|
|
117
|
+
|
|
118
|
+
This is the opposite of most filter DSLs ("truthy = keep"). A
|
|
119
|
+
correctly-shaped filter looks like:
|
|
120
|
+
|
|
121
|
+
```liquid
|
|
122
|
+
{% if msg.row.should_drop == 'Y' %}drop{% endif %}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Renders `"drop"` when the condition fires (SKIP); renders empty
|
|
126
|
+
otherwise (PASS). Inverting `{% unless %}...{% endunless %}` is
|
|
127
|
+
equivalent and stylistically common — but always anchor on the
|
|
128
|
+
empty-render-passes rule, not on the Liquid keyword.
|
|
129
|
+
|
|
130
|
+
If you omit `filter_template`, every row passes through.
|
|
131
|
+
|
|
132
|
+
### `template_config` body is Liquid, with row data on `msg`
|
|
133
|
+
|
|
134
|
+
Templates receive the inbound row on the `msg` variable. The
|
|
135
|
+
exact accessor depends on the source shape — CSV-shaped sources
|
|
136
|
+
typically use `msg.<column>` (with `msg.row.<column>` available
|
|
137
|
+
for nested cases); JSON-shaped sources mirror the inbound JSON
|
|
138
|
+
under `msg.<field>`. The render output is JSON — the platform
|
|
139
|
+
parses the rendered string as JSON and treats it as the target
|
|
140
|
+
resource body.
|
|
141
|
+
|
|
142
|
+
A render that produces invalid JSON fails at sandbox-run with
|
|
143
|
+
`stage: 'failed'` (see §5). Always sandbox-run a contract before
|
|
144
|
+
binding it to a live data activation client.
|
|
145
|
+
|
|
146
|
+
## 3. Field ownership
|
|
147
|
+
|
|
148
|
+
**Server-derived (Response-only).** Universal set from
|
|
149
|
+
`type_naming.md`, plus:
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
type string — mirrors template_config.type at create time
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**Caller-supplied (round-trip).**
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
name required string — agent-facing label
|
|
159
|
+
description optional string — narrative
|
|
160
|
+
resource_type required string — target shape (industry-scoped)
|
|
161
|
+
filter_template optional string — Liquid pre-filter (inverted semantics)
|
|
162
|
+
template_config required embed — { type, body?, path? }
|
|
163
|
+
mdm_input_config optional embed — { type, body?, path? }; omit equals { type: 'null' }
|
|
164
|
+
generic_table_id required UUID|null — required iff resource_type='generic_table'
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Write-only (Request-only).** None — every caller-supplied field
|
|
168
|
+
round-trips on the response.
|
|
169
|
+
|
|
170
|
+
## 4. Error envelopes
|
|
171
|
+
|
|
172
|
+
Standard JSON:API envelopes per `errors.md`. Common rejections:
|
|
173
|
+
|
|
174
|
+
| `source.pointer` | Cause |
|
|
175
|
+
|------------------------------|--------------------------------------------------|
|
|
176
|
+
| `/resource_type` | value not valid for the parent datalake's domain |
|
|
177
|
+
| `/template_config/type` | enum mismatch |
|
|
178
|
+
| `/template_config/body` | empty when `type === 'custom'` |
|
|
179
|
+
| `/mdm_input_config/type` | enum mismatch |
|
|
180
|
+
| `/generic_table_id` | missing when resource_type='generic_table', |
|
|
181
|
+
| | or non-null when not |
|
|
182
|
+
| `/name` | uniqueness within datalake |
|
|
183
|
+
| `errors.base` | "System-created contracts cannot be edited" (PUT) or "...deleted" (DELETE) — see §5 |
|
|
184
|
+
|
|
185
|
+
## 5. Lifecycle
|
|
186
|
+
|
|
187
|
+
### Create
|
|
188
|
+
|
|
189
|
+
Synchronous. The create response is immediately usable — no async
|
|
190
|
+
deployment phase. Templates are compiled lazily at sandbox-run
|
|
191
|
+
time and at activation-client fan-out time, so a `body`
|
|
192
|
+
containing invalid Liquid syntax will surface at first use, not
|
|
193
|
+
at create. Always sandbox-run before binding to a live data
|
|
194
|
+
activation client.
|
|
195
|
+
|
|
196
|
+
### Sandbox run — `.run(slug, msg)`
|
|
197
|
+
|
|
198
|
+
The contract's templates can be exercised against a sample row
|
|
199
|
+
WITHOUT actually ingesting:
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
const { data: result } = await api.interoperabilityContracts.run(
|
|
203
|
+
tenantSlug, datalakeSlug, contractSlug,
|
|
204
|
+
{ /* sample row, same shape as inbound CSV/JSON */ },
|
|
205
|
+
)
|
|
206
|
+
// result.stage === 'completed' | 'failed'
|
|
207
|
+
// result.filter_result === 'pass' | 'skip'
|
|
208
|
+
// result.transformed object | null — the rendered resource body
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Use this verb as a CI / pre-bind verification step: produce a
|
|
212
|
+
representative sample row, run it through `.run`, and assert
|
|
213
|
+
the `transformed` output shape matches the consumer's
|
|
214
|
+
expectations. If `filter_result === 'skip'`, the row was filtered
|
|
215
|
+
out before rendering — useful for verifying the filter semantics
|
|
216
|
+
on a known-skip example.
|
|
217
|
+
|
|
218
|
+
### Generic-table contracts: auto-created identity contracts
|
|
219
|
+
|
|
220
|
+
When you create a generic table (see `generic_tables.md`), the
|
|
221
|
+
platform automatically creates an **identity contract** bound
|
|
222
|
+
to that table:
|
|
223
|
+
|
|
224
|
+
```
|
|
225
|
+
new generic table (T) → auto-created contract with
|
|
226
|
+
resource_type: 'generic_table'
|
|
227
|
+
generic_table_id: T.id
|
|
228
|
+
template_config.type: 'identity'
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Discover this contract via `.list()` + filter on
|
|
232
|
+
`generic_table_id`:
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
const { data: all } = await api.interoperabilityContracts.list(
|
|
236
|
+
tenantSlug, datalakeSlug,
|
|
237
|
+
)
|
|
238
|
+
const identityContract = all.data?.find(
|
|
239
|
+
(c) => c.generic_table_id === tableId && c.type === 'identity',
|
|
240
|
+
)
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Multiple contracts MAY bind to the same generic table — one
|
|
244
|
+
identity (auto) plus any number of caller-created `custom`
|
|
245
|
+
contracts. Distinguish by `type` when iterating. The platform
|
|
246
|
+
enforces at most ONE identity contract per generic table;
|
|
247
|
+
attempts to create a second `type: 'identity'` contract for
|
|
248
|
+
the same table are rejected.
|
|
249
|
+
|
|
250
|
+
### Read shapes
|
|
251
|
+
|
|
252
|
+
```
|
|
253
|
+
.list(tenantSlug, datalakeSlug) Paged: { data, meta }
|
|
254
|
+
.get(tenantSlug, datalakeSlug, slug) One row, by slug
|
|
255
|
+
.metadata(tenantSlug, datalakeSlug, slug) Markdown string — one contract
|
|
256
|
+
.run(tenantSlug, datalakeSlug, slug, msg) Sandbox runner (above)
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Delete
|
|
260
|
+
|
|
261
|
+
`delete()` removes the contract. Contracts referenced by active
|
|
262
|
+
data activation clients reject with a 422 naming the dependency.
|
|
263
|
+
|
|
264
|
+
**System-created contracts cannot be edited or deleted.** The
|
|
265
|
+
contract record carries a `system_created` flag (server-derived).
|
|
266
|
+
When `system_created === true`, both `update()` and `delete()`
|
|
267
|
+
reject with 422 on `/base` and a literal message:
|
|
268
|
+
|
|
269
|
+
```
|
|
270
|
+
PUT → "System-created contracts cannot be edited"
|
|
271
|
+
DELETE → "System-created contracts cannot be deleted"
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
System-created contracts include the auto-generated identity
|
|
275
|
+
contracts the platform provisions when a generic table is
|
|
276
|
+
created (see §5 "Generic-table contracts: auto-created identity
|
|
277
|
+
contracts") and any industry-built templates the platform ships
|
|
278
|
+
with the datalake's data_domain. User-created contracts of any
|
|
279
|
+
`type` (including caller-created `type: 'identity'` rows bound
|
|
280
|
+
to a generic table) ARE editable and deletable — the gate is
|
|
281
|
+
`system_created`, not `type`.
|
|
282
|
+
|
|
283
|
+
The flag IS exposed on the `InteroperabilityContractResponse`,
|
|
284
|
+
so consumer agents can pre-filter before attempting a mutation:
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
const { data: list } = await api.interoperabilityContracts.list(
|
|
288
|
+
tenantSlug, datalakeSlug,
|
|
289
|
+
)
|
|
290
|
+
const editable = list.data.filter((c) => !c.system_created)
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## 6. Nesting AI agents on the contract body
|
|
294
|
+
|
|
295
|
+
A contract can run AI-agent **enrichment** during ingestion. Agents
|
|
296
|
+
are **nested directly on the contract body** — the create/update body
|
|
297
|
+
accepts an `interoperability_contract_ai_agents` array.
|
|
298
|
+
There is no separate attach/detach endpoint, and **no per-join
|
|
299
|
+
checksum**: the nested agents fold into the contract's own checksum.
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
// Nest on create. `position` is REQUIRED.
|
|
303
|
+
const { data: contract } = await api.interoperabilityContracts.create(
|
|
304
|
+
tenantSlug, datalakeSlug,
|
|
305
|
+
{
|
|
306
|
+
name: 'MOA Enrichment Contract',
|
|
307
|
+
resource_type: 'legal_entity',
|
|
308
|
+
template_config: { type: 'custom', body: '{{ name }}' },
|
|
309
|
+
mdm_input_config: { type: 'null' },
|
|
310
|
+
// …other contract fields…
|
|
311
|
+
interoperability_contract_ai_agents: [
|
|
312
|
+
{
|
|
313
|
+
ai_agent_id: createdAgentId,
|
|
314
|
+
position: 0, // ordering when multiple agents fire; REQUIRED
|
|
315
|
+
context_mapping_config: {
|
|
316
|
+
type: 'custom',
|
|
317
|
+
// Liquid renders to a JSON object whose keys match the agent's
|
|
318
|
+
// input_schema; values reference the inbound row via `msg.<field>`.
|
|
319
|
+
body: JSON.stringify({ name: '{{ msg.company_name }}' }),
|
|
320
|
+
// A contract join AUTHORS output_schema — unlike a workflow
|
|
321
|
+
// join, the server does NOT pin it here.
|
|
322
|
+
output_schema: '{"type":"object","properties":{"lead":{"type":"string"}}}',
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
],
|
|
326
|
+
},
|
|
327
|
+
)
|
|
328
|
+
contract.checksum // the PARENT checksum — the nested agents fold into it
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Adding, repositioning, and removing agents are all expressed by
|
|
332
|
+
re-supplying the full set on the contract's `update()` — `update()`
|
|
333
|
+
replaces the whole array, and any item you omit is removed:
|
|
334
|
+
|
|
335
|
+
| Operation | How | Result |
|
|
336
|
+
|------------|--------------------------------------------------|--------|
|
|
337
|
+
| add / nest | include in the array on create or update | contract checksum reflects the agents |
|
|
338
|
+
| reposition | `update()` with the same agent at a new `position`| full-set replace |
|
|
339
|
+
| detach | `update()` omitting that agent (or `[]` to clear all)| the omitted agent is removed |
|
|
340
|
+
| drift | contract `checksum()` endpoint over the full body| the would-be contract checksum (no persist) |
|
|
341
|
+
|
|
342
|
+
A contract `get()` surfaces its nested agents as
|
|
343
|
+
`interoperability_contract_ai_agents: [...]`. The array is writable on
|
|
344
|
+
the contract body (create + update).
|
|
345
|
+
|
|
346
|
+
**Runtime.** Enrichment fires during ingestion,
|
|
347
|
+
**not** on the sandbox `.run`. Attached agents run in `position`
|
|
348
|
+
order: each agent's `context_mapping_config` renders against the row,
|
|
349
|
+
the result is validated against the agent's `input_schema`, sent to
|
|
350
|
+
the bound `llm_enrichment` tool, and the parsed response is merged
|
|
351
|
+
under `msg.enrichment.<agent_slug>` for the contract's
|
|
352
|
+
`template_config` to read. So attaching an agent to a DAC-wired
|
|
353
|
+
contract makes ingestion run enrichment per row — keep agents off
|
|
354
|
+
contracts whose only job is a straight-through upsert.
|
|
355
|
+
|
|
356
|
+
See `ai_agents.md` §3 for the agent create body and the shared attach
|
|
357
|
+
surface, and `workflows.md` for the workflow side of the same join.
|
|
358
|
+
|
|
359
|
+
## 7. Gotchas
|
|
360
|
+
|
|
361
|
+
1. **`filter_template` is empty-passes, non-empty-skips.**
|
|
362
|
+
Already documented in §2 — repeated here because it's the
|
|
363
|
+
#1 source of "ingests nothing / ingests too much" silent
|
|
364
|
+
failures. Always anchor on this rule.
|
|
365
|
+
|
|
366
|
+
2. **`mdm_input_config` is optional; omitting equals `{ type:
|
|
367
|
+
'null' }`.** The shared TemplateConfig embed defaults `type`
|
|
368
|
+
to `'null'`, so a contract that omits `mdm_input_config`
|
|
369
|
+
behaves as "the row IS the subject — no entity resolution
|
|
370
|
+
runs". Declaring `{ type: 'null' }` explicitly is allowed
|
|
371
|
+
for readability but not required.
|
|
372
|
+
|
|
373
|
+
3. **`resource_type` is industry-scoped.** A datalake with
|
|
374
|
+
`data_domain: 'foundation'` accepts `'legal_entity'` but
|
|
375
|
+
rejects `'patient'`. The error surfaces as
|
|
376
|
+
`/resource_type` 422 at create time. Don't ship a fixed
|
|
377
|
+
enum across industries.
|
|
378
|
+
|
|
379
|
+
4. **Sandbox-run is not optional in practice.** A template that
|
|
380
|
+
compiles but renders structurally-invalid JSON, or that
|
|
381
|
+
silently drops fields, won't surface until live ingestion —
|
|
382
|
+
and at that point the failures are batched and harder to
|
|
383
|
+
debug. Run `.run(slug, sampleRow)` against a representative
|
|
384
|
+
row for every new contract before binding it to a data
|
|
385
|
+
activation client.
|
|
386
|
+
|
|
387
|
+
5. **Generic-table identity contracts are auto-created; don't
|
|
388
|
+
create them yourself.** Creating a generic table fires a
|
|
389
|
+
side-effect that creates a `type: 'identity'` contract bound
|
|
390
|
+
to the new table. Creating a second identity contract for the
|
|
391
|
+
same table is rejected. To add custom transformation on top
|
|
392
|
+
of a generic table, create an additional `type: 'custom'`
|
|
393
|
+
contract bound to the same `generic_table_id`.
|
|
394
|
+
|
|
395
|
+
6. **Templates use `msg` to access the row.** Liquid receives
|
|
396
|
+
the row under the `msg` variable. CSV columns are typically
|
|
397
|
+
`msg.<column>`; nested cases use `msg.row.<column>`. JSON
|
|
398
|
+
sources expose the inbound fields under `msg.<field>`. Read
|
|
399
|
+
any platform-shipped template (see `templates.md`) to learn
|
|
400
|
+
the exact convention for your row shape.
|
|
401
|
+
|
|
402
|
+
7. **No partial updates.** `update()` replaces the whole resource
|
|
403
|
+
— resupply the full body (see `mutations.md`). Re-supply
|
|
404
|
+
`template_config` / `mdm_input_config` verbatim if not
|
|
405
|
+
changing them.
|
|
406
|
+
|
|
407
|
+
8. **Custom Liquid filters are a closed allowlist of eleven.**
|
|
408
|
+
The platform exposes exactly eleven custom filters beyond
|
|
409
|
+
stock Liquid (`e164`, `age`, `date`, `to_json`,
|
|
410
|
+
`json_escape`, `now`, `uuid`, `parse_date`, `convert_time`,
|
|
411
|
+
`random`, `tz_offset`). Other filter names — including the
|
|
412
|
+
Shopify-style `| json` — fall through to stock Liquid and
|
|
413
|
+
produce malformed output silently. See `ai_sandbox.md` §2
|
|
414
|
+
for the full per-filter reference and the safety rationale
|
|
415
|
+
for the closed set. The allowlist is the same in
|
|
416
|
+
`template_config.body`, `mdm_input_config.body`, and
|
|
417
|
+
`filter_template`.
|