@abloatai/ablo 0.57.0 → 0.59.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/AGENTS.md +12 -5
- package/CHANGELOG.md +269 -13
- package/README.md +6 -5
- package/dist/ai-sdk.d.ts +1 -1
- package/dist/ai-sdk.d.ts.map +1 -1
- package/dist/context/evidence.d.ts +6 -8
- package/dist/context/evidence.d.ts.map +1 -1
- package/dist/context/evidence.js +6 -20
- package/dist/context/evidence.js.map +1 -1
- package/dist/context/index.d.ts +23 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +26 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/onChange.d.ts +9 -0
- package/dist/context/onChange.d.ts.map +1 -0
- package/dist/context/onChange.js +37 -0
- package/dist/context/onChange.js.map +1 -0
- package/dist/source-conformance.d.ts +1 -1
- package/dist/source-conformance.d.ts.map +1 -1
- package/dist/source-conformance.js +1 -1
- package/dist/source-conformance.js.map +1 -1
- package/dist/source-drizzle.d.ts +1 -1
- package/dist/source-drizzle.d.ts.map +1 -1
- package/dist/source-drizzle.js +1 -1
- package/dist/source-drizzle.js.map +1 -1
- package/dist/source-kysely.d.ts +1 -1
- package/dist/source-kysely.d.ts.map +1 -1
- package/dist/source-kysely.js +1 -1
- package/dist/source-kysely.js.map +1 -1
- package/dist/source-next.d.ts +1 -1
- package/dist/source-next.d.ts.map +1 -1
- package/dist/source-next.js +1 -1
- package/dist/source-next.js.map +1 -1
- package/docs/agents.md +38 -15
- package/docs/api-keys.md +6 -6
- package/docs/api.md +107 -34
- package/docs/basic-usage.md +84 -0
- package/docs/branch-development.md +23 -4
- package/docs/cli.md +16 -9
- package/docs/client-behavior.md +27 -29
- package/docs/comparison.md +63 -0
- package/docs/concurrency-convention.md +92 -75
- package/docs/context.md +76 -31
- package/docs/coordinate-existing-work.md +104 -0
- package/docs/coordination.md +93 -38
- package/docs/data-sources.md +12 -6
- package/docs/debugging.md +1 -1
- package/docs/deployment.md +19 -1
- package/docs/examples/agent-human.md +6 -18
- package/docs/examples/coordination-conformance.md +69 -0
- package/docs/examples/evidence-backed-document-pipeline.md +488 -0
- package/docs/examples/existing-python-backend.md +10 -13
- package/docs/examples/nextjs.md +2 -2
- package/docs/examples/scoped-agent.md +18 -1
- package/docs/examples/server-agent.md +2 -2
- package/docs/faq.md +75 -0
- package/docs/groups.md +19 -139
- package/docs/guarantees.md +8 -8
- package/docs/idempotency.md +3 -0
- package/docs/identity.md +2 -1
- package/docs/implement.md +61 -0
- package/docs/implementation-index.md +20 -0
- package/docs/index.md +59 -173
- package/docs/installation.md +77 -0
- package/docs/instrumentation.md +52 -0
- package/docs/integration-guide.md +20 -19
- package/docs/integrations/sandbox-runtime.md +157 -0
- package/docs/integrations.md +9 -0
- package/docs/migration.md +12 -7
- package/docs/operating-on-your-database.md +7 -0
- package/docs/options.md +172 -0
- package/docs/quickstart.md +25 -14
- package/docs/react.md +9 -9
- package/docs/schema-contract.md +14 -13
- package/docs/security.md +64 -0
- package/docs/sessions.md +1 -1
- package/examples/README.md +8 -2
- package/examples/agent-turn.ts +1 -1
- package/examples/expensive-agent-turn.ts +1 -1
- package/examples/stale-context-agent-turn.ts +106 -0
- package/llms.txt +23 -12
- package/package.json +7 -6
- package/dist/context/sources.d.ts +0 -21
- package/dist/context/sources.d.ts.map +0 -1
- package/dist/context/sources.js +0 -36
- package/dist/context/sources.js.map +0 -1
- package/dist/context.d.ts +0 -22
- package/dist/context.d.ts.map +0 -1
- package/dist/context.js +0 -33
- package/dist/context.js.map +0 -1
package/docs/index.md
CHANGED
|
@@ -1,193 +1,79 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Introduction
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Coordination infrastructure for agents, applications, services, and people working on shared state
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
Ablo is a framework-agnostic coordination layer for agents, applications,
|
|
6
|
+
services, and people working on shared state. It provides claims, waiting,
|
|
7
|
+
participant identity, stale-work rejection, confirmed writes, and live updates
|
|
8
|
+
through one typed interface. Whether you are adding agents to an existing
|
|
9
|
+
application or building a new multi-user system, Ablo lets you focus on your
|
|
10
|
+
product instead of rebuilding coordination infrastructure.
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
await using claim = await ablo.reports.claim({ id: reportId });
|
|
12
|
+
Ablo works with your existing database, API, authorization, and business logic,
|
|
13
|
+
while providing a common coordination model across runtimes and frameworks.
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
id: claim.data.id,
|
|
16
|
-
data: { forecast: await generateForecast(claim.data) },
|
|
17
|
-
});
|
|
18
|
-
```
|
|
15
|
+
## Features
|
|
19
16
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
based on a row that has since changed is turned away rather than applied.
|
|
24
|
-
|
|
25
|
-
## What people build
|
|
17
|
+
Ablo provides a comprehensive set of coordination capabilities and a shared
|
|
18
|
+
model that can be used across agents, services, applications, and human
|
|
19
|
+
interfaces.
|
|
26
20
|
|
|
27
21
|
<Columns>
|
|
28
|
-
<Card title="
|
|
29
|
-
|
|
22
|
+
<Card title="Claims and waiting" icon="handshake" href="/coordination">
|
|
23
|
+
Let one participant perform contested work while others wait, skip, or fail according to an explicit policy.
|
|
24
|
+
</Card>
|
|
25
|
+
|
|
26
|
+
<Card title="Existing PostgreSQL" icon="database" href="/coordinate-existing-work">
|
|
27
|
+
Keep the authoritative transaction, locks, constraints, and direct SQL paths your application already owns.
|
|
28
|
+
</Card>
|
|
29
|
+
|
|
30
|
+
<Card title="Participant identity" icon="fingerprint" href="/identity">
|
|
31
|
+
Give agents, people, and services distinct scoped credentials instead of treating every worker as the same caller.
|
|
32
|
+
</Card>
|
|
33
|
+
|
|
34
|
+
<Card title="Crash recovery" icon="rotate-ccw" href="/guarantees">
|
|
35
|
+
Expiring leases and heartbeats let later participants proceed when an owner disappears.
|
|
30
36
|
</Card>
|
|
31
37
|
|
|
32
|
-
<Card title="
|
|
33
|
-
|
|
38
|
+
<Card title="Stale-work rejection" icon="shield-check" href="/concurrency-convention">
|
|
39
|
+
Carry the rows behind a decision into its write and reject the result when those premises changed.
|
|
34
40
|
</Card>
|
|
35
41
|
|
|
36
|
-
<Card title="
|
|
37
|
-
|
|
42
|
+
<Card title="Atomic commits" icon="git-merge" href="/api#atomic-commits">
|
|
43
|
+
Apply several Ablo writes together, with their captured premises, or apply none of them.
|
|
38
44
|
</Card>
|
|
39
45
|
|
|
40
|
-
<Card title="
|
|
41
|
-
|
|
46
|
+
<Card title="Confirmed writes" icon="receipt" href="/guarantees">
|
|
47
|
+
Know when a write reached the authoritative database and why a rejected write did not land.
|
|
42
48
|
</Card>
|
|
43
49
|
|
|
44
|
-
<Card title="
|
|
45
|
-
|
|
50
|
+
<Card title="Humans and agents" icon="users" href="/react">
|
|
51
|
+
Coordinate stateless HTTP workers with live human interfaces over the same shared state.
|
|
46
52
|
</Card>
|
|
47
53
|
|
|
48
|
-
<Card title="
|
|
49
|
-
|
|
54
|
+
<Card title="Audit and visibility" icon="scroll-text" href="/audit">
|
|
55
|
+
Inspect ownership, contention, and committed changes with the responsible participant attached.
|
|
50
56
|
</Card>
|
|
51
57
|
</Columns>
|
|
52
58
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
DDL and owns no schema — your migration tool stays in charge of the shape of your database.
|
|
75
|
-
|
|
76
|
-
```bash
|
|
77
|
-
npx ablo connect
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
No database yet? Pass an `apiKey` only and Ablo keeps the rows in its own log, so you can
|
|
81
|
-
build the whole system today and point it at Postgres when you are ready.
|
|
82
|
-
</Step>
|
|
83
|
-
|
|
84
|
-
<Step title="Build with Ablo">
|
|
85
|
-
You are writing the agent yourself — a worker, a job handler, a tool inside a model loop.
|
|
86
|
-
Agents hold no socket; the credential is the identity.
|
|
87
|
-
|
|
88
|
-
```ts
|
|
89
|
-
const ablo = Ablo({ schema, apiKey: process.env.ABLO_API_KEY, transport: 'http' });
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
Read with `list` / `get`, coordinate with `claim`, write with `create` / `update` /
|
|
93
|
-
`delete`. See [Agents](./agents.md) for the loop and [API Reference](./api.md) for the shape.
|
|
94
|
-
</Step>
|
|
95
|
-
|
|
96
|
-
<Step title="Or point an MCP host at it">
|
|
97
|
-
The agent is Claude, Cursor, or another MCP host, and you want it operating your data
|
|
98
|
-
directly. The coordination server exposes the same claim-and-commit loop as tools.
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
|
-
claude mcp add ablo -- npx -y @abloatai/mcp
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
See [Model Context Protocol](./mcp.md) — and read the surface table below before you pick,
|
|
105
|
-
because Ablo publishes two MCP servers and only one of them is a data plane.
|
|
106
|
-
</Step>
|
|
107
|
-
</Steps>
|
|
108
|
-
|
|
109
|
-
## Surfaces
|
|
110
|
-
|
|
111
|
-
Every surface reaches the same coordinated state. Pick by who is calling.
|
|
112
|
-
|
|
113
|
-
| Surface | Use it for |
|
|
114
|
-
|---|---|
|
|
115
|
-
| **SDK**: `@abloatai/ablo`, `transport: 'http'` | The agents themselves. Stateless, request/response, nothing held open. The main path. |
|
|
116
|
-
| **Coordination MCP**: `@abloatai/mcp` | An agent living inside an MCP host that needs claim and commit as tools. A data plane. |
|
|
117
|
-
| **`humans()`**: with `@abloatai/ablo/react` | The interfaces a person watches agent work arrive in: presence, live queries, a local copy. |
|
|
118
|
-
| **CLI**: `ablo` | Scaffolding, schema push, connecting a database. Terminals and CI. |
|
|
119
|
-
| **REST**: `/api/v1` | Runtimes with no SDK. |
|
|
120
|
-
| **Integration-helper MCP**: hosted `/api/mcp` | Teaching a coding assistant the SDK while you build. Docs, lint, and scaffolds only. |
|
|
121
|
-
|
|
122
|
-
The two MCP servers are not interchangeable. The coordination server changes
|
|
123
|
-
your data; the integration-helper server serves documentation and has no
|
|
124
|
-
per-model data tools at all. An agent that edits rows uses the SDK or the
|
|
125
|
-
coordination server — never the helper.
|
|
126
|
-
|
|
127
|
-
### Where people fit
|
|
128
|
-
|
|
129
|
-
The bare client is the coordination layer: commit, read, observe, claim. People
|
|
130
|
-
are something you add to it. `humans()` is the plugin that declares the local,
|
|
131
|
-
watchable copy — the offline store, live queries, presence, and the framework
|
|
132
|
-
bindings — and it needs a duplex connection, so a stateless agent cannot install
|
|
133
|
-
it and is told so at construction rather than left with a subscription that never
|
|
134
|
-
delivers.
|
|
135
|
-
|
|
136
|
-
There is no `agents()` plugin, and the absence is the point: agents are the
|
|
137
|
-
default caller, not a special one.
|
|
138
|
-
|
|
139
|
-
## Concepts
|
|
140
|
-
|
|
141
|
-
- [How Ablo Works](./how-it-works.md) — the mental model in one page: you write through Ablo, it lands in your Postgres, the write-ahead log confirms it. **Read this first.**
|
|
142
|
-
- [Coordination](./coordination.md) — `claim`, `claim.state`, and `claim.queue`: who holds a row, and who is waiting.
|
|
143
|
-
- [Concurrency Convention](./concurrency-convention.md) — the precise rule for guarded and unguarded writes.
|
|
144
|
-
- [Guarantees](./guarantees.md) — what a confirmed write, a stale-write rejection, and a claim each promise.
|
|
145
|
-
- [Idempotency](./idempotency.md) — make a retried write safe; what replays, what re-runs, and for how long.
|
|
146
|
-
- [Schema Contract](./schema-contract.md) — one schema becomes typed clients, agent writes, React reads, and the push.
|
|
147
|
-
- [Agents](./agents.md) — the stateless participant: wake, read, claim, commit, idle.
|
|
148
|
-
- [Agent Messaging](./agent-messaging.md) — durable handoffs between agents, linked to the claim they discuss.
|
|
149
|
-
- [Identity & Sync Groups](./identity.md) — who is connecting, and which slice of state they see.
|
|
150
|
-
- [Change Propagation](./groups.md) — how one row's change reaches the actors that depend on it.
|
|
151
|
-
- [Client Behavior](./client-behavior.md) — options, errors, retries, timeouts, and imports.
|
|
152
|
-
|
|
153
|
-
## Authority
|
|
154
|
-
|
|
155
|
-
- [Projects](./projects.md) — one organization, many apps; each with its own schema, planes, and keys.
|
|
156
|
-
- [API Keys](./api-keys.md) — the credential that carries an agent's identity and its scopes.
|
|
157
|
-
- [Sessions](./sessions.md) — short-lived scoped credentials your backend mints.
|
|
158
|
-
- [Customer Organizations](./customer-organizations.md) — serve many isolated customer organizations from one schema and backend.
|
|
159
|
-
- [Audit Log](./audit.md) — trace any confirmed write back to the person behind it.
|
|
160
|
-
- [Operating on Your Database](./operating-on-your-database.md) — which actions run freely, which to verify first, and which belong to a human.
|
|
161
|
-
- [Session Settings](./session-settings.md) — point your row-level-security policies at Ablo's writes, by naming the settings they already read.
|
|
162
|
-
|
|
163
|
-
## Build
|
|
164
|
-
|
|
165
|
-
- [Quickstart](./quickstart.md) — make your first coordinated write.
|
|
166
|
-
- [Integration Guide](./integration-guide.md) — the canonical end-to-end integration.
|
|
167
|
-
- [Integrations](./integrations.md) — long-running records, ingestion, and other application-edge runtimes.
|
|
168
|
-
- [CLI & Migrations](./cli.md) — `init` / `connect` / `push` / `migrate` / `generate`.
|
|
169
|
-
- [Connect Your Database](./data-sources.md) — where rows land when your own database is canonical.
|
|
170
|
-
- [Deployment](./deployment.md) — the database, the keys, and the schema push that take an integration to production.
|
|
171
|
-
- [React](./react.md) — provider, hooks, and reactive reads.
|
|
172
|
-
- [Webhooks](./webhooks.md) — react to confirmed change from outside the SDK.
|
|
173
|
-
- [Debugging & Logs](./debugging.md) — watch claims, queueing, and grants while you build.
|
|
174
|
-
|
|
175
|
-
## Reference
|
|
176
|
-
|
|
177
|
-
- [API Reference](./api.md) — model-by-model method shape.
|
|
178
|
-
- [Errors](./errors.md) — the code registry, its categories, and what to do about each.
|
|
179
|
-
- [Upgrade Guide](./migration.md) — upgrade a pinned pre-1.0 SDK safely.
|
|
180
|
-
- [Changelog](../CHANGELOG.md) — what shipped recently.
|
|
181
|
-
|
|
182
|
-
## Examples
|
|
183
|
-
|
|
184
|
-
- [AI SDK Tool](./examples/ai-sdk-tool.md) — put Ablo inside a model's tool call.
|
|
185
|
-
- [Agent + Human](./examples/agent-human.md) — yield when a person is holding the same report.
|
|
186
|
-
- [Server Agent](./examples/server-agent.md) — a schema-backed worker.
|
|
187
|
-
- [Existing Python Backend](./examples/existing-python-backend.md) — add coordination without replacing your API server.
|
|
188
|
-
- [Next.js](./examples/nextjs.md) — app-router setup with React bindings.
|
|
189
|
-
|
|
190
|
-
## More
|
|
191
|
-
|
|
192
|
-
- [README](../README.md) — product overview and first example.
|
|
193
|
-
- [AGENTS.md](../AGENTS.md) — installation guidance for coding assistants.
|
|
59
|
+
...and more.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Get started
|
|
64
|
+
|
|
65
|
+
- [Installation](./installation.md) — install Ablo, declare the shared models,
|
|
66
|
+
and create a client.
|
|
67
|
+
- [Basic usage](./basic-usage.md) — read, write, and coordinate one operation.
|
|
68
|
+
- [Comparison](./comparison.md) — see how Ablo relates to PostgreSQL locks,
|
|
69
|
+
Redis reservations, queues, workflow engines, and rolling your own.
|
|
70
|
+
- [Choose the Ablo operation](./implement.md) — route an existing use case to
|
|
71
|
+
the smallest correct implementation.
|
|
72
|
+
|
|
73
|
+
## AI resources
|
|
74
|
+
|
|
75
|
+
Ablo is designed to be implemented by agents as well as people. Use
|
|
76
|
+
[llms.txt](https://docs.abloatai.com/llms.txt) for the public documentation
|
|
77
|
+
index, or connect an assistant to the [documentation MCP server](./mcp.md). The
|
|
78
|
+
coordination MCP package also ships its agent-facing skill as
|
|
79
|
+
`@abloatai/mcp/skill.md`.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Installation
|
|
2
|
+
|
|
3
|
+
> Install Ablo, declare the models participants share, and create a typed client.
|
|
4
|
+
|
|
5
|
+
Install the Ablo TypeScript SDK in an existing or new project.
|
|
6
|
+
|
|
7
|
+
## Install the package
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @abloatai/ablo
|
|
11
|
+
npx ablo init
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`ablo init` creates the schema, registration, and client files and signs the
|
|
15
|
+
developer in. Keep these files together under one `ablo/` ownership boundary.
|
|
16
|
+
|
|
17
|
+
## Declare shared models
|
|
18
|
+
|
|
19
|
+
Declare only the rows Ablo coordinates. Your other tables stay in the schema
|
|
20
|
+
and migrations the application already owns.
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
// ablo/schema.ts
|
|
24
|
+
import { defineSchema, model, z } from '@abloatai/ablo/schema';
|
|
25
|
+
|
|
26
|
+
export const schema = defineSchema({
|
|
27
|
+
tasks: model({
|
|
28
|
+
title: z.string(),
|
|
29
|
+
status: z.enum(['open', 'done']),
|
|
30
|
+
}),
|
|
31
|
+
});
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Every model automatically has an `id`. Declare application fields such as
|
|
35
|
+
timestamps and actor ids yourself when you need them.
|
|
36
|
+
|
|
37
|
+
## Start development
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npx ablo dev
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The development command prepares an isolated Ablo branch, supplies its
|
|
44
|
+
temporary `ABLO_API_KEY`, pushes the schema, and watches for changes. Runtime
|
|
45
|
+
code always reads `ABLO_API_KEY`; do not put management credentials in the
|
|
46
|
+
application.
|
|
47
|
+
|
|
48
|
+
## Use your PostgreSQL database
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npx ablo connect
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Ablo runs no DDL and does not replace your migration tool. It writes the models
|
|
55
|
+
you declared and confirms changes from PostgreSQL's write-ahead log. Existing
|
|
56
|
+
APIs, direct SQL, constraints, and transactions can remain in place.
|
|
57
|
+
|
|
58
|
+
## Create the client
|
|
59
|
+
|
|
60
|
+
`ablo init` scaffolds this file. Start with the HTTP client for an agent or
|
|
61
|
+
server operation; it has no persistent connection to manage.
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
// ablo/client.ts
|
|
65
|
+
import Ablo from '@abloatai/ablo';
|
|
66
|
+
import { schema } from './schema';
|
|
67
|
+
|
|
68
|
+
export const ablo = Ablo({
|
|
69
|
+
schema,
|
|
70
|
+
apiKey: process.env.ABLO_API_KEY,
|
|
71
|
+
transport: 'http',
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Continue to [Basic usage](./basic-usage.md) to read, write, and coordinate one
|
|
76
|
+
operation. Use the [full Quickstart](./quickstart.md) when you need the detailed
|
|
77
|
+
branch, schema-registration, and database setup explanation.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Instrumentation
|
|
2
|
+
|
|
3
|
+
> Connect Ablo coordination outcomes and durable activity to the observability tools you already use.
|
|
4
|
+
|
|
5
|
+
Ablo exposes two complementary signals: process-local coordination events for
|
|
6
|
+
live telemetry, and credential-scoped logs for durable inspection.
|
|
7
|
+
|
|
8
|
+
## Capture coordination events
|
|
9
|
+
|
|
10
|
+
Pass an `observability` sink to the package-root client:
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
const ablo = Ablo({
|
|
14
|
+
schema,
|
|
15
|
+
observability: {
|
|
16
|
+
captureClaim: (event) => telemetry.capture('ablo.claim', event),
|
|
17
|
+
captureConflict: (event) => telemetry.capture('ablo.conflict', event),
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`captureClaim` reports acquisition, queueing, grant, release, loss, and expiry.
|
|
23
|
+
`captureConflict` reports stale dependencies and writes rejected by another
|
|
24
|
+
participant's claim. The sink should return quickly and must not be the
|
|
25
|
+
correctness path for the operation it observes.
|
|
26
|
+
|
|
27
|
+
## Inspect durable activity
|
|
28
|
+
|
|
29
|
+
The stateless client exposes `ablo.logs` for authoritative, credential-scoped
|
|
30
|
+
event pages. Use it for audit views, support tooling, and reconciliation rather
|
|
31
|
+
than treating process logs as durable truth.
|
|
32
|
+
|
|
33
|
+
Access is bounded by the same credential that created the client. Do not copy
|
|
34
|
+
customer-wide logs into a less restricted telemetry destination.
|
|
35
|
+
|
|
36
|
+
## What to measure
|
|
37
|
+
|
|
38
|
+
Useful coordination measures include:
|
|
39
|
+
|
|
40
|
+
- claim acquisition, wait, and hold duration;
|
|
41
|
+
- queue depth and contention outcome;
|
|
42
|
+
- expired or lost claims;
|
|
43
|
+
- stale-context and foreign-claim rejections;
|
|
44
|
+
- retry count and request latency;
|
|
45
|
+
- durable-write backlog and replay outcome.
|
|
46
|
+
|
|
47
|
+
Alert on sustained changes in rates, not on every expected contention event.
|
|
48
|
+
Queueing and stale-write rejection are often the system preventing duplicate or
|
|
49
|
+
obsolete work, not failures by themselves.
|
|
50
|
+
|
|
51
|
+
See [Debugging & Logs](./debugging.md) for local diagnosis and event formatting,
|
|
52
|
+
and [Audit Log](./audit.md) for stored attribution and verification.
|
|
@@ -77,7 +77,7 @@ When handing this to a coding agent, give it a concrete target:
|
|
|
77
77
|
```txt
|
|
78
78
|
Add Ablo to this app for one model your agents edit.
|
|
79
79
|
Run npx ablo dev and use its branch-bound key. Declare schema, add the Ablo client, replace
|
|
80
|
-
one
|
|
80
|
+
one decision read with ablo.<model>.read(...), pass that row in the write's reads array, and add a smoke test for two concurrent writers.
|
|
81
81
|
```
|
|
82
82
|
|
|
83
83
|
## 1. Declare A Schema
|
|
@@ -93,9 +93,9 @@ import { defineSchema, model, z } from '@abloatai/ablo/schema';
|
|
|
93
93
|
export const schema = defineSchema(
|
|
94
94
|
{
|
|
95
95
|
weatherReports: model({
|
|
96
|
-
//
|
|
97
|
-
// are
|
|
98
|
-
//
|
|
96
|
+
// `id` is the only field the SDK supplies; never declare it. Audit
|
|
97
|
+
// fields are ordinary fields: declare `createdAt` / `updatedAt` /
|
|
98
|
+
// `createdBy` here if you want columns for them.
|
|
99
99
|
projectId: z.string(),
|
|
100
100
|
location: z.string(),
|
|
101
101
|
status: z.enum(['pending', 'ready']),
|
|
@@ -158,7 +158,7 @@ falsy flag. See `packages/transaction/src/schema/model.ts` for the full option s
|
|
|
158
158
|
Trusted runtimes can use `ABLO_API_KEY`.
|
|
159
159
|
|
|
160
160
|
```ts
|
|
161
|
-
// src/ablo.ts
|
|
161
|
+
// src/client/ablo.ts
|
|
162
162
|
import Ablo from '@abloatai/ablo';
|
|
163
163
|
import { schema } from './ablo/schema';
|
|
164
164
|
|
|
@@ -176,7 +176,7 @@ client now).
|
|
|
176
176
|
|
|
177
177
|
```tsx
|
|
178
178
|
// src/ablo-client.ts
|
|
179
|
-
import Ablo from '@abloatai/ablo';
|
|
179
|
+
import { Ablo } from '@abloatai/ablo/react';
|
|
180
180
|
import { schema } from '@/ablo/schema';
|
|
181
181
|
|
|
182
182
|
// The browser never holds the API key. The client mints a short-lived token
|
|
@@ -256,7 +256,7 @@ refreshes before expiry.
|
|
|
256
256
|
## 3. Read State
|
|
257
257
|
|
|
258
258
|
Reads come in two flavors, and you pick based on whether you can wait.
|
|
259
|
-
`get({ id })` and `list({ where })` hit the server (and hydrate the local
|
|
259
|
+
`get({ id })`, `read({ id })`, and `list({ where })` hit the server (and hydrate the local
|
|
260
260
|
store) — they're async, so you `await` them. `local.get(id)`,
|
|
261
261
|
`local.list({ where })`, and `local.count({ where })` read the already-synced local
|
|
262
262
|
graph synchronously, so they're the ones you call in render — and the ones you
|
|
@@ -268,7 +268,7 @@ and waits.
|
|
|
268
268
|
```ts
|
|
269
269
|
await ablo.ready();
|
|
270
270
|
|
|
271
|
-
const report = await ablo.weatherReports.
|
|
271
|
+
const report = await ablo.weatherReports.read({ id: 'report_stockholm' });
|
|
272
272
|
if (!report) throw new Error('report not found');
|
|
273
273
|
```
|
|
274
274
|
|
|
@@ -318,17 +318,17 @@ For simple writes:
|
|
|
318
318
|
await ablo.weatherReports.update({ id: 'report_stockholm', data: { status: 'ready' } });
|
|
319
319
|
```
|
|
320
320
|
|
|
321
|
-
For writes based on state
|
|
322
|
-
|
|
321
|
+
For writes based on current state, use a decision read and pass the exact row
|
|
322
|
+
to the mutation:
|
|
323
323
|
|
|
324
324
|
```ts
|
|
325
|
-
const
|
|
325
|
+
const report = await ablo.weatherReports.read({ id: 'report_stockholm' });
|
|
326
|
+
if (!report) throw new Error('report not found');
|
|
326
327
|
|
|
327
328
|
await ablo.weatherReports.update({
|
|
328
|
-
id:
|
|
329
|
+
id: report.id,
|
|
329
330
|
data: { status: 'ready' },
|
|
330
|
-
|
|
331
|
-
onStale: 'reject',
|
|
331
|
+
reads: [report],
|
|
332
332
|
});
|
|
333
333
|
```
|
|
334
334
|
|
|
@@ -481,12 +481,12 @@ const completeReport = tool({
|
|
|
481
481
|
forecast: z.string(),
|
|
482
482
|
}),
|
|
483
483
|
execute: async ({ reportId, forecast }) => {
|
|
484
|
-
const
|
|
484
|
+
const report = await ablo.weatherReports.read({ id: reportId });
|
|
485
|
+
if (!report) throw new Error('report not found');
|
|
485
486
|
return ablo.weatherReports.update({
|
|
486
487
|
id: reportId,
|
|
487
488
|
data: { status: 'ready', forecast },
|
|
488
|
-
|
|
489
|
-
onStale: 'reject',
|
|
489
|
+
reads: [report],
|
|
490
490
|
});
|
|
491
491
|
},
|
|
492
492
|
});
|
|
@@ -529,7 +529,7 @@ that guarantee as eventual completion with repair—not atomicity.
|
|
|
529
529
|
| `persistence: 'indexeddb'` | Durable browser cache that survives reloads, for apps that need it. |
|
|
530
530
|
| `durableWrites: { store, namespace? }` | Recover unacknowledged worker writes after a process restart. |
|
|
531
531
|
| `claim` / `claim.state` / `claim.queue` | Show active work and coordinate before a write. |
|
|
532
|
-
| `
|
|
532
|
+
| `read` + `reads` | Reject writes based on stale state. |
|
|
533
533
|
| `mutable`, `readOnly`, `field`, `indexed` | Advanced schema and read tuning. |
|
|
534
534
|
|
|
535
535
|
The first integration should not need most of these. Start with schema and
|
|
@@ -540,7 +540,8 @@ them.
|
|
|
540
540
|
|
|
541
541
|
| Method | Use it for |
|
|
542
542
|
| -------------------------------------- | -------------------------------------------------------------------------------- |
|
|
543
|
-
| `get({ id })`
|
|
543
|
+
| `get({ id })` | Observe one row from the server (await it). |
|
|
544
|
+
| `read({ id })` | Read one decision input to carry in a mutation's `reads` array. |
|
|
544
545
|
| `list({ where })` | Async read of many rows from the server (await it). |
|
|
545
546
|
| `listAll({ where, maxPages?, signal? })` | Explicit bounded traversal of every matching page; filter before collecting. |
|
|
546
547
|
| `local.get(id)` | Synchronous local read of one synced row (use in render). |
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# Anthropic Sandbox Runtime
|
|
2
|
+
|
|
3
|
+
> Run an Ablo agent with OS-enforced filesystem and network boundaries while its coordinated work remains durable outside the sandbox.
|
|
4
|
+
|
|
5
|
+
## Choose the write owner first
|
|
6
|
+
|
|
7
|
+
- If the sandbox reads and writes shared Ablo rows itself, continue with this page.
|
|
8
|
+
- If the sandbox only returns a prepared result and the host process commits
|
|
9
|
+
through an existing application operation, **stop here and open**
|
|
10
|
+
[Coordinate existing work](../coordinate-existing-work.md).
|
|
11
|
+
That guide owns the implementation. Keep the operation, its database
|
|
12
|
+
transaction, and the existing Ablo wiring.
|
|
13
|
+
|
|
14
|
+
Anthropic Sandbox Runtime and Ablo own different boundaries:
|
|
15
|
+
|
|
16
|
+
| Concern | Owner |
|
|
17
|
+
|---|---|
|
|
18
|
+
| Filesystem, network, Unix sockets, process-tree restrictions | Sandbox Runtime |
|
|
19
|
+
| Typed reads and writes, claims, safe retries, and confirmation | Ablo |
|
|
20
|
+
| Prompts, tools, model calls, and business behavior | Your application |
|
|
21
|
+
| Authentication, branch creation, schema push, and database connection | A trusted host workflow |
|
|
22
|
+
|
|
23
|
+
An **execution sandbox** is the disposable process boundary. An **Ablo branch**
|
|
24
|
+
is an isolated data and schema plane. Keep the names and lifecycles separate.
|
|
25
|
+
|
|
26
|
+
## Use two profiles
|
|
27
|
+
|
|
28
|
+
Do not give a repository-editing agent the same authority as a runtime agent.
|
|
29
|
+
|
|
30
|
+
The **integration profile** adapts source code. It receives no Ablo credential,
|
|
31
|
+
has no Ablo network access, cannot read real environment files, and writes only
|
|
32
|
+
inside the selected application root. It can still read the installed-version
|
|
33
|
+
documentation without a network connection:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm exec --offline -- ablo docs integration-guide
|
|
37
|
+
npm exec --offline -- ablo docs api
|
|
38
|
+
npm exec --offline -- ablo setup --plan --json
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The **runtime profile** executes application work. It receives one short-lived,
|
|
42
|
+
branch-bound runtime credential, permits the Ablo API, and normally has no
|
|
43
|
+
repository write access. Give it only the model operations and sync groups one
|
|
44
|
+
run needs.
|
|
45
|
+
|
|
46
|
+
Never inject control-plane authority, a database URL, cloud credentials, or an
|
|
47
|
+
ambient host environment into either profile.
|
|
48
|
+
|
|
49
|
+
## Prepare authority outside the sandbox
|
|
50
|
+
|
|
51
|
+
Run control-plane and database operations in a trusted host workflow. That
|
|
52
|
+
workflow prepares the branch, pushes the reviewed schema, and delegates a
|
|
53
|
+
per-run `rk_` credential. Management and broad branch credentials are
|
|
54
|
+
infrastructure details and never enter the agent-facing launcher:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# Supplied by trusted CI or a credential broker after branch preparation.
|
|
58
|
+
export ABLO_API_KEY=rk_...
|
|
59
|
+
npm run sandbox -- agent job_123
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The sandbox launcher should build an explicit child environment rather than
|
|
63
|
+
inherit `process.env`. A runtime credential is not a substitute for process
|
|
64
|
+
isolation, and process isolation is not a substitute for a narrowly scoped
|
|
65
|
+
credential.
|
|
66
|
+
|
|
67
|
+
## Keep the repository tree downward
|
|
68
|
+
|
|
69
|
+
Give the Ablo boundary and agent behavior separate entry points:
|
|
70
|
+
|
|
71
|
+
```text
|
|
72
|
+
src/
|
|
73
|
+
ablo/
|
|
74
|
+
index.ts schema-backed client boundary
|
|
75
|
+
client.ts
|
|
76
|
+
schema.ts
|
|
77
|
+
agent/
|
|
78
|
+
index.ts process entry point
|
|
79
|
+
processJob.ts
|
|
80
|
+
sandbox/
|
|
81
|
+
index.ts sanitized launcher and command catalog
|
|
82
|
+
integration.policy.json
|
|
83
|
+
runtime.policy.json
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The agent enters through `agent/index.ts` and follows dependencies down into
|
|
87
|
+
`ablo/index.ts`. Ablo code never imports the agent or sandbox implementation.
|
|
88
|
+
|
|
89
|
+
## Coordinate effects that outlive the process
|
|
90
|
+
|
|
91
|
+
A filesystem sandbox cannot prevent two valid agents from overwriting the same
|
|
92
|
+
shared row. Read, claim, and write through the schema-backed Ablo client:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
const claim = await ablo.jobs.claim({
|
|
96
|
+
id: jobId,
|
|
97
|
+
description: 'processing in a sandbox',
|
|
98
|
+
ttl: '30s',
|
|
99
|
+
heartbeat: { every: '10s' },
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
const result = await performWork(claim.data);
|
|
104
|
+
await ablo.jobs.update({
|
|
105
|
+
id: claim.data.id,
|
|
106
|
+
data: { status: 'complete', result },
|
|
107
|
+
claim,
|
|
108
|
+
idempotencyKey: `job:${claim.data.id}:complete`,
|
|
109
|
+
});
|
|
110
|
+
} finally {
|
|
111
|
+
await claim.release();
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
If the sandbox disappears, its heartbeat stops and the lease expires. A later
|
|
116
|
+
holder reads fresh state. If the old process resumes, the checked write is
|
|
117
|
+
refused because it no longer owns the claim.
|
|
118
|
+
|
|
119
|
+
## Start from deny-first policies
|
|
120
|
+
|
|
121
|
+
Sandbox Runtime denies writes and network access unless they are allowed, but
|
|
122
|
+
filesystem reads require explicit deny regions. A practical policy should:
|
|
123
|
+
|
|
124
|
+
- deny the user's home region, then re-allow the selected repository;
|
|
125
|
+
- keep real `.env*`, SSH material, cloud configuration, and credentials denied;
|
|
126
|
+
- allow writes only to the selected application root and a dedicated temporary directory;
|
|
127
|
+
- allow only `api.abloatai.com:443` for an Ablo runtime agent;
|
|
128
|
+
- add a model-provider domain only when the model process itself runs inside the sandbox;
|
|
129
|
+
- leave Docker sockets and Apple Events disabled; and
|
|
130
|
+
- use resolved literal paths because filesystem globs are not supported on Linux.
|
|
131
|
+
|
|
132
|
+
Domain allowlists are coarse: an allowed domain can still be an exfiltration
|
|
133
|
+
channel. Filesystem isolation, environment sanitation, and least-authority
|
|
134
|
+
credentials must be used together.
|
|
135
|
+
|
|
136
|
+
## Run the complete example
|
|
137
|
+
|
|
138
|
+
The repository example contains the launcher, both policies, the schema-backed
|
|
139
|
+
agent, and boundary tests:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
cd examples/sandboxed-agent
|
|
143
|
+
npm install
|
|
144
|
+
npm run docs:sandboxed
|
|
145
|
+
npm test
|
|
146
|
+
npm run typecheck
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Sandbox Runtime is a beta research preview. Keep it behind the launcher boundary
|
|
150
|
+
so policy and API changes do not spread through application or Ablo code.
|
|
151
|
+
|
|
152
|
+
## References
|
|
153
|
+
|
|
154
|
+
- [Anthropic Sandbox Runtime](https://github.com/anthropic-experimental/sandbox-runtime)
|
|
155
|
+
- [Agents](../agents.md)
|
|
156
|
+
- [Branch-first development](../branch-development.md)
|
|
157
|
+
- [Coordination](../coordination.md)
|