@abloatai/ablo 0.57.0 → 0.58.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 (77) hide show
  1. package/AGENTS.md +10 -4
  2. package/CHANGELOG.md +199 -13
  3. package/README.md +2 -1
  4. package/dist/ai-sdk.d.ts +1 -1
  5. package/dist/ai-sdk.d.ts.map +1 -1
  6. package/dist/context/evidence.d.ts +6 -8
  7. package/dist/context/evidence.d.ts.map +1 -1
  8. package/dist/context/evidence.js +6 -20
  9. package/dist/context/evidence.js.map +1 -1
  10. package/dist/context/index.d.ts +23 -0
  11. package/dist/context/index.d.ts.map +1 -0
  12. package/dist/context/index.js +26 -0
  13. package/dist/context/index.js.map +1 -0
  14. package/dist/context/onChange.d.ts +9 -0
  15. package/dist/context/onChange.d.ts.map +1 -0
  16. package/dist/context/onChange.js +37 -0
  17. package/dist/context/onChange.js.map +1 -0
  18. package/dist/source-conformance.d.ts +1 -1
  19. package/dist/source-conformance.d.ts.map +1 -1
  20. package/dist/source-conformance.js +1 -1
  21. package/dist/source-conformance.js.map +1 -1
  22. package/dist/source-drizzle.d.ts +1 -1
  23. package/dist/source-drizzle.d.ts.map +1 -1
  24. package/dist/source-drizzle.js +1 -1
  25. package/dist/source-drizzle.js.map +1 -1
  26. package/dist/source-kysely.d.ts +1 -1
  27. package/dist/source-kysely.d.ts.map +1 -1
  28. package/dist/source-kysely.js +1 -1
  29. package/dist/source-kysely.js.map +1 -1
  30. package/dist/source-next.d.ts +1 -1
  31. package/dist/source-next.d.ts.map +1 -1
  32. package/dist/source-next.js +1 -1
  33. package/dist/source-next.js.map +1 -1
  34. package/docs/agent-integration-decision-guide.md +123 -0
  35. package/docs/agents.md +18 -14
  36. package/docs/api-keys.md +6 -6
  37. package/docs/api.md +52 -29
  38. package/docs/branch-development.md +23 -4
  39. package/docs/cli.md +16 -9
  40. package/docs/client-behavior.md +21 -15
  41. package/docs/concurrency-convention.md +67 -77
  42. package/docs/context.md +56 -31
  43. package/docs/coordination.md +115 -36
  44. package/docs/data-sources.md +12 -6
  45. package/docs/debugging.md +1 -1
  46. package/docs/examples/agent-human.md +6 -18
  47. package/docs/examples/coordination-conformance.md +69 -0
  48. package/docs/examples/existing-document-pipeline.md +488 -0
  49. package/docs/examples/existing-python-backend.md +10 -13
  50. package/docs/examples/nextjs.md +2 -2
  51. package/docs/examples/scoped-agent.md +18 -1
  52. package/docs/examples/server-agent.md +2 -2
  53. package/docs/groups.md +19 -139
  54. package/docs/guarantees.md +5 -6
  55. package/docs/identity.md +2 -1
  56. package/docs/index.md +5 -0
  57. package/docs/integration-guide.md +20 -19
  58. package/docs/integrations/sandbox-runtime.md +148 -0
  59. package/docs/integrations.md +9 -0
  60. package/docs/operating-on-your-database.md +7 -0
  61. package/docs/quickstart.md +19 -13
  62. package/docs/react.md +9 -9
  63. package/docs/schema-contract.md +14 -13
  64. package/docs/sessions.md +1 -1
  65. package/examples/README.md +2 -2
  66. package/examples/agent-turn.ts +1 -1
  67. package/examples/expensive-agent-turn.ts +1 -1
  68. package/llms.txt +22 -11
  69. package/package.json +6 -6
  70. package/dist/context/sources.d.ts +0 -21
  71. package/dist/context/sources.d.ts.map +0 -1
  72. package/dist/context/sources.js +0 -36
  73. package/dist/context/sources.js.map +0 -1
  74. package/dist/context.d.ts +0 -22
  75. package/dist/context.d.ts.map +0 -1
  76. package/dist/context.js +0 -33
  77. package/dist/context.js.map +0 -1
package/docs/react.md CHANGED
@@ -25,7 +25,7 @@ as props. Construct the client once, then pass that instance to the provider.
25
25
 
26
26
  ```ts
27
27
  // lib/ablo.ts
28
- import Ablo from '@abloatai/ablo';
28
+ import { Ablo } from '@abloatai/ablo/react';
29
29
  import { createAbloReact } from '@abloatai/ablo/react';
30
30
  import { schema } from '@/ablo/schema';
31
31
 
@@ -140,12 +140,12 @@ const reports = useAblo((ablo) =>
140
140
  ## Server Load
141
141
 
142
142
  ```tsx
143
- const report = await ablo.weatherReports.get({ id });
143
+ const report = await ablo.weatherReports.read({ id });
144
144
  ```
145
145
 
146
146
  Use `get` in Server Components when the row may not be in the local pool
147
147
  yet — it hydrates from the local store and the server, and returns a Promise, so
148
- `await` it. (Server reads come in two shapes: `get({ id })` for one row and
148
+ `await` it. (Server reads come in two shapes: `read({ id })` for one row and
149
149
  `list({ where })` for many; both are async. The synchronous local reads are
150
150
  the `local` reads, used in render below.)
151
151
 
@@ -156,12 +156,12 @@ For Server Actions and route handlers, call the SDK directly:
156
156
  ```ts
157
157
  import { ablo } from '@/lib/ablo';
158
158
 
159
- const snap = ablo.snapshot({ weatherReports: id });
159
+ const report = await ablo.weatherReports.read({ id });
160
+ if (!report) throw new Error('report not found');
160
161
  await ablo.weatherReports.update({
161
162
  id,
162
163
  data: patch,
163
- readAt: snap.stamp,
164
- onStale: 'reject',
164
+ reads: [report],
165
165
  });
166
166
  ```
167
167
 
@@ -173,12 +173,12 @@ const ablo = useAblo();
173
173
 
174
174
  async function markReady() {
175
175
  if (!ablo) return;
176
- const snap = ablo.snapshot({ weatherReports: id });
176
+ const report = await ablo.weatherReports.read({ id });
177
+ if (!report) return;
177
178
  await ablo.weatherReports.update({
178
179
  id,
179
180
  data: { status: 'ready' },
180
- readAt: snap.stamp,
181
- onStale: 'reject',
181
+ reads: [report],
182
182
  });
183
183
  }
184
184
  ```
@@ -51,26 +51,27 @@ The model key (`weatherReports`) becomes the client namespace
51
51
  contract. You should not create a parallel string-keyed write path for the same
52
52
  data.
53
53
 
54
- ### Reserved fields
54
+ ### The one field you don't declare
55
55
 
56
- The SDK provides these on every row automatically do **not** declare them in
57
- your `model(...)` fields:
56
+ `id` is supplied on every row, so leave it out of your `model(...)` fields. That
57
+ is the whole list.
58
58
 
59
- - `id`
60
- - `createdAt`
61
- - `updatedAt`
62
- - `organizationId`
63
- - `createdBy`
59
+ Two things look like framework territory and are not. **Audit fields are yours to
60
+ declare and yours to fill.** Add `createdAt`, `updatedAt` or `createdBy` and
61
+ `ablo migrate` gives each a column, which your own write or a database default
62
+ then populates; Ablo records who changed what in its transaction log and does not
63
+ write these columns for you. Omit them and no column is created at all; the model
64
+ still reads and writes, it just orders and attributes its history less precisely.
64
65
 
65
- Declare only your own fields; the reserved ones are still present on the row and
66
- readable, you just don't author them.
66
+ **The tenancy column** (`organizationId` by default) comes from the model's
67
+ `policy` rather than its field list, so you neither declare it nor lose it.
67
68
 
68
69
  ## Reads and writes
69
70
 
70
71
  Use async reads when the row may not be local:
71
72
 
72
73
  ```ts
73
- const report = await ablo.weatherReports.get({ id: reportId });
74
+ const report = await ablo.weatherReports.read({ id: reportId });
74
75
  const ready = await ablo.weatherReports.list({ where: { status: 'ready' } });
75
76
  ```
76
77
 
@@ -123,8 +124,8 @@ session route, never a raw API key.
123
124
  - Keep direct database writes out of the coordinated path unless they are reported
124
125
  back through Data Source events.
125
126
  - Use `claim` for slow read -> think -> write spans.
126
- - Use `readAt` + `onStale: 'reject'` when a write must fail if the row changed
127
- after it was read.
127
+ - Use `read` and pass its exact row in `reads` when a write must fail if the row
128
+ changed after it was read.
128
129
 
129
130
  For the shortest runnable path, start with [Quickstart](./quickstart.md). For a
130
131
  production app, continue with [Integration Guide](./integration-guide.md).
package/docs/sessions.md CHANGED
@@ -74,7 +74,7 @@ plumbing:
74
74
  ```tsx
75
75
  'use client';
76
76
 
77
- import Ablo from '@abloatai/ablo';
77
+ import { Ablo } from '@abloatai/ablo/react';
78
78
  import { AbloProvider } from '@abloatai/ablo/react';
79
79
  import { schema } from '@/ablo.schema';
80
80
 
@@ -30,8 +30,8 @@ For read-reason-write work, pass the exact returned rows that informed the
30
30
  decision. Their watermarks stay opaque:
31
31
 
32
32
  ```ts
33
- const record = await ablo.records.get({ id: recordId });
34
- const policy = await ablo.policies.get({ id: policyId });
33
+ const record = await ablo.records.read({ id: recordId });
34
+ const policy = await ablo.policies.read({ id: policyId });
35
35
  const result = await model({ record, policy });
36
36
  await ablo.records.update({
37
37
  id: record.id,
@@ -21,7 +21,7 @@ if (!recordId) throw new Error('RECORD_ID is required');
21
21
  const ablo = Ablo({ schema, apiKey: process.env.ABLO_API_KEY });
22
22
  try {
23
23
  await ablo.ready();
24
- const record = await ablo.records.get({ id: recordId });
24
+ const record = await ablo.records.read({ id: recordId });
25
25
  if (!record) throw new Error(`Record ${recordId} was not found`);
26
26
  const commitId = `record:${recordId}:cheap`;
27
27
  await ablo.records.update({
@@ -49,7 +49,7 @@ try {
49
49
  if (!durable) throw new Error(`Commit ${commitId} was not retained`);
50
50
  console.log({
51
51
  identity: ablo.identity,
52
- readSet: durable.readSet,
52
+ reads: durable.reads,
53
53
  attempts: durable.attempts,
54
54
  claims: durable.claims,
55
55
  authority: durable.authority,
package/llms.txt CHANGED
@@ -27,7 +27,7 @@ These are the jobs it is WRONG for, and saying so saves you a wasted integration
27
27
  - **Vector search, embeddings, or retrieval.** Different problem. Ablo coordinates the rows; your vector store indexes them.
28
28
  - **A file, blob, or artifact store.** Coordinate the ROW that points at the object; put the bytes in object storage.
29
29
 
30
- How to call it, shortest path first: install `@abloatai/ablo`, run `npx ablo init --yes --framework <nextjs|vite|remix|vanilla>`, and construct `Ablo({ schema, apiKey: process.env.ABLO_API_KEY })`. Read with `ablo.<model>.get({ id })` / `.list({ where })`, write with `ablo.<model>.update({ id, data, readAt })`, and wrap anything slow in `await using claim = await ablo.<model>.claim({ id })`. The sections below cover the rest; "Start here" is the first thing to run.
30
+ How to call it, shortest path first: install `@abloatai/ablo`, run `npx ablo init --yes --framework <nextjs|vite|remix|vanilla>`, and construct `Ablo({ schema, apiKey: process.env.ABLO_API_KEY })`. Use `ablo.<model>.get({ id })` to observe a row. Use `const input = await ablo.<model>.read({ id })`, then pass `reads: [input]` to the create/update/delete whose decision used it; if the row moved, that mutation does not land. `list({ where })` is observational. Wrap anything slow and exclusive in `await using claim = await ablo.<model>.claim({ id })`. The sections below cover the rest; "Start here" is the first thing to run.
31
31
 
32
32
  ## Machine-readable surfaces
33
33
 
@@ -38,10 +38,15 @@ Everything below is public, needs no credential, and is served from `https://www
38
38
  - `/developers` — every developer surface named on one page: the SDK, the API reference, the OpenAPI document, the MCP server, the CLI.
39
39
  - `/openapi.json` — the REST contract as OpenAPI 3.1: every route, a stable `operationId` and description on each, typed responses, and the `ErrorEnvelope` every 4xx and 5xx decodes through. Generate a client from it when no SDK exists for your runtime.
40
40
  - `/mcp` — the integration-helper MCP server over Streamable HTTP. POST your JSON-RPC here; a GET returns a descriptor rather than the protocol.
41
+ - `/.well-known/mcp` — the same Streamable HTTP server at its direct discovery endpoint, with no page/transport negotiation.
41
42
  - `/.well-known/mcp.json` — that server's manifest, in the MCP registry's `server.json` format.
42
43
  - `/api/docs/<page>` — any documentation page as plain Markdown, for a client that fetches URLs rather than speaking MCP.
43
44
  - `/sitemap.xml` — every indexable page on the domain.
44
45
 
46
+ Pricing and plan limits are public at [Ablo pricing](https://docs.abloatai.com/pricing). Do not
47
+ infer prices or follow a dashboard billing link: the documentation page is the canonical rate
48
+ card and needs no sign-in.
49
+
45
50
  Every page on that host serves a Markdown representation from its own URL: send
46
51
  `Accept: text/markdown` (q-values are honoured), or append `.md` to the path if
47
52
  your client cannot set the header. Responses carry `Vary: Accept`, a client that
@@ -65,6 +70,7 @@ Every surface reaches the same coordinated state. They are not interchangeable.
65
70
  - Coordination MCP, `@abloatai/mcp` — an agent living inside an MCP host (Claude, Cursor). Two jobs: MANAGING your Ablo the way the CLI does (`get_schema`, `list_projects`, `create_project`, `tail_logs`, `get_usage`), and claim/commit as tools over your rows. Call `get_schema` first — it is the only way to learn the model names every data tool needs. `init`, `push`, `pull`, `generate` have no tools (they touch your repo); run those in a shell.
66
71
  - `humans()` with `@abloatai/humans/react` — the interfaces a person watches agent work arrive in. The bare client is the coordination layer (commit, read, observe, claim); `humans()` is the plugin that adds the local watchable copy, live queries, and presence. It `requires: { duplex: true }`, so a `transport: 'http'` agent is rejected at construction rather than left holding a subscription that never delivers. It remains the compatibility default for omitted `plugins`; new code should install it explicitly from `@abloatai/humans`. There is no `agents()` plugin — agents are the default caller, not a special one. A browser NEVER receives the secret key; mint a session token.
67
72
  - CLI, `ablo` — scaffolding, schema push, connecting a database. Terminals and CI. Agents must run it non-interactively (see the CLI section below).
73
+ - CLI package, [`@abloatai/cli`](https://www.npmjs.com/package/@abloatai/cli) — the official npm package that installs the `ablo` binary.
68
74
  - REST, `/api/v1` — runtimes with no SDK.
69
75
  - Integration-helper MCP, hosted `/api/mcp` — teaching a coding assistant the SDK WHILE BUILDING an integration. Docs, schema lint, and scaffolds only; it has NO per-model data tools and cannot touch application rows.
70
76
 
@@ -103,7 +109,7 @@ const schema = defineSchema({
103
109
 
104
110
  const ablo = Ablo({ schema, apiKey: process.env.ABLO_API_KEY });
105
111
 
106
- const report = await ablo.weatherReports.get({ id: 'report_stockholm' });
112
+ const report = await ablo.weatherReports.read({ id: 'report_stockholm' });
107
113
  if (!report) throw new Error('Row not found');
108
114
 
109
115
  // Claim the row (waits if someone else holds it), read the fresh copy off
@@ -115,7 +121,7 @@ const updated = await ablo.weatherReports.update({
115
121
  });
116
122
  ```
117
123
 
118
- That is the normal app path: declare models in a schema, then use `ablo.<model>.get({ id })`, `ablo.<model>.create(...)`, `ablo.<model>.update(...)`, and `ablo.<model>.delete(...)`. Every verb takes a single options object.
124
+ That is the normal app path: declare models in a schema, then use `ablo.<model>.read({ id })`, `ablo.<model>.create(...)`, `ablo.<model>.update(...)`, and `ablo.<model>.delete(...)`. Every verb takes a single options object.
119
125
 
120
126
  Treat the schema as the integration contract. It drives typed model clients,
121
127
  React selectors, server and agent writes, Data Source request/response shape,
@@ -126,12 +132,12 @@ For full integrations, use `integration-guide` as the canonical doc. It covers
126
132
  the same model API across your own Data Source-backed app databases,
127
133
  React selectors, multiplayer, and future agent workers.
128
134
 
129
- Reads come in two flavors, and you pick by whether you can wait. `get({ id })`
130
- (one row) and `list({ where })` (many) are async they answer from the local
131
- graph and fall back to the server, so await them. `local.get(id)`,
135
+ Server reads separate observation from decision input. `get({ id })` and
136
+ `list({ where })` return current data without creating stale evidence.
137
+ `read({ id })` returns the same row with private `{ model, id, readAt }` evidence
138
+ for a specific mutation's `reads` array. `local.get(id)`,
132
139
  `local.list({ where })`, and `local.count({ where })` are the same reads narrowed
133
- to what has already synced: nothing to await, reactive in render. There is no
134
- second verb to learn — `local.` is the only difference. The query reads accept
140
+ to what has already synced: nothing to await, reactive in render. The collection queries accept
135
141
  `where`, `filter`, `orderBy`, `limit`, `offset`,
136
142
  and `state`; state defaults to `'live'`, with `'archived'` and `'all'` to include
137
143
  retired rows.
@@ -168,7 +174,7 @@ coordination until the app reports it through Data Source events.
168
174
 
169
175
  ## Change propagation
170
176
 
171
- A change to one row reaches other rows three ways. ROUTING: a write fans out to every sync group the row belongs to, INCLUDING its ancestors' groups (editing a block routes to `block:` + `document:` + `workspace:`), so everyone watching the workspace sees it — delivery, not recomputation. DELETE CASCADE: deleting a parent emits explicit tombstone deltas for its descendants, so open clients never silently hold rows that are gone. VALUE: derived values are NOT recomputed server-side — Ablo surfaces that the source moved and the actor decides. To keep dependent work fresh, declare what you read as a batch premise: `reads: [{ group: 'workspace:abc', readAt: N, onStale: 'notify' }]`. At commit the server checks whether anything in that group moved past `readAt`; `notify` holds the write and returns a `StaleNotification` (re-read the group and regenerate), `reject` aborts. To chain A→B→C, put A+B in one group and B+C in another: A's change reaches B, and C hears it only once B ITSELF writes — the engine wires the edges and signals each hop, the actor walks them. No transitive auto-recompute, no convergence guarantee for cycles. `reads` guards ONE commit; for a long-running actor that reads now and writes much later, register a DURABLE premise with `track`: `ablo.<model>.track({ id })` for a row, or the `track:` write option (`track: [{ group: 'workspace:abc' }]`) alongside a write. A track persists server-side; the next time you commit anything, a change that landed on the tracked target rides back on your receipt's `notifications` (same `StaleNotification` shape as `onStale: 'notify'`). It is an idempotent registration, re-baselines so a change fires once, and never notifies you of your own writes. Delivery is on your next commit — a track does not yet push out of band between commits.
177
+ A change to one row reaches other rows three ways. ROUTING: a write fans out to every sync group the row belongs to, INCLUDING its ancestors' groups (editing a block routes to `block:` + `document:` + `workspace:`), so everyone watching the workspace sees it — delivery, not recomputation. DELETE CASCADE: deleting a parent emits explicit tombstone deltas for its descendants, so open clients never silently hold rows that are gone. VALUE: derived values are NOT recomputed server-side — Ablo surfaces that the source moved and the actor decides. To keep dependent work fresh, pass rows returned by `ablo.<model>.read({ id })` in the mutation's `reads` array. Ablo records compact model/id/readAt evidence, not row contents. At commit the server checks whether anything moved past the read watermark; if so, the mutation does not land. Use `get` when no such relationship exists. To chain A→B→C, put A+B in one group and B+C in another: A's change reaches B, and C hears it only once B ITSELF writes. No transitive auto-recompute, no convergence guarantee for cycles.
172
178
 
173
179
  ## Nouns
174
180
 
@@ -180,7 +186,7 @@ A change to one row reaches other rows three ways. ROUTING: a write fans out to
180
186
  ## Claimed Behavior
181
187
 
182
188
  Reads never silently block. Schema reads stay open while a row is claimed.
183
- Typed reads through `ablo.<model>.get({ id })` return the row by default,
189
+ Typed reads through `ablo.<model>.read({ id })` return the row by default,
184
190
  even while it is claimed. Pass `ifClaimed: 'fail'` to throw
185
191
  `AbloClaimedError`, and inspect active coordination separately through
186
192
  `ablo.<model>.claim.state({ id })`.
@@ -198,7 +204,7 @@ frees. Use `ifClaimed: 'fail'` when you'd rather refuse to read a claimed row.
198
204
 
199
205
  ## Guarantees
200
206
 
201
- Schema model writes update local state optimistically, while their returned promise always waits for authoritative confirmation. Server rejection rolls back local state. To prevent lost updates, read with `snapshot(...)` to capture a `readAt`, then write with `onStale: 'reject'` the server rejects your update if someone else changed the row after that `readAt`.
207
+ Schema model writes update local state optimistically, while their returned promise always waits for authoritative confirmation. Server rejection rolls back local state. To prevent lost updates, fetch the decision input with `read({ id })` and pass that exact row in the mutation's `reads` array. If it changed, the mutation does not land.
202
208
 
203
209
  Claims coordinate writers; they do not block readers. Most users should stay on
204
210
  schema-backed reads/writes and `claim(...)`; manual protocol bookkeeping is not
@@ -282,6 +288,11 @@ Import from these public paths only:
282
288
 
283
289
  Do not teach `/api`, `/agent`, `/core`, `/realtime`, or internal subpaths. (`/source` and `/ai-sdk` are public.)
284
290
 
291
+ `onChange` is WebSocket-only. It exists on the reactive client because that
292
+ client holds an open socket and a local graph. The stateless HTTP client does
293
+ not expose it at the type or runtime level; HTTP agents re-read or inspect
294
+ durable logs instead.
295
+
285
296
  ## CLI: agents run it NON-INTERACTIVELY
286
297
 
287
298
  `ablo init` and other prompts need a TTY; an agent/CI run has none and will HANG. Always:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abloatai/ablo",
3
- "version": "0.57.0",
3
+ "version": "0.58.0",
4
4
  "description": "The public Ablo SDK for coordinated reads, commits, claims, observation, and reactive applications.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -81,9 +81,9 @@
81
81
  "default": "./dist/ai-sdk.js"
82
82
  },
83
83
  "./context": {
84
- "types": "./dist/context.d.ts",
85
- "import": "./dist/context.js",
86
- "default": "./dist/context.js"
84
+ "types": "./dist/context/index.d.ts",
85
+ "import": "./dist/context/index.js",
86
+ "default": "./dist/context/index.js"
87
87
  },
88
88
  "./wire": {
89
89
  "types": "./dist/wire.d.ts",
@@ -139,8 +139,8 @@
139
139
  "directory": "packages/ablo"
140
140
  },
141
141
  "dependencies": {
142
- "@abloatai/humans": "^0.57.0",
143
- "@abloatai/transaction": "^0.57.0",
142
+ "@abloatai/humans": "^0.58.0",
143
+ "@abloatai/transaction": "^0.58.0",
144
144
  "zod": "^4.4.3"
145
145
  },
146
146
  "peerDependencies": {
@@ -1,21 +0,0 @@
1
- import { z } from 'zod';
2
- import type { ContextEvidenceSlice } from './evidence.js';
3
- export declare const contextSourceSchema: z.ZodReadonly<z.ZodDiscriminatedUnion<[z.ZodObject<{
4
- key: z.ZodString;
5
- kind: z.ZodLiteral<"ablo">;
6
- guarantee: z.ZodLiteral<"guardable">;
7
- cursor: z.ZodNumber;
8
- }, z.core.$strip>, z.ZodObject<{
9
- key: z.ZodString;
10
- kind: z.ZodLiteral<"value">;
11
- guarantee: z.ZodLiteral<"informational">;
12
- cursor: z.ZodNull;
13
- }, z.core.$strip>, z.ZodObject<{
14
- key: z.ZodString;
15
- kind: z.ZodLiteral<"mixed">;
16
- guarantee: z.ZodLiteral<"partial">;
17
- cursor: z.ZodNumber;
18
- }, z.core.$strip>], "kind">>;
19
- export type ContextSource = z.infer<typeof contextSourceSchema>;
20
- export declare function sourceFor(key: string, evidence: ContextEvidenceSlice): ContextSource;
21
- //# sourceMappingURL=sources.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sources.d.ts","sourceRoot":"","sources":["../../src/context/sources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAE1D,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;4BAmBnB,CAAC;AAEd,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,wBAAgB,SAAS,CACvB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,oBAAoB,GAC7B,aAAa,CAaf"}
@@ -1,36 +0,0 @@
1
- import { z } from 'zod';
2
- export const contextSourceSchema = z.discriminatedUnion('kind', [
3
- z.object({
4
- key: z.string(),
5
- kind: z.literal('ablo'),
6
- guarantee: z.literal('guardable'),
7
- cursor: z.number().int().nonnegative(),
8
- }),
9
- z.object({
10
- key: z.string(),
11
- kind: z.literal('value'),
12
- guarantee: z.literal('informational'),
13
- cursor: z.null(),
14
- }),
15
- z.object({
16
- key: z.string(),
17
- kind: z.literal('mixed'),
18
- guarantee: z.literal('partial'),
19
- cursor: z.number().int().nonnegative(),
20
- }),
21
- ]).readonly();
22
- export function sourceFor(key, evidence) {
23
- if (evidence.reads.length === 0) {
24
- return contextSourceSchema.parse({
25
- key,
26
- kind: 'value',
27
- guarantee: 'informational',
28
- cursor: null,
29
- });
30
- }
31
- const cursor = Math.max(...evidence.reads.map((item) => item.entry.watermark));
32
- return evidence.includesInformational
33
- ? contextSourceSchema.parse({ key, kind: 'mixed', guarantee: 'partial', cursor })
34
- : contextSourceSchema.parse({ key, kind: 'ablo', guarantee: 'guardable', cursor });
35
- }
36
- //# sourceMappingURL=sources.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sources.js","sourceRoot":"","sources":["../../src/context/sources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,kBAAkB,CAAC,MAAM,EAAE;IAC9D,CAAC,CAAC,MAAM,CAAC;QACP,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;QACf,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;QACvB,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC;QACjC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;KACvC,CAAC;IACF,CAAC,CAAC,MAAM,CAAC;QACP,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;QACf,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;QACxB,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC;QACrC,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE;KACjB,CAAC;IACF,CAAC,CAAC,MAAM,CAAC;QACP,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;QACf,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;QACxB,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;QAC/B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;KACvC,CAAC;CACH,CAAC,CAAC,QAAQ,EAAE,CAAC;AAId,MAAM,UAAU,SAAS,CACvB,GAAW,EACX,QAA8B;IAE9B,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChC,OAAO,mBAAmB,CAAC,KAAK,CAAC;YAC/B,GAAG;YACH,IAAI,EAAE,OAAO;YACb,SAAS,EAAE,eAAe;YAC1B,MAAM,EAAE,IAAI;SACb,CAAC,CAAC;IACL,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;IAC/E,OAAO,QAAQ,CAAC,qBAAqB;QACnC,CAAC,CAAC,mBAAmB,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;QACjF,CAAC,CAAC,mBAAmB,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;AACvF,CAAC"}
package/dist/context.d.ts DELETED
@@ -1,22 +0,0 @@
1
- import type { CapturedRow } from '@abloatai/transaction';
2
- import { type AwaitedDeep } from './context/await.js';
3
- import { type ContextSource } from './context/sources.js';
4
- export { contextSourceSchema, type ContextSource } from './context/sources.js';
5
- export type { AwaitedDeep } from './context/await.js';
6
- export interface ContextOptions<TData extends Readonly<Record<string, unknown>>> {
7
- /** The client whose read evidence may guard a later write. */
8
- readonly ablo: object;
9
- /** Values selected by the application. Nested promises are accepted. */
10
- readonly data: TData;
11
- }
12
- export interface ContextResult<TData extends Readonly<Record<string, unknown>>> {
13
- readonly data: AwaitedDeep<TData>;
14
- /** Exact returned Ablo rows, ready to pass to a write's `reads` option. */
15
- readonly reads: readonly CapturedRow[];
16
- /** The greatest watermark among included authoritative reads. */
17
- readonly cursor: number | null;
18
- readonly sources: readonly ContextSource[];
19
- }
20
- /** Assemble selected application values and the Ablo evidence they retain. */
21
- export declare function context<const TData extends Readonly<Record<string, unknown>>>(options: ContextOptions<TData>): Promise<ContextResult<TData>>;
22
- //# sourceMappingURL=context.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAa,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjE,OAAO,EAAa,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErE,OAAO,EAAE,mBAAmB,EAAE,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC/E,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAItD,MAAM,WAAW,cAAc,CAAC,KAAK,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7E,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;CACtB;AAED,MAAM,WAAW,aAAa,CAAC,KAAK,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5E,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC;IAClC,2EAA2E;IAC3E,QAAQ,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IACvC,iEAAiE;IACjE,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;CAC5C;AAED,8EAA8E;AAC9E,wBAAsB,OAAO,CAAC,KAAK,CAAC,KAAK,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACjF,OAAO,EAAE,cAAc,CAAC,KAAK,CAAC,GAC7B,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAsB/B"}
package/dist/context.js DELETED
@@ -1,33 +0,0 @@
1
- /**
2
- * Additive context assembly for one Ablo client.
3
- *
4
- * The caller chooses the data. This module awaits it, reports the exact Ablo
5
- * rows it contains, and leaves model execution and external retrieval alone.
6
- */
7
- import { z } from 'zod';
8
- import { awaitDeep } from './context/await.js';
9
- import { bindContextEvidence } from './context/evidence.js';
10
- import { sourceFor } from './context/sources.js';
11
- export { contextSourceSchema } from './context/sources.js';
12
- const contextDataSchema = z.record(z.string(), z.unknown());
13
- /** Assemble selected application values and the Ablo evidence they retain. */
14
- export async function context(options) {
15
- const collectEvidence = bindContextEvidence(options.ablo);
16
- const data = await awaitDeep(options.data);
17
- const parsed = contextDataSchema.safeParse(data);
18
- if (!parsed.success) {
19
- throw new TypeError('context() requires `data` to be an object.', { cause: parsed.error });
20
- }
21
- const evidence = collectEvidence(data);
22
- const sources = Object.entries(data).map(([key, value]) => sourceFor(key, evidence.inspect(value)));
23
- const cursor = evidence.all.length === 0
24
- ? null
25
- : Math.max(...evidence.all.map((item) => item.entry.watermark));
26
- return {
27
- data: data,
28
- reads: evidence.all.map((item) => item.row),
29
- cursor,
30
- sources,
31
- };
32
- }
33
- //# sourceMappingURL=context.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,SAAS,EAAoB,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAsB,MAAM,sBAAsB,CAAC;AAErE,OAAO,EAAE,mBAAmB,EAAsB,MAAM,sBAAsB,CAAC;AAG/E,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;AAkB5D,8EAA8E;AAC9E,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,OAA8B;IAE9B,MAAM,eAAe,GAAG,mBAAmB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,iBAAiB,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACjD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,SAAS,CAAC,4CAA4C,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;IAC7F,CAAC;IAED,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CACxD,SAAS,CAAC,GAAG,EAAE,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CACxC,CAAC;IACF,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,KAAK,CAAC;QACtC,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;IAElE,OAAO;QACL,IAAI,EAAE,IAA0B;QAChC,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAkB,CAAC;QAC1D,MAAM;QACN,OAAO;KACR,CAAC;AACJ,CAAC"}