@abloatai/ablo 0.28.0 → 0.29.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 (96) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/Database.js +9 -6
  3. package/dist/SyncClient.d.ts +2 -2
  4. package/dist/SyncClient.js +22 -10
  5. package/dist/adapters/inMemoryStorage.js +18 -14
  6. package/dist/ai-sdk/coordinatedTool.d.ts +15 -2
  7. package/dist/auth/schemas.d.ts +6 -0
  8. package/dist/auth/schemas.js +7 -0
  9. package/dist/cli.cjs +815 -400
  10. package/dist/client/Ablo.d.ts +29 -23
  11. package/dist/client/Ablo.js +8 -196
  12. package/dist/client/claimHeartbeatLoop.d.ts +1 -1
  13. package/dist/client/claimHeartbeatLoop.js +1 -1
  14. package/dist/client/createInternalComponents.d.ts +4 -2
  15. package/dist/client/createInternalComponents.js +3 -1
  16. package/dist/client/durableWrites.d.ts +21 -0
  17. package/dist/client/durableWrites.js +46 -0
  18. package/dist/client/httpClient.d.ts +19 -45
  19. package/dist/client/httpClient.js +104 -26
  20. package/dist/client/httpTransport.d.ts +8 -0
  21. package/dist/client/{ApiClient.js → httpTransport.js} +78 -291
  22. package/dist/client/options.d.ts +43 -19
  23. package/dist/client/options.js +3 -2
  24. package/dist/client/resourceTypes.d.ts +9 -85
  25. package/dist/client/resourceTypes.js +1 -1
  26. package/dist/client/sessionMint.d.ts +5 -6
  27. package/dist/client/sessionMint.js +4 -5
  28. package/dist/client/validateAbloOptions.js +3 -3
  29. package/dist/coordination/schema.d.ts +30 -0
  30. package/dist/coordination/schema.js +14 -0
  31. package/dist/core/index.d.ts +1 -1
  32. package/dist/errors.d.ts +4 -40
  33. package/dist/errors.js +5 -5
  34. package/dist/index.d.ts +14 -8
  35. package/dist/index.js +7 -5
  36. package/dist/interfaces/index.d.ts +3 -0
  37. package/dist/mutators/defineMutators.d.ts +18 -0
  38. package/dist/mutators/defineMutators.js +4 -8
  39. package/dist/react/index.d.ts +1 -1
  40. package/dist/schema/index.d.ts +1 -1
  41. package/dist/schema/index.js +1 -1
  42. package/dist/schema/select.d.ts +15 -0
  43. package/dist/schema/select.js +27 -3
  44. package/dist/source/connector.d.ts +3 -3
  45. package/dist/source/factory.d.ts +4 -6
  46. package/dist/source/factory.js +4 -7
  47. package/dist/source/index.d.ts +4 -4
  48. package/dist/source/index.js +2 -2
  49. package/dist/source/signing.d.ts +0 -3
  50. package/dist/source/types.d.ts +0 -26
  51. package/dist/stores/ObjectStore.js +8 -8
  52. package/dist/surface.d.ts +1 -1
  53. package/dist/surface.js +1 -0
  54. package/dist/sync/BootstrapFetcher.d.ts +10 -0
  55. package/dist/sync/BootstrapFetcher.js +27 -4
  56. package/dist/sync/createClaimStream.d.ts +11 -2
  57. package/dist/sync/createClaimStream.js +4 -3
  58. package/dist/testing/mocks/FakeDatabase.d.ts +1 -1
  59. package/dist/transactions/TransactionQueue.d.ts +2 -2
  60. package/dist/transactions/TransactionQueue.js +3 -3
  61. package/dist/transactions/commitEnvelope.d.ts +1 -1
  62. package/dist/transactions/commitEnvelope.js +2 -2
  63. package/dist/transactions/commitOutboxStore.d.ts +19 -19
  64. package/dist/transactions/durableWriteStore.d.ts +123 -0
  65. package/dist/transactions/durableWriteStore.js +30 -0
  66. package/dist/transactions/httpCommitEnvelope.d.ts +1 -0
  67. package/dist/transactions/httpCommitEnvelope.js +14 -12
  68. package/dist/transactions/idempotencyKey.d.ts +10 -0
  69. package/dist/transactions/idempotencyKey.js +9 -0
  70. package/dist/types/global.d.ts +10 -29
  71. package/dist/types/global.js +4 -7
  72. package/dist/types/streams.d.ts +2 -28
  73. package/dist/wire/frames.d.ts +6 -257
  74. package/dist/wire/frames.js +4 -25
  75. package/dist/wire/index.d.ts +4 -3
  76. package/dist/wire/index.js +2 -2
  77. package/dist/wire/protocolVersion.d.ts +30 -17
  78. package/dist/wire/protocolVersion.js +34 -18
  79. package/docs/agents.md +8 -3
  80. package/docs/api.md +6 -4
  81. package/docs/client-behavior.md +6 -3
  82. package/docs/coordination.md +2 -3
  83. package/docs/data-sources.md +5 -8
  84. package/docs/guarantees.md +21 -0
  85. package/docs/integration-guide.md +1 -0
  86. package/docs/migration.md +21 -1
  87. package/docs/quickstart.md +11 -0
  88. package/docs/react.md +0 -46
  89. package/examples/README.md +6 -5
  90. package/llms.txt +15 -15
  91. package/package.json +2 -7
  92. package/dist/client/ApiClient.d.ts +0 -177
  93. package/dist/commit/contract.d.ts +0 -493
  94. package/dist/commit/contract.js +0 -187
  95. package/dist/commit/index.d.ts +0 -6
  96. package/dist/commit/index.js +0 -5
@@ -125,6 +125,27 @@ const ablo = Ablo({
125
125
 
126
126
  Node, SSR, tests, and agents use in-memory persistence ('memory') automatically.
127
127
 
128
+ Cache persistence and outbound-write recovery are separate concerns. Most
129
+ clients need only the default memory cache: once the server confirms a write,
130
+ the server is durable and the idempotency key makes a retry safe. A long-running
131
+ worker that must also recover an unacknowledged write after its own process dies
132
+ can opt into a durable write journal:
133
+
134
+ ```ts
135
+ const ablo = Ablo({
136
+ schema,
137
+ apiKey: process.env.ABLO_API_KEY,
138
+ durableWrites: {
139
+ store: workerWriteStore,
140
+ namespace: 'report-worker',
141
+ },
142
+ });
143
+ ```
144
+
145
+ The store can be backed by the worker's workflow state, SQLite, or another
146
+ durable system. Actor identity is derived from authentication; `namespace` only
147
+ separates workflow or deployment lanes sharing the same store.
148
+
128
149
  ## Storage Boundary
129
150
 
130
151
  Ablo does not need a customer database URL. When your own database is canonical,
@@ -492,6 +492,7 @@ Keep agent writes on the same schema client surface as the app.
492
492
  | `/testing` | Test harnesses and deterministic mocks. |
493
493
  | `Data Source` | Keep your app database canonical. |
494
494
  | `persistence: 'indexeddb'` | Durable browser cache that survives reloads, for apps that need it. |
495
+ | `durableWrites: { store, namespace? }` | Recover unacknowledged worker writes after a process restart. |
495
496
  | `claim` / `claim.state` / `claim.queue` | Show active work and coordinate before a write. |
496
497
  | `snapshot` + `readAt` | Reject writes based on stale state. |
497
498
  | `mutable`, `readOnly`, `field`, `indexed` | Advanced schema and read tuning. |
package/docs/migration.md CHANGED
@@ -11,7 +11,8 @@ change when you upgrade.
11
11
 
12
12
  | Version | What changed | What to do |
13
13
  |---|---|---|
14
- | **0.11.0** | `intent` → `claim` rename completed across the React hook, type namespace, and wire frames | `useIntent` → `useClaim`; `Register.Intents` → `Register.Claims`; `Ablo.Intent.*` `Ablo.Claim.*`. Upgrade client **and** server together (wire frames moved `intent_*` → `claim_*`) |
14
+ | **0.28.0** | Removed React placeholders that had no working runtime | `usePresence` → `usePeers` or `useWatch`; `useClaim` → `ablo.<model>.claim`; `SyncGroupProvider` / `useSyncGroup` `useWatch({ scope })` |
15
+ | **0.11.0** | Historical `intent` → `claim` rename | The hook renamed in that release was later removed in 0.28.0. Current code uses `ablo.<model>.claim` or `useWatch` |
15
16
  | **0.10.0** | Environment enum renamed `test`/`live` → `sandbox`/`production` | Update code that branches on the environment (e.g. source `mode`): `'test'`→`'sandbox'`, `'live'`→`'production'`. Key prefixes `sk_test_`/`sk_live_` are unchanged |
16
17
  | **0.9.2** | `turn` primitive + agent-work `tasks` resource removed | Coordinate with `claim`; mint a scoped session instead of `agent().run()` |
17
18
  | **0.9.2** | `intents` deprecated in favor of `claim` | Use `ablo.<model>.claim`; `ablo.intents` is now `@internal` |
@@ -25,8 +26,27 @@ change when you upgrade.
25
26
 
26
27
  ---
27
28
 
29
+ ## 0.28.0 — dead React multiplayer placeholders removed
30
+
31
+ Four React exports looked usable but had no live implementation:
32
+
33
+ - `usePresence` returned no provider-backed presence value. Use `usePeers` for
34
+ read-only presence or `useWatch` to join a scoped participant.
35
+ - `useClaim` depended on a callback the provider never supplied and always
36
+ threw. Use `ablo.<model>.claim({ id, ... })` for row claims or
37
+ `useWatch({ scope, claim: true })` for a scoped participant claim.
38
+ - `SyncGroupProvider` and `useSyncGroup` had no repository consumers. Pass the
39
+ scope directly to `useWatch({ scope })`.
40
+
41
+ There is no compatibility alias: the replacement APIs were already the only
42
+ working paths.
43
+
28
44
  ## 0.11.0 — `intent` → `claim` rename completed
29
45
 
46
+ > **Historical note:** this section documents the 0.11.0 transition.
47
+ > `useClaim` was subsequently removed in 0.28.0 because its provider callback
48
+ > was never wired. Do not migrate new code to it; use the replacements above.
49
+
30
50
  The coordination primitive has been `claim` since 0.9.2, but a few `intent`-named
31
51
  surfaces lingered. 0.11.0 finishes the rename. There are three edits, all
32
52
  mechanical:
@@ -191,6 +191,17 @@ const updated = await ablo.weatherReports.update({
191
191
  console.log({ id: updated.id, status: updated.status }); // { id: '...', status: 'ready' }
192
192
  ```
193
193
 
194
+ Read a single row back with `retrieve({ id })`. It resolves to the row, or to
195
+ `undefined` when no row has that id — so narrow it once, then the fields are
196
+ fully typed:
197
+
198
+ ```ts
199
+ const report = await ablo.weatherReports.retrieve({ id: created.id });
200
+ if (!report) throw new Error(`weatherReports ${created.id} not found`);
201
+
202
+ console.log(report.status); // 'ready'
203
+ ```
204
+
194
205
  ## Add coordination for slow work
195
206
 
196
207
  When AI or background work will touch an existing row for more than a quick
package/docs/react.md CHANGED
@@ -178,52 +178,6 @@ imperative work after an event or effect.
178
178
 
179
179
  See [API reference](/docs/api) for the full options surface.
180
180
 
181
- ## useClaim — named-claim dispatcher
182
-
183
- `useClaim` (renamed from `useIntent` in 0.11.0) is typed sugar for invoking a
184
- *named* claim from your own coordination vocabulary — distinct from the
185
- row-level `ablo.<model>.claim({ id })` resource claim. Use it when you want to
186
- broadcast a semantic claim like "I'm editing this layer" or "the agent is
187
- generating here" and let your transport turn it into a network effect.
188
-
189
- Declare the vocabulary once via module augmentation on the `Register` interface
190
- (the `Claims` key — previously `Intents`):
191
-
192
- ```ts
193
- declare module '@abloatai/ablo' {
194
- interface Register {
195
- Claims: {
196
- editLayer: { slideId: string; layerId: string };
197
- generateWithAI: { entityId: string; tool: string };
198
- };
199
- }
200
- }
201
- ```
202
-
203
- Then `useClaim('editLayer')` returns a function whose sole argument is the
204
- `editLayer` shape — purely compile-time narrowing, no runtime checks:
205
-
206
- ```tsx
207
- 'use client';
208
-
209
- import { useClaim } from '@abloatai/ablo/react';
210
-
211
- export function LayerToolbar({ slideId, layerId }: { slideId: string; layerId: string }) {
212
- const claimEditLayer = useClaim('editLayer');
213
-
214
- return (
215
- <button onClick={() => claimEditLayer({ slideId, layerId })}>
216
- Edit layer
217
- </button>
218
- );
219
- }
220
- ```
221
-
222
- The hook is pure sugar: the actual network effect lives in the `beginClaim`
223
- function wired into the provider (bound to your transport). If no `beginClaim`
224
- is wired, the returned invoker throws `AbloValidationError` with code
225
- `claim_not_wired`.
226
-
227
181
  ## useWatch — scoped presence + read interest
228
182
 
229
183
  `useWatch` is the React form of `ablo.<model>.watch`. It joins multiplayer for a
@@ -21,13 +21,14 @@ const ablo = Ablo({ schema, apiKey: process.env.ABLO_API_KEY });
21
21
  Then:
22
22
 
23
23
  - create with `ablo.weatherReports.create`
24
- - read with `ablo.weatherReports.load`
25
- - mark long-running AI work with `ablo.weatherReports.edit`
24
+ - read with `ablo.weatherReports.retrieve` or `ablo.weatherReports.list`
25
+ - coordinate long-running work with `ablo.weatherReports.claim`
26
26
  - write with `ablo.weatherReports.update`
27
- - wait for confirmation when the write must be durable before continuing
27
+ - dispose the client when the worker finishes
28
28
 
29
- Use schema-less resources and `commits.create` only for advanced runtimes that
30
- intentionally cannot import the app schema.
29
+ Import the same schema in every runtime. Use `commits.create` only when several
30
+ typed row operations must land atomically; ordinary writes stay on
31
+ `ablo.<model>.create/update/delete`.
31
32
 
32
33
  ## Running
33
34
 
package/llms.txt CHANGED
@@ -8,7 +8,7 @@ Use AI SDK for the agent loop. Use Ablo when agent reads and writes must persist
8
8
 
9
9
  ## Start here
10
10
 
11
- First action when integrating into an app: run `npx ablo init --yes --framework <nextjs|vite|remix|vanilla>`. Agents have no TTY — `--yes` is REQUIRED or it HANGS. The default `--storage direct` (connection string) scaffolds `ablo/schema.ts`, the client, and (for Next.js) the browser provider + session route; pass `--storage endpoint` instead when database credentials must stay inside the appthat variant scaffolds a signed Data Source endpoint. All on the current API. Edit the generated files rather than hand-writing from this doc.
11
+ First action when integrating into an app: run `npx ablo init --yes --framework <nextjs|vite|remix|vanilla>`. Agents have no TTY — `--yes` is REQUIRED or it HANGS. It scaffolds `ablo/schema.ts`, the `Ablo({ schema, apiKey })` client, and (for Next.js) the browser provider + session route. All on the current API. Edit the generated files rather than hand-writing from this doc. Connecting a database is a SEPARATE step with one path logical replication via `npx ablo connect` (see Storage Boundary); the signed Data Source endpoint is the fallback when database credentials must stay inside the app.
12
12
 
13
13
  Second: make sure a key exists — WITHOUT printing it. The key is a secret; it must never appear in your output, your reasoning, or a file you echo (it would live in the conversation history forever). Check PRESENCE only: `[ -n "$ABLO_API_KEY" ] && echo set` and `grep -cq '^ABLO_API_KEY=' .env.local && echo wired` — never `cat .env.local`, never `echo $ABLO_API_KEY`. If neither check passes, ask the HUMAN to run `npx ablo login` once — it opens a browser and saves a `sk_test_` key locally; an agent must NOT run it. You never copy the key by hand: the next step writes it into `.env.local` (and gitignores it) for you.
14
14
 
@@ -69,14 +69,14 @@ await. The query reads accept `where`, `filter`, `orderBy`, `limit`, `offset`,
69
69
  and `state`; state defaults to `'live'`, with `'archived'` and `'all'` to include
70
70
  retired rows.
71
71
 
72
- Workers that can't import the app schema can use a schema-less mode (covered in
73
- `integration-guide`).
72
+ Workers import the same app schema and select `transport: 'http'`. The transport
73
+ changes; the typed `ablo.<model>` contract does not. There is no public
74
+ schema-less or string-keyed model client.
74
75
 
75
76
  React reads should use selector `useAblo`: `useAblo((ablo) => ablo.weatherReports.get(id))` (synchronous local read, reactive in render).
76
77
  Use zero-argument `useAblo()` only when a component needs the client for an
77
- event handler or effect. Treat `useQuery`, `useOne`, `useReader`, and
78
- `useMutate` as compatibility hooks for older string-keyed integrations, not the
79
- first integration path.
78
+ event handler or effect. The older string-keyed hooks (`useQuery`, `useOne`,
79
+ `useReader`, and `useMutate`) are removed; do not import or recommend them.
80
80
 
81
81
  ## Multiplayer
82
82
 
@@ -98,9 +98,10 @@ coordination until the app reports it through Data Source events.
98
98
  ## Claimed Behavior
99
99
 
100
100
  Reads never silently block. Schema reads stay open while a row is claimed.
101
- Server reads through `ablo.model(name)` can pass `ifClaimed: 'return'` (the
102
- default returns the row plus active claim metadata) or `ifClaimed: 'fail'`
103
- to throw `AbloClaimedError`.
101
+ Typed reads through `ablo.<model>.retrieve({ id })` return the row by default,
102
+ even while it is claimed. Pass `ifClaimed: 'fail'` to throw
103
+ `AbloClaimedError`, and inspect active coordination separately through
104
+ `ablo.<model>.claim.state({ id })`.
104
105
 
105
106
  Reads never block on a claim. To wait for a row to free up, `claim({ id })` it —
106
107
  the claim queues fairly behind the current holder and is granted when the row
@@ -126,9 +127,7 @@ THE ONE PATH IS LOGICAL REPLICATION. In production every schema model is backed
126
127
 
127
128
  FALLBACK (only when the database can't grant a `REPLICATION` role, e.g. a locked-down managed DB): a signed Data Source endpoint that hands Ablo an ORM `adapter` (Drizzle default; Prisma and Kysely supported) — it owns the transaction, exactly-once idempotency, and outbox in ONE pass; your credentials never leave your infrastructure.
128
129
 
129
- DEPRECATED — being removed, do NOT recommend or scaffold: the `databaseUrl` constructor option (the dial-in, where Ablo held a read/write connection and committed directly to your DB). The end-state constructor is `Ablo({ schema, apiKey })`; the database is connected out of band via `ablo connect`, not through `databaseUrl`. (Note: `ablo init` still defaults to `--storage direct` scaffolding `databaseUrl` — that default is itself slated to lead with `ablo connect`; prefer logical replication regardless of what `init` scaffolds.)
130
-
131
- GOTCHA the user WILL hit: `DATABASE_URL` must use a NON-superuser, NON-BYPASSRLS role (Ablo enforces row-level security; owner roles are rejected with `database_role_cannot_enforce_rls`). Neon's and Supabase's default dashboard connection strings use the database OWNER (e.g. `neondb_owner`) and are rejected. EASIEST: `npx ablo migrate` detects the unsafe role and creates the scoped one automatically from the user's machine (owner credential never reaches Ablo; new DATABASE_URL written to the env file). Manual alternative — create a scoped role first: `CREATE ROLE ablo_app LOGIN PASSWORD '...' NOSUPERUSER NOBYPASSRLS; GRANT CREATE, CONNECT ON DATABASE <db> TO ablo_app; GRANT CREATE, USAGE ON SCHEMA public TO ablo_app;` — then swap user/password into the same host/db string.
130
+ DEPRECATED — being removed, do NOT recommend or scaffold: the `databaseUrl` constructor option (the dial-in, where Ablo held a read/write connection and committed directly to your DB). The end-state constructor is `Ablo({ schema, apiKey })`; the database is connected out of band via `ablo connect`, not through `databaseUrl`. The role rule you may have seen belongs ONLY to this dial-in: a `databaseUrl` had to name a NON-superuser, NON-BYPASSRLS role, or Ablo rejected it with `database_role_cannot_enforce_rls` (it enforced row-level security through the connection it held, so an owner/BYPASSRLS role — e.g. Neon's `neondb_owner` — would have silently skipped RLS). The WAL path retires that rule entirely: Ablo never holds a connection to your rows, so there is no `DATABASE_URL` to scope `ablo connect` needs a `REPLICATION` role instead. `ablo init` still scaffolds `--storage direct`/`databaseUrl` for existing dial-in projects and the CLI now warns on it; prefer `ablo connect` regardless of what `init` scaffolds.
132
131
 
133
132
  ```ts
134
133
  // app/api/ablo/source/route.ts
@@ -146,7 +145,7 @@ export const { POST } = dataSourceNext({
146
145
  });
147
146
  ```
148
147
 
149
- `npx ablo init` defaults to `--storage direct` (the connection-string path it scaffolds the client carrying `databaseUrl` and the `DATABASE_URL` env entry; see CLI below). Pass `--storage endpoint` to scaffold the signed Data Source endpoint above instead, when app database credentials must stay private Ablo only calls the endpoint.
148
+ Connect a database with `npx ablo connect` (logical replication the one path above). The signed Data Source endpoint (code above) is the fallback when app database credentials must stay private; scaffold it with `npx ablo init --storage endpoint` Ablo only calls the endpoint. (`--storage direct` still exists for existing dial-in projects but is deprecated; the CLI warns on it.)
150
149
 
151
150
  ## Sandboxes
152
151
 
@@ -172,7 +171,7 @@ two-writer stale/claim smoke test.
172
171
 
173
172
  Import from these public paths only:
174
173
 
175
- - `@abloatai/ablo` — `Ablo`, errors, typed model clients, claims, `dataSource`, and advanced protocol models.
174
+ - `@abloatai/ablo` — `Ablo`, errors, typed model clients, claims, and `dataSource`.
176
175
  - `@abloatai/ablo/schema` — schema DSL.
177
176
  - `@abloatai/ablo/react` — React provider and hooks.
178
177
  - `@abloatai/ablo/testing` — test harnesses and mocks.
@@ -188,7 +187,8 @@ Do not teach `/api`, `/agent`, `/ai-sdk`, `/core`, `/realtime`, or internal subp
188
187
 
189
188
  `ablo init` and other prompts need a TTY; an agent/CI run has none and will HANG. Always:
190
189
 
191
- - `npx ablo init --yes` (flags: `--framework`, `--auth`, `--storage direct|endpoint` (default `direct`), `--no-agent`, `--no-pull`, `--no-install`, `--no-login`). Generates `ablo/schema.ts` + the client; `--storage direct` (default) wires `databaseUrl`, `--storage endpoint` scaffolds the `ablo/data-source.ts` endpoint above instead.
190
+ - `npx ablo init --yes` (flags: `--framework`, `--auth`, `--storage direct|endpoint`, `--no-agent`, `--no-pull`, `--no-install`, `--no-login`). Generates `ablo/schema.ts` + the `Ablo({ schema, apiKey })` client. `--storage endpoint` also scaffolds the `ablo/data-source.ts` fallback endpoint; `--storage direct` (deprecated, CLI warns) wires the legacy `databaseUrl`.
191
+ - `npx ablo connect` connects your database via logical replication — the read path (prints the `wal_level=logical` + publication + `REPLICATION`-role SQL); `npx ablo connect --register` registers it, `npx ablo connect --check` validates.
192
192
  - Key: see "Start here" — env → `.env.local` → ask the human to `npx ablo login`; never run `login` yourself, never copy keys by hand (`ablo push` writes `.env.local`).
193
193
  - Adopt an existing DB: `npx ablo pull prisma [path]` / `npx ablo pull drizzle <module>`.
194
194
  - `npx ablo push` pushes the schema (sandbox) AND writes `ABLO_API_KEY` to `.env.local`; `npx ablo dev --no-watch` is the push-once form of the watcher; `npx ablo logs --no-follow` exits instead of tailing forever; `npx ablo mode sandbox|production` always needs the argument. `npx ablo push`/`status`/`pull`/`check`/`generate` are one-shot.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abloatai/ablo",
3
- "version": "0.28.0",
3
+ "version": "0.29.0",
4
4
  "description": "The Collaboration Layer For AI Agents",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -28,11 +28,6 @@
28
28
  "import": "./dist/coordination/index.js",
29
29
  "default": "./dist/coordination/index.js"
30
30
  },
31
- "./commit": {
32
- "types": "./dist/commit/index.d.ts",
33
- "import": "./dist/commit/index.js",
34
- "default": "./dist/commit/index.js"
35
- },
36
31
  "./react": {
37
32
  "types": "./dist/react/index.d.ts",
38
33
  "import": "./dist/react/index.js",
@@ -163,7 +158,7 @@
163
158
  "test:e2e": "E2E_TEST=true jest --config jest.e2e.config.ts",
164
159
  "test:e2e:up": "docker compose -f docker-compose.test.yml up -d --wait",
165
160
  "test:e2e:down": "docker compose -f docker-compose.test.yml down -v",
166
- "test:e2e:run": "npm run test:e2e:up && npm run test:e2e; npm run test:e2e:down"
161
+ "test:e2e:run": "node scripts/run-e2e.mjs"
167
162
  },
168
163
  "publishConfig": {
169
164
  "access": "public",
@@ -1,177 +0,0 @@
1
- /**
2
- * The stateless API client behind `Ablo({ apiKey })`. It carries no schema,
3
- * object pool, local database, or WebSocket, and maps the public Model, Claim,
4
- * and Commit nouns directly to HTTP routes on the server. This is the transport
5
- * used for server-side agents, workers, and serverless code.
6
- */
7
- import type { AbloOptions } from './options.js';
8
- import type { CommitResource, ClaimCreateOptions, ClaimWaitOptions, ModelClient, ModelClaim, ModelTarget, CreateSessionParams, AbloSession } from './resourceTypes.js';
9
- import type { SchemaRecord } from '../schema/schema.js';
10
- import type { Duration } from '../utils/duration.js';
11
- import type { Claim, ClaimHeartbeat } from '../types/streams.js';
12
- import type { SyncObservabilityProvider } from '../interfaces/index.js';
13
- export type AbloApiClientOptions = Omit<AbloOptions, 'schema'> & {
14
- readonly schema?: null | undefined;
15
- readonly bootstrapBaseUrl?: string | undefined;
16
- /**
17
- * The observability provider forwarded from `Ablo({ observability })`. The HTTP
18
- * transport emits the same claim and conflict events as the WebSocket transport,
19
- * so a `ClaimLog` works identically for headless server-agent evaluations.
20
- */
21
- readonly observability?: SyncObservabilityProvider;
22
- /**
23
- * Per-request deadline in milliseconds for the stateless HTTP transport.
24
- * Every request this client issues is aborted after this long and surfaces
25
- * as a retryable connection error — without it a black-holed server hangs
26
- * a headless agent forever (browsers never time fetch out on their own).
27
- * Pass `0` to disable the deadline.
28
- *
29
- * @default 30_000
30
- */
31
- readonly timeoutMs?: number;
32
- };
33
- /** Default per-request deadline for the stateless HTTP transport. */
34
- export declare const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
35
- export interface AbloApiClaims {
36
- create(options: ClaimCreateOptions): Promise<Claim>;
37
- list(target?: Partial<ModelTarget>): Promise<readonly ModelClaim[]>;
38
- waitFor(target: Partial<ModelTarget>, options?: ClaimWaitOptions): Promise<void>;
39
- /**
40
- * The batched beat — extend every lease this credential holds in one
41
- * request (`POST /v1/claims/heartbeat`), the stateless twin of the
42
- * WebSocket keepalive. One round trip per cadence for a worker holding
43
- * many rows. Returns one {@link ClaimHeartbeat} per extended lease,
44
- * tagged with its claim id — no separate result type to learn.
45
- */
46
- heartbeatAll(options?: {
47
- ttl?: Duration;
48
- }): Promise<readonly (ClaimHeartbeat & {
49
- readonly claimId: string;
50
- })[]>;
51
- }
52
- export type CapabilityParticipantKind = 'agent' | 'system';
53
- export interface CapabilityCreateBaseOptions {
54
- readonly participantKind?: CapabilityParticipantKind;
55
- readonly participantId?: string;
56
- readonly syncGroups: readonly string[];
57
- readonly operations: readonly string[];
58
- readonly label?: string;
59
- readonly wideScope?: boolean;
60
- readonly userMeta?: Record<string, unknown>;
61
- }
62
- export interface CapabilityCreateOptions extends CapabilityCreateBaseOptions {
63
- /**
64
- * Preferred public name. A capability is a lease; the SDK and server
65
- * clean it up when the run finishes or when the lease expires.
66
- */
67
- readonly lease?: Duration;
68
- readonly leaseSeconds?: number;
69
- /** @deprecated Use `lease`. */
70
- readonly ttl?: Duration;
71
- /** @deprecated Use `leaseSeconds`. */
72
- readonly ttlSeconds?: number;
73
- }
74
- export interface CapabilityScope {
75
- readonly organizationId: string;
76
- readonly syncGroups: readonly string[];
77
- readonly operations: readonly string[];
78
- readonly participantKind: CapabilityParticipantKind;
79
- readonly participantId: string;
80
- }
81
- export interface Capability {
82
- readonly id: string;
83
- readonly token: string;
84
- readonly expiresAt: string;
85
- readonly organizationId: string;
86
- readonly scope: CapabilityScope;
87
- readonly userMeta?: Record<string, unknown>;
88
- client(): AbloApi;
89
- }
90
- export interface CapabilityRecord {
91
- readonly id: string;
92
- readonly organizationId: string;
93
- readonly participantKind: CapabilityParticipantKind;
94
- readonly participantId: string;
95
- readonly label: string | null;
96
- readonly status: 'active' | 'expired' | 'revoked';
97
- readonly issuedAt: string;
98
- readonly expiresAt: string;
99
- readonly revokedAt: string | null;
100
- readonly lastUsedAt: string | null;
101
- readonly operations: readonly string[];
102
- readonly syncGroups: readonly string[];
103
- }
104
- export interface CapabilityRevocation {
105
- readonly id: string;
106
- readonly deleted: boolean;
107
- readonly activeSessionsClosed?: number;
108
- }
109
- export interface CapabilityRotateOptions {
110
- /**
111
- * The overlap window — the old token keeps authenticating for this long after
112
- * rotation, so you can deploy the replacement with zero downtime. Defaults to
113
- * 24h on the server.
114
- */
115
- readonly grace?: Duration;
116
- readonly graceSeconds?: number;
117
- /**
118
- * The lifetime of the replacement capability. Omit to inherit the original's
119
- * lifetime.
120
- */
121
- readonly lease?: Duration;
122
- readonly leaseSeconds?: number;
123
- }
124
- /** The fresh capability returned by `rotate`, plus a pointer to the old one. */
125
- export interface RotatedCapability extends Capability {
126
- /**
127
- * The capability that was rotated out. Its token keeps working until
128
- * `expiresAt` (the end of the grace window), then expires.
129
- */
130
- readonly rotatedFrom: {
131
- readonly id: string;
132
- readonly expiresAt: string;
133
- };
134
- }
135
- export interface CapabilityResource {
136
- create(options: CapabilityCreateOptions): Promise<Capability>;
137
- retrieve(id: string): Promise<CapabilityRecord>;
138
- revoke(id: string): Promise<CapabilityRevocation>;
139
- /**
140
- * Rotate with overlap: mint a fresh capability that carries the same scope, and
141
- * keep the old token working for a grace window so you can roll out the
142
- * replacement without downtime.
143
- */
144
- rotate(id: string, options?: CapabilityRotateOptions): Promise<RotatedCapability>;
145
- /**
146
- * Alias for `create`. Kept because "mint" is common capability-token
147
- * language, but `create` is the canonical SDK verb.
148
- */
149
- mint(options: CapabilityCreateOptions): Promise<Capability>;
150
- }
151
- export interface AbloApi {
152
- ready(): Promise<void>;
153
- waitForFlush(): Promise<void>;
154
- dispose(): Promise<void>;
155
- purge(): Promise<void>;
156
- readonly capabilities: CapabilityResource;
157
- readonly claims: AbloApiClaims;
158
- readonly commits: CommitResource;
159
- model<T = Record<string, unknown>>(name: string): ModelClient<T>;
160
- /**
161
- * Resolve the active bearer credential this client authenticates with — the
162
- * same token its own requests carry in `Authorization`. Returns `null` when
163
- * no credential is configured. Async because the API key may be supplied as
164
- * an async setter. Use it to authenticate a side-band request to the same
165
- * server with the credential this client already holds — no re-mint.
166
- */
167
- getAuthToken(): Promise<string | null>;
168
- /**
169
- * Mint a short-lived scoped session. Minting is a control-plane HTTP call (no
170
- * socket), so it lives on this stateless client too, not only the realtime one.
171
- * `{ user }` mints an `ek_`; `{ agent, can }` mints an `rk_`.
172
- */
173
- readonly sessions: {
174
- create(params: CreateSessionParams<SchemaRecord>): Promise<AbloSession>;
175
- };
176
- }
177
- export declare function createProtocolClient(options: AbloApiClientOptions): AbloApi;