@alvera-ai/platform-sdk 0.21.0 → 0.21.1-next.g7fffc89

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 CHANGED
@@ -327,7 +327,10 @@ from the filesystem at
327
327
  is no SDK function and no CLI verb that returns cookbook bytes
328
328
  at runtime.
329
329
 
330
- Each cookbook structure:
330
+ Each cookbook structure — front matter, then these top-level `#`
331
+ sections, each exactly once and in this order. The validator refuses a
332
+ cookbook that is missing one, repeats one, reorders them, leaves one
333
+ empty, or adds a top-level section that is not on this list:
331
334
 
332
335
  - **Front matter** — `title`, `summary`, `use_case`, `slug`,
333
336
  `vitest_source` (list of integration-test files the snippets
@@ -349,14 +352,17 @@ Each cookbook structure:
349
352
  carrying one fenced TypeScript block lifted from the anchor or
350
353
  ancillary vitest. State threads forward across subsections via
351
354
  plain JavaScript variables in the validator's generated
352
- `describe` closure.
353
- - **Branches** — alternate paths the anchor vitest covers (a
354
- filter rejection, a transport failure, an agent-classification
355
- fallback).
356
- - **Rollback** — teardown order for the resources the cookbook
357
- provisioned.
355
+ `describe` closure. A numbered step anywhere else in the file
356
+ is refused.
358
357
  - **Outcome** — author's prose summary of what the scenario
359
358
  produces.
359
+ - **Gotchas** — one numbered list of the mistakes the walk exists
360
+ to keep you from making. Each item opens with a bold one-sentence
361
+ rule, says why it bites, and names the step that proves it as
362
+ `§NNN`. The validator refuses an item that names no step, or a
363
+ step the cookbook does not have — and every step runs against a
364
+ live platform — so a gotcha cannot outlive the behaviour it warns
365
+ about.
360
366
  - **See also** — links to relevant per-resource reference MDs
361
367
  and to the anchor + ancillary vitest files.
362
368
 
@@ -3077,12 +3077,66 @@ if (afterSubjects.size !== 1 || !afterSubjects.has(twoIdSubject)) {
3077
3077
 
3078
3078
  # Outcome
3079
3079
 
3080
- The marketing tenant exists with one raw datalake, an SMS tool, an LLM
3081
- tool, a lead-submissions table, and an agent-driven workflow that bands
3082
- every inbound lead and fans out to exactly one of four SMS actions.
3080
+ After all forty-seven steps run green, on one tenant and **one raw
3081
+ datalake**:
3082
+
3083
+ - **Every inbound lead is banded.** Three leads land in the Lead Submissions
3084
+ table through the client the platform provisioned with it, and the Score
3085
+ Lead agent's band steers each one to exactly one of four SMS actions
3086
+ (§014–§018).
3087
+ - **A birthday greeting is scheduled a year out.** Two contacts arrive through
3088
+ the lead-form client; the Happy Birthday workflow's pure-Liquid trigger
3089
+ schedules a greeting for the one with a birthday and filters the one
3090
+ without. Fast-forwarded, the greeting carries an authenticated link whose
3091
+ exchange names that contact, and the tap, the page and the form are tracked
3092
+ on the message (§020–§030).
3093
+ - **A campaign goes out A/B on two channels.** One business and four
3094
+ recipients land in the roster and audience tables through contract pairs on
3095
+ separate clients, identities first. The campaign workflow's gates decide who
3096
+ is contacted at all, and its split sends each of them one variant on both
3097
+ SMS and email, every message carrying its own link to the booking page
3098
+ (§031–§040).
3099
+ - **A reply attaches to whoever sent it.** It ingests into the system inbound
3100
+ table and resolves back to the recipient's subject without creating a new
3101
+ one (§041).
3102
+ - **Delivery is reconciled, not assumed.** An action status updater turns
3103
+ provider delivery events into updates on a schedule, its stored body is
3104
+ read back against its checksum, and a poller whose request could never move
3105
+ to the next page is refused at create (§042–§044).
3106
+ - **The lake answers in plain language.** Text-to-SQL proposes a query
3107
+ without running it; the read-only executor runs one and returns the same
3108
+ page as CSV (§045–§046).
3109
+ - **One person carries two identifiers.** A second ingest that names only the
3110
+ email leaves the phone slot untouched on the same subject (§047).
3083
3111
 
3084
3112
  Running this walk again reuses all of it.
3085
3113
 
3114
+ # Gotchas
3115
+
3116
+ 1. **`link_prefix` is required on every SMS and email call, even one whose
3117
+ body carries no link of its own.** Any URL in the body — including one a
3118
+ lead typed into the form — goes out as `<link_prefix>/<token>`. Leave the
3119
+ prefix off and the workflow is refused at create with a 422 on
3120
+ `/actions/<n>/tool_call/link_prefix` — §012, §038.
3121
+ 2. **`workflows.run` only schedules a run.** The ids a scenario needs are
3122
+ written later, when the run fires, so poll until `workflow_run_log_id` is
3123
+ a string, not until `status` leaves `'scheduled'`. And a run whose action
3124
+ is scheduled for a future date never reaches a terminal status at all:
3125
+ wait for every row's execution log instead — §007, §025.
3126
+ 3. **Gate on `dataset_updated`, never on `rows_ingested`.** They answer
3127
+ different questions — received versus written — and a batch whose row is
3128
+ refused reports `rows_ingested: 1, dataset_updated: 0, status: 'partial'`.
3129
+ A gate on `rows_ingested` calls that green, and the failure surfaces three
3130
+ steps later as a workflow that seems broken — §007, §015.
3131
+ 4. **On a repeat run every action is skipped, and that is correct.** The
3132
+ idempotency key that already fired will not fire again, so read the
3133
+ `message` row the lake keeps, not the execution log of the run you just
3134
+ made — on run two that log carries no body at all — §017, §029.
3135
+ 5. **`skip_mdm_resolution: false` is set explicitly, and must be.** A
3136
+ generic-table row is never its own subject. With resolution on, the
3137
+ message and every link minted with it name the stamped subject as their
3138
+ `legal_entity_id`; skip it and the subject is nil — §038.
3139
+
3086
3140
  # See also
3087
3141
 
3088
3142
  - `datalakes.md` — the create body, and what a derived pair would add
@@ -2131,7 +2131,7 @@ truncate a table with an inbound foreign key, regardless of whether the
2131
2131
  referencing tables hold any rows, so repairing a parent empties its whole
2132
2132
  dependent closure, transitively, in a single statement — and all of them are
2133
2133
  re-copied by the same run. `legal_entities` is the target here on purpose:
2134
- §029's contract pair resolved every screened subject through it, and
2134
+ §019's contract pair resolved every screened subject through it, and
2135
2135
  `legal_entity_identifications` points at it, so `emptied` genuinely names more
2136
2136
  than one table. Any surface offering this button should read `resync_empties`
2137
2137
  and say the count before it is pressed.
@@ -2208,15 +2208,34 @@ pairs split across four Data Activation Clients, and two agentic workflows
2208
2208
  that between them screen, decide, notify and track — plus the copy's own
2209
2209
  arrival state, read back per table and repaired.
2210
2210
 
2211
- Two things are worth carrying out of this walk. **The pair must not run
2212
- concurrently** (§020) — one writer per subject, or the unique index that
2213
- guards against duplicate entities refuses your row on the only run that
2214
- matters, the first. And **assert end states, not transitions** (§024) — an
2215
- idempotency key that has already fired will not fire again, which is the
2216
- product working, not the walk failing.
2217
-
2218
2211
  Running this walk again reuses all of it.
2219
2212
 
2213
+ # Gotchas
2214
+
2215
+ 1. **The pair must not run concurrently.** One writer per subject, or the
2216
+ unique index that guards against duplicate entities refuses your row on
2217
+ the only run that matters, the first — §020.
2218
+ 2. **Assert end states, not transitions.** An idempotency key that has
2219
+ already fired will not fire again, which is the product working, not the
2220
+ walk failing — §024.
2221
+ 3. **Gate on `dataset_updated`, never on `rows_ingested`.** They answer
2222
+ different questions — received versus written — and a batch whose row is
2223
+ refused reports `rows_ingested: 1, dataset_updated: 0, status: 'partial'`.
2224
+ A gate on `rows_ingested` calls that green, and the failure surfaces three
2225
+ steps later as a workflow that seems broken — §008, §022.
2226
+ 4. **The system-template catalog is paginated, and page 1 is not the
2227
+ catalog.** The template you want is as likely to be on page 3; reading
2228
+ page 1 and concluding a template does not exist is the trap — §014.
2229
+ 5. **The identifier shape is where the contract pair goes wrong, and there
2230
+ are three of them.** A contract's `mdm_input_config` takes `uri` / `type`
2231
+ / `value`; a LegalEntity identification takes `id_type` / `id_number` /
2232
+ `uri`; `POST /mdm/verify` takes `system` / `id_type` / `value` — §019.
2233
+ 6. **A repair empties more than the table you name, and its `202` means
2234
+ started.** Postgres will not truncate a table with an inbound foreign key,
2235
+ so repairing a parent empties its whole dependent closure; there is no
2236
+ completion callback, so poll until every emptied table reads `caught_up`
2237
+ again — §039.
2238
+
2220
2239
  # See also
2221
2240
 
2222
2241
  - `datalakes.md` — the create body, and what the derived pair is for
@@ -2194,30 +2194,30 @@ After all thirty-one steps run green, on one tenant and **three datalakes**:
2194
2194
  read back, one table emptied and copied again, and every table the repair
2195
2195
  took with it waited back to `caught_up`.
2196
2196
 
2197
- What this surface teaches that the raw cookbooks cannot:
2197
+ # Gotchas
2198
2198
 
2199
2199
  1. **A masked jsonb column is `jsonb` in raw and `text` in both copies, and
2200
2200
  the types are supposed to differ.** Logical replication ships a text
2201
2201
  rendering and feeds it to the subscriber's input function, so
2202
2202
  `jsonb → text` always works and `jsonb → text[]` never does. The
2203
2203
  invariant is scalar-ness, not type parity — and `is_array: true`
2204
- describes the JSON document, not the Postgres column.
2204
+ describes the JSON document, not the Postgres column — §016.
2205
2205
  2. **The tier follows what the prompt can reach.** The triage agent here runs
2206
2206
  `tokenized` because it reads patient messages; the equivalent agent in
2207
2207
  `subscription-saas` runs `raw` because its prompt carries an account
2208
- number and nothing else. Neither is a house style.
2208
+ number and nothing else. Neither is a house style — §021.
2209
2209
  3. **The two copies are different from each other on purpose.** `tok_<hash>`
2210
2210
  is a stable join key for a machine; `xxxx-<tail>` is an unjoinable
2211
- placeholder for a person.
2211
+ placeholder for a person — §029.
2212
2212
  4. **A key the contract does not read is not an error.** It renders nothing,
2213
2213
  the column lands `null`, the batch reports green, and the failure appears
2214
2214
  two steps later as an action rejecting an empty recipient. Assert columns
2215
- on the row.
2215
+ on the row — §015.
2216
2216
  5. **"The copy exists" and "the copy is complete" are different questions.**
2217
2217
  `finished_copy` has not finished — read `caught_up`, not the state's name.
2218
2218
  And a repair is not scoped to the table you name: Postgres will not
2219
2219
  truncate a table anything references, so the dependent closure goes with
2220
- it. Both are why `emptied` is returned rather than assumed.
2220
+ it. Both are why `emptied` is returned rather than assumed — §030, §031.
2221
2221
 
2222
2222
  # See also
2223
2223
 
@@ -2405,7 +2405,7 @@ After all forty-one steps run green, on one tenant and **one raw datalake**:
2405
2405
  - **A second person** is on the tenant with a `member` role, invited and
2406
2406
  accepted entirely through the SDK.
2407
2407
 
2408
- Four things this walk teaches that are easy to learn the expensive way:
2408
+ # Gotchas
2409
2409
 
2410
2410
  1. **Never bind a contract pair to one activation client.** The fan-out
2411
2411
  staggers by row, so a pair on one client resolves the same subject twice
@@ -2413,10 +2413,10 @@ Four things this walk teaches that are easy to learn the expensive way:
2413
2413
  sequence them — §012, and again at §030 and §033.
2414
2414
  2. **Gate on `dataset_updated`, not `rows_ingested`.** Received is not
2415
2415
  written. A refused row reports the first and not the second, and a walk
2416
- gated on the wrong one goes green over an empty table.
2416
+ gated on the wrong one goes green over an empty table — §007, §013.
2417
2417
  3. **Search the idempotency key, never the workflow id.** The key is built
2418
2418
  from a stable business id and names the same message forever; a workflow
2419
- id changes the moment the resource is recreated.
2419
+ id changes the moment the resource is recreated — §019, §023.
2420
2420
  4. **Assert the end state, not the transition.** An idempotency key fires
2421
2421
  once ever, so every assertion here accepts both the first run's shape and
2422
2422
  the repeat run's — §027 spells that out, and §040 defers its proof to
package/dist/index.d.mts CHANGED
@@ -679,7 +679,7 @@ type ActionStatusUpdaterResponse = {
679
679
  */
680
680
  readonly last_run_at?: string | null;
681
681
  /**
682
- * Why the last poll run failed, or why a `partial` run was truncated; null when the run completed and read its whole window
682
+ * Why the last poll run failed; null when the run completed and read its whole window
683
683
  */
684
684
  readonly last_run_error?: string | null;
685
685
  /**
@@ -687,9 +687,9 @@ type ActionStatusUpdaterResponse = {
687
687
  */
688
688
  readonly last_run_events_found?: number | null;
689
689
  /**
690
- * Outcome of the last poll run. `partial` means the run completed but its provider fetch was truncated, so the window was not fully read and the newest events may be missing — read `last_run_error` for detail.
690
+ * Outcome of the last poll run: `ok` when the run read its whole window, `error` when it stopped — read `last_run_error` for why. A run is never partial: every page of the window is read, one job per page.
691
691
  */
692
- readonly last_run_status?: 'ok' | 'partial' | 'error';
692
+ readonly last_run_status?: 'ok' | 'error';
693
693
  message_config: SimpleTemplateConfigResponse;
694
694
  /**
695
695
  * Updater name
@@ -990,6 +990,7 @@ declare enum ActionType {
990
990
  EMAIL = "email",
991
991
  VOICE = "voice",
992
992
  DATA_EXCHANGE = "data_exchange",
993
+ DATA_ACTIVATION = "data_activation",
993
994
  }
994
995
  /**
995
996
  * MDMVerifyRequest
@@ -1185,11 +1186,13 @@ type ActionResponse = {
1185
1186
  tool_call_type: 'aws_lambda_request';
1186
1187
  } & ActionAwsLambdaCallResponse) | ({
1187
1188
  tool_call_type: 'manual_upload';
1188
- } & ActionManualUploadCallResponse);
1189
+ } & ActionManualUploadCallResponse) | ({
1190
+ tool_call_type: 'run_data_activation_client';
1191
+ } & ActionRunDataActivationClientCallResponse);
1189
1192
  /**
1190
- * Tool that executes this action
1193
+ * Tool that executes this action. Required for every action type except data_activation, which refuses one: its client carries its own tool.
1191
1194
  */
1192
- tool_id: string;
1195
+ tool_id?: string | null;
1193
1196
  /**
1194
1197
  * Liquid template that determines when this action executes
1195
1198
  */
@@ -1663,6 +1666,21 @@ type SystemTemplateListResponse = {
1663
1666
  type DataActivationClientS3CallResponse = S3CallResponse & {
1664
1667
  tool_call_type: 's3_request';
1665
1668
  };
1669
+ /**
1670
+ * RunDataActivationClientCallRequest
1671
+ *
1672
+ * Runs a data activation client for the row that started the workflow, with the tool call the template renders for that row in place of the client's stored one. Request
1673
+ */
1674
+ type RunDataActivationClientCallRequest = {
1675
+ /**
1676
+ * Slug of the data activation client to run, in the workflow's datalake
1677
+ */
1678
+ client_slug: string;
1679
+ /**
1680
+ * Liquid template rendered against the row that started the workflow, to the client's whole tool call as JSON — the shape the run-manually endpoint takes. It replaces the client's stored tool call for this run only.
1681
+ */
1682
+ tool_call_override_template: string;
1683
+ };
1666
1684
  /**
1667
1685
  * EmailCallResponse
1668
1686
  *
@@ -2373,6 +2391,12 @@ type AuthenticatedLinkUpdateMessageTrackingRequest = {
2373
2391
  type DataActivationClientSftpCallResponse = SftpCallResponse & {
2374
2392
  tool_call_type: 'sftp_request';
2375
2393
  };
2394
+ /**
2395
+ * ActionRunDataActivationClientCallResponse
2396
+ */
2397
+ type ActionRunDataActivationClientCallResponse = RunDataActivationClientCallResponse & {
2398
+ tool_call_type: 'run_data_activation_client';
2399
+ };
2376
2400
  /**
2377
2401
  * DataSourceRequest
2378
2402
  *
@@ -2615,6 +2639,21 @@ type AlveraApiError = {
2615
2639
  */
2616
2640
  mode?: string | null;
2617
2641
  };
2642
+ /**
2643
+ * RunDataActivationClientCallResponse
2644
+ *
2645
+ * Runs a data activation client for the row that started the workflow, with the tool call the template renders for that row in place of the client's stored one.
2646
+ */
2647
+ type RunDataActivationClientCallResponse = {
2648
+ /**
2649
+ * Slug of the data activation client to run, in the workflow's datalake
2650
+ */
2651
+ client_slug: string;
2652
+ /**
2653
+ * Liquid template rendered against the row that started the workflow, to the client's whole tool call as JSON — the shape the run-manually endpoint takes. It replaces the client's stored tool call for this run only.
2654
+ */
2655
+ tool_call_override_template: string;
2656
+ };
2618
2657
  /**
2619
2658
  * CloudStorageAwsResponse
2620
2659
  *
@@ -3438,7 +3477,7 @@ type EmailResponse = {
3438
3477
  */
3439
3478
  domain?: string;
3440
3479
  /**
3441
- * Custom Mailgun API base URL (e.g., https://api.eu.mailgun.net/v3 for EU domains, or a WireMock endpoint for integration tests); leave blank for real Mailgun
3480
+ * Custom provider endpoint; leave blank for the real provider. For SES, the whole request URL (e.g., http://localhost:4566 for MiniStack); blank means https://email.<region>.amazonaws.com. For Mailgun, the API base URL (e.g., https://api.eu.mailgun.net/v3 for EU domains, or a WireMock endpoint for integration tests)
3442
3481
  */
3443
3482
  endpoint_url?: string | null;
3444
3483
  /**
@@ -4973,11 +5012,13 @@ type ActionRequestWritable = {
4973
5012
  tool_call_type: 'aws_lambda_request';
4974
5013
  } & ActionAwsLambdaCallRequestWritable) | ({
4975
5014
  tool_call_type: 'manual_upload';
4976
- } & ActionManualUploadCallRequest);
5015
+ } & ActionManualUploadCallRequest) | ({
5016
+ tool_call_type: 'run_data_activation_client';
5017
+ } & ActionRunDataActivationClientCallRequestWritable);
4977
5018
  /**
4978
- * Tool that executes this action
5019
+ * Tool that executes this action. Required for every action type except data_activation, which refuses one: its client carries its own tool.
4979
5020
  */
4980
- tool_id: string;
5021
+ tool_id?: string | null;
4981
5022
  /**
4982
5023
  * Liquid template that determines when this action executes
4983
5024
  */
@@ -5098,7 +5139,7 @@ type EmailRequestWritable = {
5098
5139
  */
5099
5140
  domain?: string;
5100
5141
  /**
5101
- * Custom Mailgun API base URL (e.g., https://api.eu.mailgun.net/v3 for EU domains, or a WireMock endpoint for integration tests); leave blank for real Mailgun
5142
+ * Custom provider endpoint; leave blank for the real provider. For SES, the whole request URL (e.g., http://localhost:4566 for MiniStack); blank means https://email.<region>.amazonaws.com. For Mailgun, the API base URL (e.g., https://api.eu.mailgun.net/v3 for EU domains, or a WireMock endpoint for integration tests)
5102
5143
  */
5103
5144
  endpoint_url?: string | null;
5104
5145
  /**
@@ -5794,6 +5835,12 @@ type SftpRequestWritable = {
5794
5835
  */
5795
5836
  user_name: string;
5796
5837
  };
5838
+ /**
5839
+ * ActionRunDataActivationClientCallRequest
5840
+ */
5841
+ type ActionRunDataActivationClientCallRequestWritable = RunDataActivationClientCallRequest & {
5842
+ tool_call_type: 'run_data_activation_client';
5843
+ };
5797
5844
  /**
5798
5845
  * CloudStorageCustomRequest
5799
5846
  *