@alvera-ai/platform-sdk 0.17.0 → 0.18.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 (83) hide show
  1. package/.agent/AGENTS.md +82 -144
  2. package/.agent/account_management.md +2 -2
  3. package/.agent/action_logs.md +4 -4
  4. package/.agent/ai_agents.md +28 -21
  5. package/.agent/ai_sandbox.md +49 -39
  6. package/.agent/connected_apps.md +3 -3
  7. package/.agent/cookbook/_fixtures/README.md +1 -1
  8. package/.agent/cookbook/_fixtures/{foundation → organic-marketing}/_lead_submissions_foundation_generic_table.liquid +1 -1
  9. package/.agent/cookbook/_fixtures/organic-marketing/_lead_submissions_foundation_legal_entity.liquid +80 -0
  10. package/.agent/cookbook/_fixtures/{foundation → organic-marketing}/_lead_submissions_foundation_mdm.liquid +2 -1
  11. package/.agent/cookbook/_fixtures/payments-compliance/_compliance_screenings_generic_table.liquid +57 -0
  12. package/.agent/cookbook/_fixtures/payments-compliance/_compliance_screenings_legal_entity.liquid +30 -0
  13. package/.agent/cookbook/_fixtures/payments-compliance/_compliance_screenings_mdm.liquid +44 -0
  14. package/.agent/cookbook/_fixtures/payments-compliance/_payment_accounts_generic_table.liquid +57 -0
  15. package/.agent/cookbook/_fixtures/payments-compliance/_payment_accounts_legal_entity.liquid +36 -0
  16. package/.agent/cookbook/_fixtures/payments-compliance/_payment_accounts_mdm.liquid +41 -0
  17. package/.agent/cookbook/_fixtures/primary-care-feedback/_cahps_appointments_generic_table.liquid +70 -0
  18. package/.agent/cookbook/_fixtures/primary-care-feedback/_cahps_appointments_legal_entity.liquid +52 -0
  19. package/.agent/cookbook/_fixtures/primary-care-feedback/_cahps_appointments_mdm.liquid +42 -0
  20. package/.agent/cookbook/_fixtures/subscription-saas/_customers_subscription_generic_table.liquid +38 -0
  21. package/.agent/cookbook/_fixtures/subscription-saas/_customers_subscription_legal_entity.liquid +48 -0
  22. package/.agent/cookbook/_fixtures/subscription-saas/_customers_subscription_mdm.liquid +49 -0
  23. package/.agent/cookbook/organic-marketing.md +2801 -0
  24. package/.agent/cookbook/payments-compliance.md +2180 -0
  25. package/.agent/cookbook/primary-care.md +2175 -0
  26. package/.agent/cookbook/subscription-saas.md +2403 -0
  27. package/.agent/data_activation_clients.md +65 -52
  28. package/.agent/datalakes.md +338 -171
  29. package/.agent/errors.md +3 -3
  30. package/.agent/generic_tables.md +151 -62
  31. package/.agent/interoperability_contracts.md +57 -22
  32. package/.agent/mdm.md +136 -153
  33. package/.agent/messages.md +36 -34
  34. package/.agent/mock-services.md +1 -1
  35. package/.agent/mutations.md +2 -2
  36. package/.agent/templates.md +14 -13
  37. package/.agent/tools.md +63 -21
  38. package/.agent/type_naming.md +13 -13
  39. package/.agent/workflows.md +99 -53
  40. package/README.md +2 -2
  41. package/dist/bin/platform-sdk.mjs +33 -47
  42. package/dist/bin/platform-sdk.mjs.map +1 -1
  43. package/dist/index.d.mts +565 -379
  44. package/dist/index.d.mts.map +1 -1
  45. package/dist/index.mjs +494 -59
  46. package/dist/index.mjs.map +1 -1
  47. package/package.json +4 -3
  48. package/.agent/cookbook/_fixtures/foundation/_lead_submissions_foundation_legal_entity.liquid +0 -88
  49. package/.agent/cookbook/_fixtures/healthcare/_cahps_appointments_healthcare_appointment.liquid +0 -47
  50. package/.agent/cookbook/_fixtures/healthcare/_cahps_appointments_healthcare_mdm.liquid +0 -24
  51. package/.agent/cookbook/_fixtures/healthcare/_cahps_appointments_healthcare_patient.liquid +0 -38
  52. package/.agent/cookbook/_fixtures/payments/_compliance_screenings_payments_compliance_screening.liquid +0 -59
  53. package/.agent/cookbook/_fixtures/payments/_compliance_screenings_payments_mdm.liquid +0 -36
  54. package/.agent/cookbook/_fixtures/payments/_payment_accounts_payments_mdm.liquid +0 -30
  55. package/.agent/cookbook/_fixtures/payments/_payment_accounts_payments_payment_account.liquid +0 -55
  56. package/.agent/cookbook/_fixtures/subscription/_customers_subscription_mdm.liquid +0 -20
  57. package/.agent/cookbook/_setup/foundation.md +0 -359
  58. package/.agent/cookbook/_setup/healthcare.md +0 -361
  59. package/.agent/cookbook/_setup/payments.md +0 -365
  60. package/.agent/cookbook/_setup/subscription.md +0 -364
  61. package/.agent/cookbook/action-status-updaters.md +0 -278
  62. package/.agent/cookbook/ai-agent-invoke.md +0 -279
  63. package/.agent/cookbook/appointment-review-sms-workflow.md +0 -801
  64. package/.agent/cookbook/birthday-greeting-sms-trigger.md +0 -696
  65. package/.agent/cookbook/bulk-ingest.md +0 -302
  66. package/.agent/cookbook/contact-us-triage-with-llm.md +0 -663
  67. package/.agent/cookbook/dunning-sms-for-delinquent.md +0 -659
  68. package/.agent/cookbook/generic-tables.md +0 -244
  69. package/.agent/cookbook/invite-team.md +0 -200
  70. package/.agent/cookbook/kyc-notification-on-account-activation.md +0 -661
  71. package/.agent/cookbook/marketing-campaign-send.md +0 -1044
  72. package/.agent/cookbook/paginated-restapi-poller.md +0 -383
  73. package/.agent/cookbook/rest-fetch.md +0 -273
  74. package/.agent/cookbook/sanctions-screening-with-agent-review.md +0 -773
  75. package/.agent/cookbook/score-leads-with-llm-categorization.md +0 -665
  76. package/.agent/cookbook/system-templates.md +0 -165
  77. package/.agent/cookbook/talk-to-data.md +0 -178
  78. package/.agent/cookbook/triage-prospects-by-priority.md +0 -571
  79. package/.agent/cookbook/welcome-sms-for-customers.md +0 -647
  80. /package/.agent/cookbook/_fixtures/{healthcare → primary-care-feedback}/memorandum-of-association-01.png +0 -0
  81. /package/.agent/cookbook/_fixtures/{healthcare → primary-care-feedback}/sample_two_page.pdf +0 -0
  82. /package/.agent/cookbook/_fixtures/{subscription → subscription-saas}/_customers_subscription_customer.liquid +0 -0
  83. /package/.agent/cookbook/_fixtures/{subscription → subscription-saas}/stripe_customers_batch1.csv +0 -0
@@ -1,165 +0,0 @@
1
- ---
2
- title: "Capability: discover the platform's built-in row-mapping templates"
3
- summary: A capability walk for system-template discovery. List the Liquid templates the platform ships for your datalake's data domain (`api.templates.systemTemplates`), read the human-readable catalog (`api.templates.metadata`), and pull one template's detail (`api.templates.metadataDetails`) — so you can start an interoperability contract from a platform template instead of hand-writing Liquid. Read-only; creates nothing.
4
- industry: subscription
5
- slug: system-templates
6
- vitest_source:
7
- - integration-tests/tests/subscription/system-templates.test.ts
8
- - integration-tests/tests/subscription/bootstrap.test.ts
9
- status: green
10
- ---
11
-
12
- # Capability
13
-
14
- **What you get:** the list of row-mapping Liquid templates the platform already
15
- ships for your datalake's data domain, so an interoperability contract can start
16
- from a vetted template instead of hand-written Liquid.
17
-
18
- `api.templates.*` is read-only discovery. Three calls:
19
-
20
- - `systemTemplates(tenantSlug, datalakeSlug)` — the machine-readable list:
21
- `{ data: [{ path, content, output_schema }] }`. The list is **filtered to your
22
- datalake's data domain** plus globals — a payments or healthcare template
23
- never appears in an subscription datalake.
24
- - `metadata(tenantSlug, datalakeSlug)` — the same catalog as a markdown summary.
25
- - `metadataDetails(tenantSlug, datalakeSlug, basename, intent)` — one template's
26
- detail by basename + intent.
27
-
28
- This is **global** to every data domain — only the anchor template paths below
29
- differ by domain. See `templates.md` for the reference, and
30
- `interoperability_contracts.md` for how a template becomes a contract.
31
-
32
- # Walkthrough
33
-
34
- The `_setup/subscription.md` bootstrap already provisioned the tenant +
35
- datalake and left `api`, `tenantSlug`, and `datalakeSlug` populated. This walk
36
- starts from there.
37
-
38
- ## 001 — list the system templates in scope for this datalake
39
-
40
- `systemTemplates` returns every platform-shipped Liquid template visible to this
41
- datalake: the templates for its data domain (here, `subscription`) plus
42
- domain-agnostic globals (e.g. the SNS delivery-status poller). Each entry has a
43
- `path`, the Liquid `content`, and an `output_schema` (`null` when the template
44
- has no companion schema). The list is domain-scoped — templates for other data
45
- domains (`healthcare`, `payments`, `foundation`, …) are filtered out, so you
46
- only see what is usable here.
47
-
48
- ```typescript
49
- const { data: templates } = await api.templates.systemTemplates(tenantSlug, datalakeSlug)
50
-
51
- const paths = (templates.data ?? []).map((t) => t.path)
52
- if (paths.length === 0) {
53
- throw new Error('expected the platform to ship at least one system template')
54
- }
55
-
56
- // The subscription anchor template is present...
57
- const arAnchor = 'data_activation/interoperability/subscription/stripe/customers_subscription_customer'
58
- if (!paths.some((p) => p.includes(arAnchor))) {
59
- throw new Error(`expected the AR customer template (${arAnchor}) in the list`)
60
- }
61
-
62
- // ...and templates for other data domains are filtered out of an AR datalake.
63
- const otherDomains = ['healthcare', 'payments', 'foundation', 'core_banking', 'service_commerce', 'trading']
64
- const leaked = paths.filter((p) => otherDomains.some((d) => p.split('/').includes(d)))
65
- if (leaked.length > 0) {
66
- throw new Error(`cross-domain templates leaked into an AR datalake: ${leaked.join(', ')}`)
67
- }
68
- ```
69
-
70
- ## 002 — read the catalog as markdown
71
-
72
- `metadata` returns the same catalog as a markdown document — the form an agent
73
- reads to decide which template to start from. It names the in-scope templates,
74
- including the AR customer template by basename.
75
-
76
- ```typescript
77
- const { data: catalog } = await api.templates.metadata(tenantSlug, datalakeSlug)
78
- if (typeof catalog !== 'string' || catalog.length === 0) {
79
- throw new Error('expected a non-empty markdown template catalog')
80
- }
81
- if (!catalog.includes('customers_subscription_customer')) {
82
- throw new Error('expected the AR customer template named in the catalog markdown')
83
- }
84
- ```
85
-
86
- ## 003 — read one template's detail by basename + intent
87
-
88
- `metadataDetails` pulls a single template's detail. Address it by its
89
- **basename** (the template name without the path) and its **intent** (the path
90
- prefix joined with underscores — here `data_activation_interoperability`). The
91
- result is a markdown body you can show an operator or feed an agent before it
92
- copies the template into a contract's `template_config`.
93
-
94
- ```typescript
95
- const { data: detail } = await api.templates.metadataDetails(
96
- tenantSlug,
97
- datalakeSlug,
98
- 'customers_subscription_customer',
99
- 'data_activation_interoperability',
100
- )
101
- if (typeof detail !== 'string' || detail.length === 0) {
102
- throw new Error('expected a non-empty markdown detail for the AR customer template')
103
- }
104
- ```
105
-
106
- ## 004 — write the integration test
107
-
108
- End the walk with a test you keep: anything that references a system
109
- template by path (`template_config: { type: 'system', path: … }`) depends
110
- on that path staying in this datalake's scope — so the durable test
111
- re-asserts the anchor is listed, the domain filter holds, and the detail
112
- surface renders. This block runs live under `make validate-cookbook`.
113
-
114
- ```typescript
115
- const { data: tplList } = await api.templates.systemTemplates(tenantSlug, datalakeSlug)
116
- const tplPaths = (tplList.data ?? []).map((t) => t.path)
117
- const anchor = 'data_activation/interoperability/subscription/stripe/customers_subscription_customer'
118
- if (!tplPaths.some((p) => p.includes(anchor))) {
119
- throw new Error(`anchor template ${anchor} vanished from the domain-scoped list`)
120
- }
121
- const foreign = ['healthcare', 'payments', 'foundation', 'core_banking', 'service_commerce', 'trading']
122
- const leakedPaths = tplPaths.filter((p) => foreign.some((d) => p.split('/').includes(d)))
123
- if (leakedPaths.length > 0) {
124
- throw new Error(`cross-domain templates leaked: ${leakedPaths.join(', ')}`)
125
- }
126
- // Behavioural probe — the detail surface must render for the anchor.
127
- const { data: anchorDetail } = await api.templates.metadataDetails(
128
- tenantSlug,
129
- datalakeSlug,
130
- 'customers_subscription_customer',
131
- 'data_activation_interoperability',
132
- )
133
- if (typeof anchorDetail !== 'string' || anchorDetail.length === 0) {
134
- throw new Error('anchor template metadataDetails came back empty')
135
- }
136
- ```
137
-
138
- If the anchor disappears in production, escalate with the listing
139
- evidence — a `type: system` reference that stops resolving is a platform
140
- regression, not something to paper over by inlining the template body.
141
-
142
- # Gotchas
143
-
144
- - **The list is domain-scoped, not global.** `systemTemplates` only returns
145
- templates for this datalake's data domain plus globals. Calling it on a
146
- healthcare datalake returns the healthcare templates, never the AR ones —
147
- there is no "all templates" view.
148
- - **`output_schema` is often `null`.** A template carries a non-null
149
- `output_schema` only when it ships a companion schema; most interop templates
150
- return `null`. Don't treat `null` as an error.
151
- - **`metadataDetails` keys on basename + intent, not the full path.** Pass the
152
- bare template name and the underscore-joined path prefix (the "intent"), not
153
- the slash path from `systemTemplates`.
154
- - **Discovery only.** None of these calls create or mutate anything — they read
155
- what the platform ships. To turn a template into a live mapping, copy its
156
- `content` into an `interoperabilityContracts.create(...)` `template_config`
157
- (see `interoperability_contracts.md`).
158
-
159
- # See also
160
-
161
- - `templates.md` — the `api.templates.*` reference (wire shapes, intents)
162
- - `interoperability_contracts.md` — how a template becomes a live row mapping
163
- - `_setup/subscription.md` — the bootstrap this walk starts from
164
- - `integration-tests/tests/subscription/system-templates.test.ts` — the
165
- green test these calls are lifted from
@@ -1,178 +0,0 @@
1
- ---
2
- title: "Capability: talk to your datalake — natural language → SQL → rows"
3
- summary: A capability walk for datalake text-to-SQL. Generate a read-only SQL statement from a natural-language prompt (`api.datalakes.textToSql`) — only the prompt + schema cross the LLM boundary, never rows — then run it read-only and read back the page (`api.datalakes.executeSql`), with both the JSON `{ data, meta }` envelope and a CSV export. The BI surface on a datalake.
4
- industry: foundation
5
- slug: talk-to-data
6
- vitest_source:
7
- - integration-tests/tests/foundation/text-to-sql.test.ts
8
- status: green
9
- ---
10
-
11
- # Capability
12
-
13
- **What you get:** a two-call BI surface on a datalake — describe what you want in
14
- plain language, get back reviewable SQL, then run it read-only and read the rows.
15
-
16
- The split is deliberate: `textToSql` *generates*, `executeSql` *runs*. A UI or
17
- agent can show the SQL, let a human edit it, and re-run — and the generate step
18
- is **data-free by construction** (only the prompt + the datalake schema reach the
19
- LLM, never rows), so it is safe in both `regulated` and `unregulated` mode.
20
-
21
- 1. `datalakes.textToSql(...)` — natural language → `{ sql, model, provider, explanation }`.
22
- 2. `datalakes.executeSql(...)` — read-only execution → `{ data, meta }` (data is an
23
- array-of-arrays row page aligned to `meta.columns`), or a CSV string with
24
- `{ format: 'csv' }`.
25
-
26
- See `datalakes.md` "Talk to data" for the wire reference and `ai_sandbox.md`
27
- Layer 3 for why the boundary holds.
28
-
29
- # Walkthrough
30
-
31
- The `_setup/foundation.md` bootstrap left `api`, `tenantSlug`, and `datalakeSlug`
32
- populated, pointing at a freshly-migrated foundation datalake.
33
-
34
- ## 001 — generate SQL from a natural-language prompt
35
-
36
- `textToSql` runs ordered multi-provider LLM failover and returns the generated
37
- `sql` plus the winning `model` / `provider` and a best-effort `explanation`
38
- (`null` when the explainer is unavailable). It does **not** execute the SQL — it
39
- hands it back for review. Nothing but the prompt and the datalake schema is sent
40
- to the model, so no datalake rows leave the trust boundary.
41
-
42
- ```typescript
43
- const { data: gen } = await api.datalakes.textToSql(tenantSlug, datalakeSlug, {
44
- prompt: 'how many legal entities are there?',
45
- mode: 'unregulated',
46
- })
47
- if (typeof gen.sql !== 'string' || gen.sql.trim() === '') {
48
- throw new Error(`textToSql returned no SQL (got: ${JSON.stringify(gen.sql)})`)
49
- }
50
- if (typeof gen.model !== 'string' || typeof gen.provider !== 'string') {
51
- throw new Error('textToSql response missing model/provider attribution')
52
- }
53
- // gen.explanation is best-effort plain-language prose, or null.
54
- ```
55
-
56
- ## 002 — run a read-only query and read back the page
57
-
58
- `executeSql` runs the SQL **read-only** (INSERT/UPDATE/DELETE/DDL are rejected)
59
- and returns `{ data, meta }`. `data` is an **array-of-arrays** row page — positional,
60
- aligned to `meta.columns`, because arbitrary SQL can have duplicate / expression
61
- column names. `meta` carries the structural + pagination fields. A deterministic
62
- `SELECT 1 AS n` keeps this step independent of the generated SQL and seeded data.
63
-
64
- The curated return is `ExecuteSqlResponse | string` (the CSV branch is a string),
65
- so narrow with a `typeof` guard before reading the JSON envelope.
66
-
67
- ```typescript
68
- const jsonResult = await api.datalakes.executeSql(tenantSlug, datalakeSlug, {
69
- sql: 'SELECT 1 AS n',
70
- mode: 'unregulated',
71
- })
72
- if (typeof jsonResult.data === 'string') {
73
- throw new Error('expected the JSON envelope, got a CSV string')
74
- }
75
- const page = jsonResult.data
76
- if (!Array.isArray(page.data) || !Array.isArray(page.data[0])) {
77
- throw new Error('executeSql data is not an array-of-arrays')
78
- }
79
- if (page.meta.columns[0] !== 'n' || page.data[0][0] !== 1 || page.meta.num_rows !== 1) {
80
- throw new Error(`unexpected SELECT 1 result: ${JSON.stringify(page)}`)
81
- }
82
- // page.meta also carries total_count, page, page_size, total_pages, duration_ms, command.
83
- ```
84
-
85
- ## 003 — export the same page as CSV
86
-
87
- Pass `{ format: 'csv' }` to get the page as a CSV **string** instead of the JSON
88
- envelope — the same read, content-negotiated for download.
89
-
90
- ```typescript
91
- const csvResult = await api.datalakes.executeSql(
92
- tenantSlug,
93
- datalakeSlug,
94
- { sql: 'SELECT 1 AS n', mode: 'unregulated' },
95
- { format: 'csv' },
96
- )
97
- if (typeof csvResult.data !== 'string') {
98
- throw new Error('expected a CSV string for { format: "csv" }')
99
- }
100
- const csv = csvResult.data
101
- if (!/\bn\b/.test(csv) || !csv.includes('1')) {
102
- throw new Error(`unexpected CSV body: ${JSON.stringify(csv)}`)
103
- }
104
- ```
105
-
106
- ## 004 — write the integration test
107
-
108
- End the walk with a test you keep: both surfaces are read-only, so the
109
- durable test is the pair of deterministic boundary probes — the
110
- round-trip works, and the read-only wall holds. Neither depends on an
111
- LLM provider or seeded data, which is what makes the test durable. This
112
- block runs live under `make validate-cookbook`.
113
-
114
- ```typescript
115
- // Round-trip probe — deterministic, data-independent.
116
- const probe = await api.datalakes.executeSql(tenantSlug, datalakeSlug, {
117
- sql: 'SELECT 1 AS ok',
118
- mode: 'unregulated',
119
- })
120
- if (typeof probe.data === 'string' || probe.data.data[0]?.[0] !== 1) {
121
- throw new Error('executeSql round-trip probe failed')
122
- }
123
- // Boundary probe — write SQL must be rejected with a 422; the
124
- // read-only wall is the security property this capability rests on.
125
- let walled = false
126
- try {
127
- await api.datalakes.executeSql(tenantSlug, datalakeSlug, {
128
- sql: "INSERT INTO legal_entities (legal_name) VALUES ('test-should-never-land')",
129
- mode: 'unregulated',
130
- })
131
- } catch (err) {
132
- const status = (err as { _httpStatus?: number })._httpStatus
133
- if (status !== 422) throw err
134
- walled = true
135
- }
136
- if (!walled) {
137
- throw new Error('executeSql accepted a write statement — the read-only wall is down')
138
- }
139
- ```
140
-
141
- If the write probe ever lands in production, stop and escalate
142
- immediately with the accepted statement as evidence — a read-only
143
- boundary failure is a security regression, never a config problem.
144
-
145
- # Branches
146
-
147
- - **Generation failure.** If every configured LLM provider fails, `textToSql`
148
- returns `422` (`AlveraApiError`) — `Text-to-SQL generation failed for all
149
- providers (...)`. Surface it; do not retry blindly.
150
- - **Write SQL rejected.** `executeSql` with a non-read-only statement
151
- (`INSERT`/`UPDATE`/`DELETE`/DDL) is rejected `422` — the read-only boundary is
152
- enforced at the app `@deny` regex, a read-only DB transaction, and an EXPLAIN
153
- preflight.
154
- - **Pagination.** Pass `page` / `page_size` to window large result sets;
155
- `meta.total_count` / `meta.total_pages` describe the full set (`page_size` is
156
- capped server-side).
157
-
158
- # Rollback
159
-
160
- Nothing to tear down — both calls are read-only (or generate-only). The
161
- `_setup/foundation.md` tenant + datalake are reset with `mix ecto.reset` on the
162
- platform; per-run `runSuffix` names avoid collisions across reruns.
163
-
164
- # Outcome
165
-
166
- A datalake becomes conversational: a prompt yields reviewable SQL with provider
167
- attribution and a plain-language explanation, that SQL runs read-only, and the
168
- page comes back either as a structured `{ data, meta }` envelope or a CSV export
169
- — all without any datalake row ever reaching the LLM.
170
-
171
- # See also
172
-
173
- - `.agent/datalakes.md` — "Talk to data" (`textToSql` / `executeSql` wire shape)
174
- and §6 gotcha on the array-of-arrays `data`.
175
- - `.agent/ai_sandbox.md` — Layer 3 SQL boundary: how generation stays data-free
176
- and execution stays read-only + mode-routed.
177
- - `integration-tests/tests/foundation/text-to-sql.test.ts` — the green vitest
178
- these snippets are lifted from.