@abloatai/ablo 0.47.0 → 0.48.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.
package/CHANGELOG.md CHANGED
@@ -1,16 +1,128 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.48.0
4
+
5
+ ### A branch is unbound until you connect a database to it
6
+
7
+ A branch keeps its own storage, and it does not quietly get Ablo's. Until a
8
+ database is connected to that branch, a request needing one fails with
9
+ `no_data_source_registered` and says what to do:
10
+
11
+ ```
12
+ This branch is not connected to your database yet.
13
+ Run `ablo connect` for this branch, then retry.
14
+ ```
15
+
16
+ The old `test_database_not_registered` is gone. It described a sandbox that no
17
+ longer exists, it arrived on requests that had nothing to do with a test
18
+ database, and its advice pointed at options the SDK had already removed. A
19
+ branch created by `ablo dev` now reports its state plainly rather than looking
20
+ ready and then refusing the first schema push.
21
+
22
+ **Action required.** Replace any handler matching `test_database_not_registered`
23
+ with `no_data_source_registered`. The new code carries the same 4xx meaning and
24
+ a recovery path a caller can act on.
25
+
26
+ ### Failures say which branch they happened on
27
+
28
+ An unconnected branch previously returned its refusal with nothing written
29
+ server-side, so a support question about one customer's branch could not be
30
+ answered from logs at all. These now carry the request, organization, project,
31
+ branch, key kind and storage state, indexed in Sentry, so a failure can be
32
+ looked up by branch instead of reconstructed from database tables.
33
+
34
+ ### Replication uses your branch's own publication and slot
35
+
36
+ Registration, validation, writer checks, replication, drift detection and
37
+ `ablo connect` all require the branch-scoped names recorded when the source was
38
+ registered. Nothing falls back to a shared `ablo_publication` or `ablo_slot`
39
+ any more, so two branches on one database can never quietly share a stream.
40
+ `ablo connect scan` reports legacy unsuffixed objects as retired.
41
+
42
+ Once your engine is on this release and `ablo connect check` is clean, the
43
+ temporary alias can go:
44
+
45
+ ```sql
46
+ DROP PUBLICATION IF EXISTS "ablo_publication";
47
+ ```
48
+
49
+ ### Renamed
50
+
51
+ `FootprintPlane` is now `DataSourceIdentity`, with the same three fields. The
52
+ old name described an internal layout; the new one describes what it
53
+ identifies.
54
+
3
55
  ## 0.47.0
4
56
 
5
- ### Minor Changes
57
+ ### Local Postgres works with Ablo Cloud
58
+
59
+ Run `npx ablo dev --local` to serve the generated signed Data Source handler
60
+ over an outbound, protocol-scoped connector. Postgres remains private on the
61
+ developer's machine and its connection string never leaves the app process.
62
+ The Data Source guide now explains exactly which writes are visible without
63
+ WAL, and the public error reference includes actionable `source_connector_*`
64
+ codes for every connector lifecycle failure.
65
+
66
+ ### Awaiting a model write now means it is confirmed
67
+
68
+ `create`, `update`, and `delete` change local reactive state immediately and
69
+ return a promise with a single meaning: the write reached authoritative
70
+ confirmation. An interface stays responsive without awaiting anything, and code
71
+ that needs to know a write survived can await the same call it already makes.
72
+
73
+ The `wait` option is gone from the client and from individual model calls.
74
+ Awaiting a model write always waits for confirmation, so there is nothing left
75
+ to configure. Explicit control over a queued versus confirmed receipt remains on
76
+ `commits.create`, which still hands back the receipt and its confirmation
77
+ separately.
78
+
79
+ **Action required.** Remove `wait` from `Ablo({ ... })` and from every
80
+ `create`, `update`, and `delete` call.
81
+
82
+ - `wait: 'confirmed'` behaves identically once removed.
83
+ - `wait: 'queued'` on a call you never awaited behaves identically once removed.
84
+ - `wait: 'queued'` on a call you did await now waits for confirmation. Move to
85
+ `commits.create` if the queued receipt was the reason for the option.
86
+
87
+ ### Customer branches connect before accepting a schema
88
+
89
+ A customer branch now remains in provisioning until it has an active Data
90
+ Source. Ablo does not invent internal storage for customer data: it reads the
91
+ customer's database through WAL and writes through the separately scoped DML
92
+ credential (or uses the explicitly registered signed endpoint fallback).
93
+
94
+ Database validation now uses the branch-scoped publication and replication slot
95
+ persisted with that Data Source. It no longer falls back to the shared
96
+ `ablo_publication` / `ablo_slot` names, so `ablo connect check` validates the
97
+ same objects that `ablo connect apply` created.
98
+
99
+ The sandbox-only `test_database_not_registered` error has been removed. An
100
+ unconnected customer branch now consistently returns `no_data_source_registered`
101
+ with the `ablo connect` recovery step.
102
+
103
+ **Action required for type imports.** `FootprintPlane` has been removed. Import
104
+ `DataSourceIdentity` from `@abloatai/ablo/source` instead; its fields remain
105
+ `organizationId`, optional `projectId`, and `branchId`.
106
+
107
+ ### The CLI names the problem it actually hit
108
+
109
+ A refused push no longer reports every failure as a missing `schema:push`
110
+ capability. That advice was wrong for most refusals: a database privilege error,
111
+ a row-level security misconfiguration, and an unregistered development database
112
+ each need a different fix, and none of them is a different API key. Each now
113
+ leads with the server's own message and the remedy for that specific cause.
6
114
 
7
- - 101ca2c: Make schema model writes optimistic with one stable promise contract: local reactive state changes immediately, while awaiting `create`, `update`, or `delete` always waits for authoritative confirmation. Remove the model-level and client-level `wait` options; explicit queued-versus-confirmed receipt control remains on `commits.create`.
115
+ Project names also resolve correctly under a branch-bound key. Listing projects
116
+ is a management operation that such a key is deliberately not allowed to
117
+ perform, so `ablo status` and `ablo push` reported a correctly minted key's
118
+ project as `unnamed` alongside a permission error. The name now comes from the
119
+ stored management credential.
8
120
 
9
- ### Patch Changes
121
+ ### Deprecations
10
122
 
11
- - Updated dependencies [101ca2c]
12
- - @abloatai/transaction@0.47.0
13
- - @abloatai/humans@0.47.0
123
+ `METER_EVENT_COUNTS` is deprecated in favour of its per-surface members, and the
124
+ `DatasourceResnapshotResponse` type and its schema are deprecated. All three
125
+ still ship and still work; they will be removed in a later release.
14
126
 
15
127
  ## 0.46.0
16
128
 
package/README.md CHANGED
@@ -22,6 +22,10 @@
22
22
 
23
23
  ---
24
24
 
25
+ > **Reading the implementation?** Start with the
26
+ > **[source code map](./CODEMAP.md)**. It shows which files own `create`,
27
+ > `update`, `delete`, `claim`, schemas, transports, and the reactive client.
28
+
25
29
  Safely coordinate AI agents, humans, workflows, and services writing to the
26
30
  same database.
27
31
 
@@ -114,6 +118,22 @@ authority, commits, claims, and ordered changes.
114
118
  Read the [Quickstart](https://docs.abloatai.com/quickstart), browse
115
119
  [docs.abloatai.com](https://docs.abloatai.com), or run `npx ablo docs`.
116
120
 
121
+ ## Navigating the source
122
+
123
+ This repository preserves the package ownership boundaries instead of
124
+ flattening the implementation into `packages/ablo`:
125
+
126
+ - `packages/ablo` is the branded public facade. Its files mostly re-export the
127
+ package that owns each API.
128
+ - `packages/transaction` owns the shared model-operation contracts and the
129
+ stateless HTTP implementation.
130
+ - `packages/humans` owns the reactive WebSocket/local/React implementation.
131
+
132
+ That means searching only inside `packages/ablo/src` will not find the
133
+ implementation of `create`, `update`, `delete`, or `claim`. Read the
134
+ **[source code map](./CODEMAP.md)** for a verb-by-verb ownership table and
135
+ guided call traces for both the default and reactive clients.
136
+
117
137
  ## Contributing
118
138
 
119
139
  Ablo is free and open source. You can help by
package/docs/cli.md CHANGED
@@ -122,7 +122,7 @@ bypasses profiles for project/branch administration; the runtime key remains
122
122
  | `ablo init` | Scaffold `ablo/` (`schema.ts`, client, optional Data Source / agent / component), write `.env`, install the SDK. Offers to log in at the end. |: |
123
123
  | `ablo login` / `logout` / `whoami` / `status` | Authentication, exact credential identity, and readiness (above). | `whoami --key-env <NAME>`, `whoami --json`, `status --json` |
124
124
  | `ablo projects list\|create\|use\|rename` | Manage projects and the active one (see [Projects](#projects)). Each project's keys/schema/data are isolated. | `--name "<display>"` (create/rename) |
125
- | `ablo dev` | **Hosted**: ensure an isolated Git branch, wire its temporary key, push, then watch `ablo/schema.ts`. | `--branch <slug>`, `--branch-ttl-hours <1-168>`, `--no-watch`, `--schema`, `--export`, `--url` |
125
+ | `ablo dev` | Ensure an isolated Git branch, wire its temporary key, push, then watch `ablo/schema.ts`. `--local` also serves local Postgres over an outbound signed connector. | `--branch <slug>`, `--branch-ttl-hours <1-168>`, `--local`, `--source <path>`, `--no-watch`, `--schema`, `--export`, `--url` |
126
126
  | `ablo branch list\|status\|check\|create\|ensure\|credential\|delete` | Manage and diagnose immutable branch planes and expiring credentials. | Run `ablo branch --help`; use `--json` for automation. |
127
127
  | `ablo logs` | Tail the resolved runtime credential's branch activity. Follows by default. | `-n, --tail <N>`, `--since <dur\|ts>`, `--model`, `--op`, `--json`, `--no-follow` |
128
128
  | `ablo push` | **Hosted**: upload the schema to Ablo; the server diffs, migrates, and activates it. | `--force`, `--rename old:new`, `--backfill model.field=value`, `--schema`, `--export`, `--url` |
@@ -166,8 +166,16 @@ npx ablo dev # discover from Git, push + watch
166
166
  npx ablo dev --branch preview-pr-482 # explicit branch
167
167
  npx ablo dev --no-watch # prepare, push once, exit
168
168
  npx ablo dev --branch-ttl-hours 24 # change temporary-key lifetime
169
+ npx ablo dev --local # keep Postgres private on localhost
169
170
  ```
170
171
 
172
+ `--local` loads `ablo/data-source.ts` (override with `--source <path>`), registers
173
+ the branch as connector-only, and opens an outbound authenticated WebSocket to
174
+ Ablo. Commit, load, list, and outbox-event requests run through the same signed
175
+ Data Source handler as production; no database credential leaves your process
176
+ and no public tunnel is opened. Because the connector is long-lived, `--local`
177
+ cannot be combined with `--no-watch`.
178
+
171
179
  It does not start your app, run migrations, create a database-provider branch,
172
180
  or copy production rows. Read [Branch-first development](./branch-development.md)
173
181
  for the exact discovery order, CI flow, database boundary, and troubleshooting.
@@ -2,6 +2,71 @@
2
2
 
3
3
  > Keep the rows in your own Postgres while Ablo coordinates and confirms every write.
4
4
 
5
+ ## Localhost development
6
+
7
+ Ablo Cloud cannot dial `localhost`: from a cloud server, that name means the
8
+ cloud server itself, not your Mac or development container. A development child
9
+ branch can still use Postgres that listens only on your machine by running a
10
+ signed Data Source over Ablo's outbound reverse channel:
11
+
12
+ ```bash
13
+ npx ablo migrate # once: models + ablo_idempotency + ablo_outbox
14
+ npx ablo dev --local
15
+ ```
16
+
17
+ The command loads `ablo/data-source.ts`, registers the current child branch as
18
+ connector-only, and dials out to Ablo over an authenticated WebSocket. Postgres
19
+ continues listening only on your machine; `DATABASE_URL` never leaves the
20
+ process. This is protocol-scoped, not a general-purpose tunnel: only signed
21
+ Data Source load, list, commit, and event requests traverse it. Use
22
+ `--source <path>` when the handler lives elsewhere.
23
+
24
+ Keep `ablo dev --local` running alongside the application. It pushes schema
25
+ changes and owns the database connector; stopping it deliberately makes the
26
+ branch's database unavailable instead of silently writing somewhere else.
27
+
28
+ ### Is this full Ablo?
29
+
30
+ Yes for the Ablo application path: model reads and lists, coordinated writes,
31
+ claims, subscriptions, idempotency, confirmations, and transactional outbox
32
+ settlement all work against localhost Postgres. The browser, server code, and
33
+ agents still connect to Ablo Cloud; only database operations cross the narrow
34
+ signed connector to your machine.
35
+
36
+ It is not logical replication. Visibility depends on how a row is written:
37
+
38
+ | Write origin | Visible to Ablo in localhost mode? | Why |
39
+ |---|---:|---|
40
+ | `ablo.<model>.create/update/delete` | Yes | Ablo coordinates the write, the local adapter commits it with idempotency + outbox, and the outbox event confirms it. |
41
+ | Code using the signed Data Source adapter | Yes | The adapter records the row and authoritative event in one transaction. |
42
+ | A supported source push/outbox integration | Yes | It explicitly publishes the authoritative event to Ablo. |
43
+ | Raw SQL, `psql`, or an unrelated ORM write | No, not automatically | There is no WAL reader in signed-endpoint mode, and bypassing the adapter does not append `ablo_outbox`. |
44
+
45
+ If Ablo must observe every arbitrary SQL/ORM write, use the direct logical-WAL
46
+ path with a network-reachable Postgres endpoint, PrivateLink/peering/VPN, or a
47
+ database-capable secure tunnel. Do not expose Postgres without TLS,
48
+ authentication, and network restrictions.
49
+
50
+ ### Local connector errors
51
+
52
+ Every stable code links to the generated [error reference](https://docs.abloatai.com/errors):
53
+
54
+ | Code | Meaning and fix |
55
+ |---|---|
56
+ | `source_connector_not_attached` | The branch is connector-only but no process is attached. Start or restart `ablo dev --local`. |
57
+ | `source_connector_unauthenticated` | The temporary branch key is missing, expired, or rejected. Rerun `ablo dev --local` to mint a fresh key. |
58
+ | `source_connector_requires_secret_key` | The connector received the wrong key kind. Let `ablo dev` supply its branch-bound `sk_` key. |
59
+ | `source_connector_no_source_registered` | No endpoint source exists for this branch. Upgrade/rerun the CLI so registration happens before socket attachment. |
60
+ | `source_connector_localhost_required` | Connector-only registration used a non-local descriptor. Use `ablo dev --local`; deployed handlers use ordinary HTTPS endpoint registration. |
61
+ | `source_connector_timeout` | The handler or local Postgres exceeded the request deadline. Inspect the local process and database. |
62
+ | `source_connector_handler_error` | `ablo/data-source.ts` or its adapter threw. The local terminal contains the underlying error. |
63
+ | `source_connector_protocol_error` | CLI/SDK and service connector protocols disagree. Upgrade the CLI and SDK together. |
64
+ | `source_connector_production_not_enabled` | A root/production key attempted the development connector. Use a supported production route or explicitly enable production reverse-channel support. |
65
+
66
+ Disconnects, service restarts, and connector replacement are retryable. Keep the
67
+ same idempotency key: Ablo never falls back from this branch to hosted storage or
68
+ another database.
69
+
5
70
  You write through Ablo, and Ablo writes to your Postgres. A call to
6
71
  `ablo.<model>.create / update / delete` enters Ablo's commit chokepoint — where
7
72
  claims, ordering, and idempotency are enforced — and Ablo applies the change to
@@ -1,5 +1,9 @@
1
1
  # Repository Structure
2
2
 
3
+ For a verb-by-verb guide to declarations and implementations—including exactly
4
+ where `create`, `update`, `delete`, and `claim` live—read the public
5
+ [`CODEMAP.md`](../../CODEMAP.md).
6
+
3
7
  The public repository preserves the same ownership boundaries as the main
4
8
  monorepo. `@abloatai/ablo` is the product package; the packages beneath it are
5
9
  implementation owners and first-party extension surfaces.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abloatai/ablo",
3
- "version": "0.47.0",
3
+ "version": "0.48.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",
@@ -124,8 +124,8 @@
124
124
  "directory": "packages/ablo"
125
125
  },
126
126
  "dependencies": {
127
- "@abloatai/humans": "^0.47.0",
128
- "@abloatai/transaction": "^0.47.0"
127
+ "@abloatai/humans": "^0.48.0",
128
+ "@abloatai/transaction": "^0.48.0"
129
129
  },
130
130
  "peerDependencies": {
131
131
  "ai": "^6.0.0 || ^7.0.0",
@@ -1,57 +0,0 @@
1
- # Agent Orchestration
2
-
3
- Do not model parent and child agents as directly talking to each other over WebSocket.
4
-
5
- Model them as actors coordinating through models:
6
-
7
- ```txt
8
- parent creates job -> child claims job -> child commits result -> parent reads result
9
- ```
10
-
11
- The WebSocket is delivery infrastructure. The product model is shared state.
12
-
13
- ## Model Shape
14
-
15
- A parent creates a job through its typed model client:
16
-
17
- ```ts
18
- const jobId = `forecast:${runId}`;
19
- await ablo.agentJobs.create({
20
- id: jobId,
21
- idempotencyKey: `job:${runId}`,
22
- data: {
23
- status: 'open',
24
- kind: 'forecast_report',
25
- target: { model: 'weatherReports', id: 'report_stockholm', field: 'forecast' },
26
- },
27
- });
28
- ```
29
-
30
- The child claims the job. If another worker holds it, the claim waits fairly,
31
- then returns the fresh row:
32
-
33
- ```ts
34
- await using claim = await ablo.agentJobs.claim({
35
- id: jobId,
36
- description: 'complete',
37
- ttl: '5m',
38
- });
39
- const job = claim.data;
40
-
41
- await ablo.agentJobs.update({
42
- id: job.id,
43
- data: {
44
- status: 'completed',
45
- result: { text },
46
- },
47
- });
48
- ```
49
-
50
- The child commits completion through the normal `update`, which is stale-guarded
51
- under the held claim. The claim releases when its scope exits.
52
-
53
- The parent retrieves the job result by model ID. Later, `ablo.events` can make that reactive, but the state model does not change.
54
-
55
- ## Rule
56
-
57
- Nested agents should create or complete models. They should not require a separate agent-to-agent protocol for normal work.