@abloatai/ablo 0.11.2 → 0.13.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 (70) hide show
  1. package/AGENTS.md +2 -2
  2. package/CHANGELOG.md +34 -0
  3. package/README.md +3 -3
  4. package/dist/ai-sdk/claim-broadcast.d.ts +4 -3
  5. package/dist/ai-sdk/claim-broadcast.js +2 -2
  6. package/dist/ai-sdk/wrap.d.ts +5 -4
  7. package/dist/ai-sdk/wrap.js +3 -3
  8. package/dist/cli.cjs +152 -41
  9. package/dist/client/Ablo.d.ts +25 -3
  10. package/dist/client/Ablo.js +5 -5
  11. package/dist/client/ApiClient.js +26 -11
  12. package/dist/client/createModelProxy.d.ts +15 -7
  13. package/dist/client/createModelProxy.js +12 -12
  14. package/dist/coordination/schema.d.ts +1 -1
  15. package/dist/coordination/schema.js +3 -1
  16. package/dist/errors.d.ts +3 -1
  17. package/dist/errors.js +6 -1
  18. package/dist/react/AbloProvider.d.ts +11 -7
  19. package/dist/react/AbloProvider.js +9 -5
  20. package/dist/react/context.d.ts +9 -14
  21. package/dist/react/context.js +10 -15
  22. package/dist/react/index.d.ts +8 -4
  23. package/dist/react/index.js +8 -4
  24. package/dist/react/useMutators.js +3 -2
  25. package/dist/react/useUndoScope.js +3 -2
  26. package/dist/schema/index.d.ts +2 -2
  27. package/dist/schema/index.js +2 -2
  28. package/dist/schema/model.d.ts +38 -77
  29. package/dist/schema/model.js +12 -12
  30. package/dist/schema/roles.d.ts +49 -0
  31. package/dist/schema/roles.js +21 -0
  32. package/dist/schema/schema.d.ts +1 -1
  33. package/dist/schema/schema.js +1 -1
  34. package/dist/schema/serialize.d.ts +4 -2
  35. package/dist/schema/serialize.js +4 -2
  36. package/dist/schema/sugar.d.ts +7 -28
  37. package/dist/schema/sugar.js +2 -7
  38. package/dist/schema/sync-delta-row.d.ts +2 -0
  39. package/dist/schema/sync-delta-row.js +2 -1
  40. package/dist/schema/tenancy.d.ts +67 -28
  41. package/dist/schema/tenancy.js +93 -23
  42. package/dist/server/commit.d.ts +8 -3
  43. package/dist/sync/createClaimStream.js +5 -4
  44. package/dist/sync/participants.js +1 -1
  45. package/dist/types/streams.d.ts +17 -7
  46. package/docs/api.md +1 -1
  47. package/docs/cli.md +43 -4
  48. package/docs/client-behavior.md +2 -2
  49. package/docs/coordination.md +1 -1
  50. package/docs/examples/agent-human.md +6 -6
  51. package/docs/examples/ai-sdk-tool.md +1 -1
  52. package/docs/examples/existing-python-backend.md +0 -2
  53. package/docs/examples/nextjs.md +2 -2
  54. package/docs/examples/scoped-agent.md +3 -3
  55. package/docs/examples/server-agent.md +4 -4
  56. package/docs/identity.md +27 -20
  57. package/docs/index.md +0 -1
  58. package/docs/integration-guide.md +12 -9
  59. package/docs/interaction-model.md +1 -1
  60. package/docs/mcp.md +17 -5
  61. package/docs/migration.md +2 -1
  62. package/docs/quickstart.md +3 -3
  63. package/llms.txt +2 -3
  64. package/package.json +3 -2
  65. package/docs/mcp/claude-code.md +0 -35
  66. package/docs/mcp/cursor.md +0 -35
  67. package/docs/mcp/windsurf.md +0 -33
  68. package/docs/roadmap.md +0 -55
  69. package/docs/the-loop.md +0 -21
  70. package/llms-full.txt +0 -396
@@ -142,20 +142,23 @@ model(
142
142
  },
143
143
  /* relations */ {},
144
144
  {
145
- // Rows carry organization_id and bootstrap filters on it.
146
- orgScoped: true,
145
+ // Axis 1 `policy`: who may READ a row (tenant isolation / RLS). A
146
+ // row-local `organization_id` column is the default, so you omit this for
147
+ // normal tables; set it only for the exceptions (parent-inherited / global).
147
148
 
149
+ // Axis 2 — `groups`: which sync-group CHANNELS a row fans into.
148
150
  // Scope root: rows form the group `matter:<id>`. Children point at it with
149
151
  // `relation.belongsTo('matters', 'matterId', { parent: true })` to inherit.
150
- scope: 'matter',
152
+ groups: { root: 'matter' },
151
153
  }
152
154
  );
153
155
  ```
154
156
 
155
- For rows that don't carry `organization_id` themselves but inherit
156
- tenancy via a foreign key, use `scopedVia` instead of `orgScoped:
157
- false` the latter exposes the entire table cross-tenant. See
158
- `packages/sync-engine/src/schema/model.ts` for the full option set.
157
+ For rows that don't carry `organization_id` themselves but inherit tenancy via a
158
+ foreign key, set `policy: { by: 'parent', fk: '<fk>', parent: '<parentTable>' }`.
159
+ For genuinely global/reference data, `policy: { by: 'none' }`. ⚠ `by: 'none'`
160
+ exposes the whole table cross-tenant, so it's an explicit, named branch — never a
161
+ falsy flag. See `packages/sync-engine/src/schema/model.ts` for the full option set.
159
162
 
160
163
  ## 2. Create The Client
161
164
 
@@ -444,7 +447,7 @@ scope.
444
447
  ```ts
445
448
  await using claim = await ablo.weatherReports.claim({
446
449
  id: reportId,
447
- action: 'forecasting',
450
+ reason: 'forecasting',
448
451
  });
449
452
  const claimed = claim.data;
450
453
  if (!claimed) return;
@@ -510,7 +513,7 @@ them.
510
513
  | `update({ id, data, ...opts })` | Update through the model client. |
511
514
  | `delete({ id, ...opts })` | Delete through the model client. |
512
515
  | `claim.state({ id })` | See who is currently working on a row (synchronous). |
513
- | `claim({ id, action?, ttl? })` | Acquire a disposable handle: wait for your turn, re-read, and hold the row. |
516
+ | `claim({ id, reason?, ttl? })` | Acquire a disposable handle: wait for your turn, re-read, and hold the row. |
514
517
 
515
518
  Keep first integrations on the model methods above. Every mutation and
516
519
  server-read verb takes one options object; only the synchronous `get(id)` stays
@@ -70,7 +70,7 @@ automatically when the scope exits:
70
70
  ```ts
71
71
  await using claim = await ablo.weatherReports.claim({
72
72
  id: 'report_stockholm',
73
- action: 'editing',
73
+ reason: 'editing',
74
74
  });
75
75
  await ablo.weatherReports.update({ id: claim.data.id, data: { status: 'ready' } }); // rejected if the row changed under the claim
76
76
  ```
package/docs/mcp.md CHANGED
@@ -67,11 +67,23 @@ Point your assistant at the hosted endpoint — no auth, no token:
67
67
  claude mcp add --transport http ablo https://<your-app>/api/mcp
68
68
  ```
69
69
 
70
- Per-client walkthroughs:
70
+ The endpoint is identical for every client — only the config surface differs:
71
71
 
72
- - [Claude Code](/docs/mcp/claude-code)
73
- - [Cursor](/docs/mcp/cursor)
74
- - [Windsurf](/docs/mcp/windsurf)
72
+ - **Claude Code** — run the `claude mcp add` command above; verify with `/mcp list`, remove with `claude mcp remove ablo`.
73
+ - **Cursor** — add the server to `~/.cursor/mcp.json` (macOS / Linux), then restart.
74
+ - **Windsurf** — add the same JSON via Settings → Cascade → MCP, then restart.
75
+
76
+ Cursor and Windsurf use the same config shape:
77
+
78
+ ```json
79
+ {
80
+ "mcpServers": {
81
+ "ablo": { "transport": "http", "url": "https://<your-app>/api/mcp" }
82
+ }
83
+ }
84
+ ```
85
+
86
+ Each client then lists the Ablo tools (`search_ablo_docs`, `get_recipe`, `get_api_surface`, `validate_schema`, `scaffold_app`) in its MCP panel.
75
87
 
76
88
  ### What it exposes
77
89
 
@@ -96,7 +108,7 @@ loading everything into context.
96
108
  Reusable, parameterised templates that drive an end-to-end flow:
97
109
 
98
110
  - `integrate-sync-engine` — wire the SDK into an existing project.
99
- - `add-agent` — add an agent worker that coordinates via intents and
111
+ - `add-agent` — add an agent worker that coordinates via claims and
100
112
  conflict-safe writes.
101
113
  - `define-schema` — design a Zod-first schema from a description, then run
102
114
  `validate_schema` before committing.
package/docs/migration.md CHANGED
@@ -284,7 +284,8 @@ One provider component now owns the full React lifecycle. `<SyncProvider>`,
284
284
  - <SyncProvider store={sync._store} organizationId={orgId}>
285
285
  - <AbloProvider ablo={ablo}>{children}</AbloProvider>
286
286
  - </SyncProvider>
287
- + <AbloProvider schema={schema} url={url} userId={userId} organizationId={orgId}>
287
+ + const ablo = Ablo({ schema, apiKey });
288
+ + <AbloProvider client={ablo}>
288
289
  + {children}
289
290
  + </AbloProvider>
290
291
  ```
@@ -222,7 +222,7 @@ Call `handle.release()` when your work is done.
222
222
  // Claim the row so other participants serialize behind us while we work.
223
223
  const handle = await ablo.weatherReports.claim({
224
224
  id: 'weather_stockholm',
225
- action: 'checking_weather',
225
+ reason: 'checking_weather',
226
226
  ttl: '2m',
227
227
  });
228
228
 
@@ -260,7 +260,7 @@ write through the model.
260
260
  ```ts
261
261
  const active = ablo.weatherReports.claim.state({ id: 'weather_stockholm' });
262
262
  if (active) {
263
- console.log(`${active.heldBy} is ${active.action}`);
263
+ console.log(`${active.heldBy} is ${active.reason}`);
264
264
  }
265
265
 
266
266
  const handle = await ablo.weatherReports.claim({ id: 'weather_stockholm' });
@@ -268,7 +268,7 @@ await ablo.weatherReports.update({ id: handle.data.id, data: { status: 'ready' }
268
268
  await handle.release();
269
269
  ```
270
270
 
271
- Use `{ wait: false }` on `claim` when work should be skipped instead of queued
271
+ Use `{ queue: false }` on `claim` when work should be skipped instead of queued
272
272
  behind an active holder.
273
273
 
274
274
  ## Next steps
package/llms.txt CHANGED
@@ -12,7 +12,7 @@ First action when integrating into an app: run `npx ablo init --yes --framework
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
 
15
- Then PUSH — this is the step everything depends on. The server keeps its OWN copy of the schema. Run `npx ablo push --no-watch`: it pushes `ablo/schema.ts` (sandbox) AND writes `ABLO_API_KEY` into `.env.local` from the stored login. Until the schema is pushed, EVERY write to a new or changed model fails with `server_execute_unknown_model`. Re-run it after schema changes (`npx ablo push` also works once the key is wired; bare `npx ablo push` watches forever don't, you have no TTY).
15
+ Then PUSH — this is the step everything depends on. The server keeps its OWN copy of the schema. Run `npx ablo push`: it pushes `ablo/schema.ts` (sandbox) AND writes `ABLO_API_KEY` into `.env.local` from the stored login. Until the schema is pushed, EVERY write to a new or changed model fails with `server_execute_unknown_model`. Re-run it after schema changes. `push` is one-shot; `dev` is the watcher, so use `npx ablo dev --no-watch` only when you intentionally want the dev command to push once and exit.
16
16
 
17
17
  ## Projects (one org, many apps)
18
18
 
@@ -29,7 +29,6 @@ TYPES: the project registers its schema ONCE via declaration merging — `npx ab
29
29
 
30
30
  const schema = defineSchema({
31
31
  weatherReports: model({
32
- id: z.string(),
33
32
  location: z.string(),
34
33
  status: z.enum(['pending', 'ready']),
35
34
  forecast: z.string().optional(),
@@ -190,6 +189,6 @@ Do not teach `/api`, `/agent`, `/ai-sdk`, `/core`, `/realtime`, or internal subp
190
189
  - `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.
191
190
  - 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`).
192
191
  - Adopt an existing DB: `npx ablo pull prisma [path]` / `npx ablo pull drizzle <module>`.
193
- - `npx ablo push --no-watch` pushes the schema (sandbox) AND writes `ABLO_API_KEY` to `.env.local` (default watches forever); `npx ablo logs --no-follow` (default tails forever); `npx ablo mode sandbox|production` (always pass the arg). `npx ablo push`/`status`/`pull`/`check`/`generate` are one-shot.
192
+ - `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.
194
193
 
195
194
  Canonical docs to read before integrating: `quickstart`, `schema-contract`, `integration-guide`, `guarantees`, `client-behavior`, `data-sources`, `examples/existing-python-backend`, `api`, `examples/ai-sdk-tool`, and `examples/server-agent`. When upgrading an existing integration, read `migration` — every breaking change, what to change, and which version introduced it.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abloatai/ablo",
3
- "version": "0.11.2",
3
+ "version": "0.13.0",
4
4
  "description": "The Collaboration Layer For AI Agents",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -119,7 +119,6 @@
119
119
  "examples",
120
120
  "AGENTS.md",
121
121
  "llms.txt",
122
- "llms-full.txt",
123
122
  "LICENSE",
124
123
  "NOTICE",
125
124
  "README.md",
@@ -131,9 +130,11 @@
131
130
  "build:cli": "tsup --config tsup.cli.config.ts",
132
131
  "typecheck:cli": "tsc -p tsconfig.cli.json",
133
132
  "pack:check": "npm_config_cache=${TMPDIR:-/tmp}/ablo-npm-cache npm pack --dry-run",
133
+ "lint": "npm run lint:imports && npm run lint:errors && npm run lint:docs",
134
134
  "lint:imports": "node scripts/check-js-extensions.mjs",
135
135
  "generate:errors": "tsx scripts/generate-error-docs.mts",
136
136
  "lint:errors": "tsx scripts/check-error-docs.mts",
137
+ "lint:docs": "node scripts/check-doc-drift.mjs",
137
138
  "lint:pkg": "publint",
138
139
  "prepublishOnly": "npm run build && npm run lint:pkg",
139
140
  "check:dist": "node scripts/check-dist-fresh.mjs",
@@ -1,35 +0,0 @@
1
- # Claude Code
2
-
3
- ## Install
4
-
5
- ```bash
6
- claude mcp add --transport http ablo https://<your-app>/api/mcp
7
- ```
8
-
9
- That's it — no token or header needed. The endpoint is public and serves
10
- only docs, schema lint, and scaffolds. The next `/help` in Claude Code will
11
- list the Ablo Sync tools.
12
-
13
- ## Verify
14
-
15
- In Claude Code, run:
16
-
17
- ```
18
- /mcp list
19
- ```
20
-
21
- You should see `ablo` with the integration tools enumerated:
22
- `search_ablo_docs`, `get_recipe`, `get_api_surface`, `validate_schema`,
23
- `scaffold_app`.
24
-
25
- ## Removing
26
-
27
- ```bash
28
- claude mcp remove ablo
29
- ```
30
-
31
- ## More
32
-
33
- - [MCP overview](/docs/mcp) — what the server exposes and how the transport works.
34
- - [Cursor setup](/docs/mcp/cursor) — same URL, different UI.
35
- - [Windsurf setup](/docs/mcp/windsurf) — same URL, different UI.
@@ -1,35 +0,0 @@
1
- # Cursor
2
-
3
- ## Install
4
-
5
- Add the Ablo Sync MCP server to Cursor's `mcp.json`:
6
-
7
- ```json
8
- {
9
- "mcpServers": {
10
- "ablo": {
11
- "transport": "http",
12
- "url": "https://<your-app>/api/mcp"
13
- }
14
- }
15
- }
16
- ```
17
-
18
- The file lives at `~/.cursor/mcp.json` on macOS / Linux. No auth header is
19
- needed — the endpoint is public and serves only docs, schema lint, and
20
- scaffolds.
21
-
22
- Restart Cursor. The Ablo Sync tools appear under the MCP icon in the agent
23
- panel.
24
-
25
- ## Verify
26
-
27
- In Cursor's agent panel, open the MCP tools list. You should see the
28
- Ablo Sync integration tools and their JSON schemas: `search_ablo_docs`,
29
- `get_recipe`, `get_api_surface`, `validate_schema`, `scaffold_app`.
30
-
31
- ## More
32
-
33
- - [MCP overview](/docs/mcp) — what the server exposes and how the transport works.
34
- - [Claude Code setup](/docs/mcp/claude-code) — CLI install.
35
- - [Windsurf setup](/docs/mcp/windsurf) — same JSON shape.
@@ -1,33 +0,0 @@
1
- # Windsurf
2
-
3
- ## Install
4
-
5
- Add the Ablo Sync MCP server to Windsurf's MCP config:
6
-
7
- ```json
8
- {
9
- "mcpServers": {
10
- "ablo": {
11
- "transport": "http",
12
- "url": "https://<your-app>/api/mcp"
13
- }
14
- }
15
- }
16
- ```
17
-
18
- The config path differs by platform — Windsurf surfaces it in Settings →
19
- Cascade → MCP. Restart Windsurf after saving. No auth header is needed —
20
- the endpoint is public and serves only docs, schema lint, and scaffolds.
21
-
22
- ## Verify
23
-
24
- Cascade's MCP panel lists every configured server with its tools. You
25
- should see `ablo` with the integration tools enumerated:
26
- `search_ablo_docs`, `get_recipe`, `get_api_surface`, `validate_schema`,
27
- `scaffold_app`.
28
-
29
- ## More
30
-
31
- - [MCP overview](/docs/mcp) — what the server exposes and how the transport works.
32
- - [Claude Code setup](/docs/mcp/claude-code) — CLI install.
33
- - [Cursor setup](/docs/mcp/cursor) — same JSON shape.
package/docs/roadmap.md DELETED
@@ -1,55 +0,0 @@
1
- # Roadmap
2
-
3
- What is shipped, what is next, and what we will not build.
4
-
5
- ## Shipped
6
-
7
- - **Models, claims, commits** — the core API.
8
- - **Audit log** — a tamper-evident, hash-chained record of who did what,
9
- per principal.
10
- - **MCP transport** — HTTP server at `/api/mcp`.
11
- - **TypeScript SDK** — `@abloatai/ablo`, with React bindings.
12
- - **Dashboard** — keys, audit, metrics, allowed origins.
13
- - **Schema migrations** — change a model's shape after it already has data.
14
- Ablo plans the migration, tells you which changes are safe to auto-apply,
15
- and backfills the rest; the server applies and activates a migration only
16
- if it succeeds. `ablo generate` emits typed clients from a pushed schema.
17
- - **Structured errors** — every error has a stable code and a request id,
18
- and the same codes work whether you reach Ablo over HTTP, WebSocket, or MCP.
19
- - **Sync groups** — clients automatically receive only the records they have
20
- access to, based on relationships you declare in the schema.
21
- - **Agent coordination** — when several agents work on the same model, they
22
- take turns instead of overwriting each other, via `intent(id)` handles
23
- that claim, wait, and commit.
24
-
25
- ## In flight
26
-
27
- - **Real-time presence** — see who else is viewing/editing a model
28
- (coordination primitives landed; presence surface in progress).
29
- - **Cross-instance fan-out via Redis** — pub/sub deltas at scale.
30
-
31
- ## On deck
32
-
33
- - **Field-level subscriptions** — subscribe to one path, not the whole row.
34
- - **Bulk import/export** — CSV/JSON round-trip with chain verification.
35
-
36
- ## Maybe, if demand
37
-
38
- - **Python SDK** — when a customer is shipping a Python-only product.
39
- - **Go SDK** — same.
40
- - **Multi-region replication** — when latency requirements force it.
41
-
42
- ## We will not build
43
-
44
- - **A general-purpose Postgres wrapper** — Ablo is for state with
45
- concurrency semantics, not for storing every table.
46
- - **Server-side compute** — no triggers, no stored procedures. Compute
47
- belongs in your application code.
48
- - **A document database UI** — your data lives in Ablo; the UI is your
49
- product, not ours.
50
-
51
- ## How priorities shift
52
-
53
- We move items between sections based on what customers ask for in
54
- production. Filing a [feature request](/docs/contact) with a concrete use
55
- case is more effective than a thread on Twitter.
package/docs/the-loop.md DELETED
@@ -1,21 +0,0 @@
1
- # The loop: how your data flows
2
-
3
- This explainer moved to the canonical, maintained docs:
4
-
5
- **→ https://abloatai.com/docs/webhooks**
6
-
7
- The short version: Ablo has the same two-sided shape as Stripe — **you call Ablo to make changes (the client), and Ablo calls you to persist them (a signed webhook)** — plus realtime sync to every connected client.
8
-
9
- ```
10
- your app ──write──▶ Ablo (hosted) ──realtime sync──▶ other clients
11
- (the client) the transaction log (live, optimistic)
12
-
13
- └──signed event──▶ /api/ablo/[...all] ──▶ YOUR database
14
- (the webhook route)
15
- ```
16
-
17
- Ablo owns the ordered transaction log (the source of truth); your database is a
18
- materialized copy you keep via the webhook. See the link above for the full
19
- guide: scaffolding the handler (`ablo init`), local testing (`ablo dev`),
20
- registering an endpoint (`ablo webhooks create`), signature verification, the
21
- delivery/retry model, and best practices.