@abloatai/ablo 0.7.0 → 0.9.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 (181) hide show
  1. package/CHANGELOG.md +72 -1
  2. package/README.md +80 -66
  3. package/dist/BaseSyncedStore.d.ts +73 -0
  4. package/dist/BaseSyncedStore.js +179 -5
  5. package/dist/Model.d.ts +42 -0
  6. package/dist/Model.js +103 -44
  7. package/dist/SyncEngineContext.d.ts +2 -1
  8. package/dist/SyncEngineContext.js +5 -3
  9. package/dist/agent/session.js +6 -5
  10. package/dist/ai-sdk/coordination-context.js +4 -0
  11. package/dist/ai-sdk/index.d.ts +56 -47
  12. package/dist/ai-sdk/index.js +56 -47
  13. package/dist/ai-sdk/intent-broadcast.d.ts +5 -0
  14. package/dist/ai-sdk/intent-broadcast.js +11 -4
  15. package/dist/ai-sdk/wrap.d.ts +14 -11
  16. package/dist/ai-sdk/wrap.js +11 -13
  17. package/dist/auth/credentialSource.d.ts +34 -0
  18. package/dist/auth/credentialSource.js +63 -0
  19. package/dist/auth/index.d.ts +2 -22
  20. package/dist/auth/index.js +26 -36
  21. package/dist/auth/schemas.d.ts +35 -0
  22. package/dist/auth/schemas.js +53 -0
  23. package/dist/client/Ablo.d.ts +259 -33
  24. package/dist/client/Ablo.js +276 -73
  25. package/dist/client/ApiClient.d.ts +52 -4
  26. package/dist/client/ApiClient.js +236 -66
  27. package/dist/client/auth.d.ts +21 -2
  28. package/dist/client/auth.js +77 -5
  29. package/dist/client/createInternalComponents.d.ts +2 -0
  30. package/dist/client/createInternalComponents.js +8 -1
  31. package/dist/client/createModelProxy.d.ts +187 -79
  32. package/dist/client/createModelProxy.js +203 -68
  33. package/dist/client/httpClient.d.ts +71 -0
  34. package/dist/client/httpClient.js +69 -0
  35. package/dist/client/identity.d.ts +2 -6
  36. package/dist/client/identity.js +63 -11
  37. package/dist/client/index.d.ts +1 -0
  38. package/dist/client/index.js +1 -0
  39. package/dist/client/registerDataSource.d.ts +19 -0
  40. package/dist/client/registerDataSource.js +59 -0
  41. package/dist/client/validateAbloOptions.d.ts +2 -1
  42. package/dist/client/validateAbloOptions.js +8 -7
  43. package/dist/core/DatabaseManager.js +30 -2
  44. package/dist/core/openIDBWithTimeout.d.ts +36 -0
  45. package/dist/core/openIDBWithTimeout.js +88 -1
  46. package/dist/errorCodes.d.ts +92 -1
  47. package/dist/errorCodes.js +139 -7
  48. package/dist/errors.d.ts +54 -3
  49. package/dist/errors.js +192 -44
  50. package/dist/index.d.ts +23 -10
  51. package/dist/index.js +21 -8
  52. package/dist/keys/index.d.ts +76 -0
  53. package/dist/keys/index.js +171 -0
  54. package/dist/mutators/UndoManager.d.ts +86 -50
  55. package/dist/mutators/UndoManager.js +129 -22
  56. package/dist/mutators/inverseOp.d.ts +129 -0
  57. package/dist/mutators/inverseOp.js +74 -0
  58. package/dist/mutators/readerActions.d.ts +1 -1
  59. package/dist/mutators/undoApply.d.ts +42 -0
  60. package/dist/mutators/undoApply.js +143 -0
  61. package/dist/query/client.d.ts +10 -9
  62. package/dist/query/client.js +22 -14
  63. package/dist/react/AbloProvider.d.ts +23 -101
  64. package/dist/react/AbloProvider.js +61 -103
  65. package/dist/react/ClientSideSuspense.d.ts +1 -1
  66. package/dist/react/DefaultFallback.d.ts +1 -1
  67. package/dist/react/SyncGroupProvider.d.ts +1 -1
  68. package/dist/react/index.d.ts +3 -2
  69. package/dist/react/index.js +3 -2
  70. package/dist/react/useAblo.d.ts +4 -4
  71. package/dist/react/useAblo.js +10 -5
  72. package/dist/react/useCurrentUserId.d.ts +1 -1
  73. package/dist/react/useCurrentUserId.js +1 -1
  74. package/dist/react/useMutators.js +19 -12
  75. package/dist/react/useReactive.js +16 -3
  76. package/dist/schema/ddl.d.ts +26 -3
  77. package/dist/schema/ddl.js +152 -4
  78. package/dist/schema/index.d.ts +4 -0
  79. package/dist/schema/index.js +12 -0
  80. package/dist/schema/model.d.ts +11 -0
  81. package/dist/schema/model.js +2 -0
  82. package/dist/schema/openapi.d.ts +28 -0
  83. package/dist/schema/openapi.js +118 -0
  84. package/dist/schema/plane.d.ts +23 -0
  85. package/dist/schema/plane.js +19 -0
  86. package/dist/schema/relation.d.ts +20 -0
  87. package/dist/schema/serialize.d.ts +7 -3
  88. package/dist/schema/serialize.js +6 -2
  89. package/dist/schema/sync-delta-row.d.ts +157 -0
  90. package/dist/schema/sync-delta-row.js +102 -0
  91. package/dist/schema/sync-delta-wire.d.ts +180 -0
  92. package/dist/schema/sync-delta-wire.js +102 -0
  93. package/dist/server/adapter.d.ts +156 -0
  94. package/dist/server/adapter.js +19 -0
  95. package/dist/server/commit.d.ts +82 -0
  96. package/dist/server/commit.js +1 -0
  97. package/dist/server/index.d.ts +14 -0
  98. package/dist/server/index.js +1 -0
  99. package/dist/server/next.d.ts +51 -0
  100. package/dist/server/next.js +47 -0
  101. package/dist/server/read-config.d.ts +60 -0
  102. package/dist/server/read-config.js +8 -0
  103. package/dist/server/storage-mode.d.ts +17 -0
  104. package/dist/server/storage-mode.js +12 -0
  105. package/dist/source/adapter.d.ts +59 -0
  106. package/dist/source/adapter.js +19 -0
  107. package/dist/source/adapters/drizzle.d.ts +34 -0
  108. package/dist/source/adapters/drizzle.js +147 -0
  109. package/dist/source/adapters/memory.d.ts +12 -0
  110. package/dist/source/adapters/memory.js +114 -0
  111. package/dist/source/adapters/prisma.d.ts +57 -0
  112. package/dist/source/adapters/prisma.js +199 -0
  113. package/dist/source/conformance.d.ts +32 -0
  114. package/dist/source/conformance.js +134 -0
  115. package/dist/source/contract.d.ts +143 -0
  116. package/dist/source/contract.js +98 -0
  117. package/dist/source/index.d.ts +61 -10
  118. package/dist/source/index.js +98 -0
  119. package/dist/source/next.d.ts +33 -0
  120. package/dist/source/next.js +26 -0
  121. package/dist/sync/BootstrapHelper.d.ts +10 -0
  122. package/dist/sync/BootstrapHelper.js +56 -42
  123. package/dist/sync/ConnectionManager.d.ts +57 -1
  124. package/dist/sync/ConnectionManager.js +186 -11
  125. package/dist/sync/HydrationCoordinator.d.ts +93 -17
  126. package/dist/sync/HydrationCoordinator.js +241 -41
  127. package/dist/sync/NetworkProbe.d.ts +60 -18
  128. package/dist/sync/NetworkProbe.js +121 -23
  129. package/dist/sync/SyncWebSocket.d.ts +45 -70
  130. package/dist/sync/SyncWebSocket.js +113 -89
  131. package/dist/sync/createIntentStream.js +10 -1
  132. package/dist/sync/participants.js +5 -2
  133. package/dist/transactions/TransactionQueue.js +13 -1
  134. package/dist/types/streams.d.ts +9 -0
  135. package/dist/utils/mobx-setup.js +1 -0
  136. package/dist/webhooks/events.d.ts +38 -0
  137. package/dist/webhooks/events.js +40 -0
  138. package/dist/webhooks/index.d.ts +10 -0
  139. package/dist/webhooks/index.js +10 -0
  140. package/dist/wire/errorEnvelope.d.ts +34 -0
  141. package/dist/wire/errorEnvelope.js +86 -0
  142. package/dist/wire/frames.d.ts +119 -0
  143. package/dist/wire/frames.js +1 -0
  144. package/dist/wire/index.d.ts +24 -0
  145. package/dist/wire/index.js +21 -0
  146. package/dist/wire/listEnvelope.d.ts +45 -0
  147. package/dist/wire/listEnvelope.js +17 -0
  148. package/docs/api-keys.md +5 -5
  149. package/docs/api.md +125 -65
  150. package/docs/audit.md +16 -9
  151. package/docs/cli.md +57 -47
  152. package/docs/client-behavior.md +54 -40
  153. package/docs/coordination.md +66 -80
  154. package/docs/data-sources.md +56 -34
  155. package/docs/examples/agent-human.md +74 -28
  156. package/docs/examples/ai-sdk-tool.md +29 -22
  157. package/docs/examples/existing-python-backend.md +41 -26
  158. package/docs/examples/nextjs.md +32 -17
  159. package/docs/examples/scoped-agent.md +43 -28
  160. package/docs/examples/server-agent.md +40 -15
  161. package/docs/guarantees.md +38 -27
  162. package/docs/identity.md +65 -59
  163. package/docs/index.md +30 -19
  164. package/docs/integration-guide.md +78 -78
  165. package/docs/interaction-model.md +43 -35
  166. package/docs/mcp/claude-code.md +11 -19
  167. package/docs/mcp/cursor.md +7 -25
  168. package/docs/mcp/windsurf.md +7 -20
  169. package/docs/mcp.md +103 -26
  170. package/docs/quickstart.md +63 -61
  171. package/docs/react.md +24 -16
  172. package/docs/roadmap.md +13 -13
  173. package/docs/schema-contract.md +111 -0
  174. package/docs/the-loop.md +21 -0
  175. package/examples/README.md +8 -4
  176. package/examples/data-source/README.md +10 -7
  177. package/examples/data-source/customer-server.ts +27 -25
  178. package/examples/data-source/run.ts +4 -3
  179. package/examples/quickstart.ts +1 -1
  180. package/llms.txt +55 -21
  181. package/package.json +48 -3
@@ -0,0 +1,53 @@
1
+ import { z } from 'zod';
2
+ import { AbloAuthenticationError } from '../errors.js';
3
+ const AuthParticipantKindSchema = z.enum(['user', 'agent', 'system']);
4
+ export const AuthTokenSchema = z.string().trim().min(1);
5
+ export const CapabilityExchangeResponseSchema = z
6
+ .object({
7
+ capabilityId: z.string().min(1),
8
+ token: AuthTokenSchema,
9
+ expiresAt: z.string().min(1),
10
+ organizationId: z.string().min(1),
11
+ scope: z
12
+ .object({
13
+ organizationId: z.string().min(1),
14
+ syncGroups: z.array(z.string()),
15
+ operations: z.array(z.string()),
16
+ participantKind: AuthParticipantKindSchema,
17
+ participantId: z.string().min(1),
18
+ })
19
+ .passthrough(),
20
+ userMeta: z.record(z.string(), z.unknown()),
21
+ })
22
+ .passthrough();
23
+ export const IdentityResolveResponseSchema = z
24
+ .object({
25
+ participantKind: AuthParticipantKindSchema,
26
+ participantId: z.string().min(1),
27
+ accountScope: z.string().min(1),
28
+ syncGroups: z.array(z.string()),
29
+ userMeta: z.record(z.string(), z.unknown()),
30
+ })
31
+ .passthrough();
32
+ function formatIssues(error) {
33
+ return error.issues
34
+ .map((issue) => {
35
+ const path = issue.path.length > 0 ? issue.path.join('.') : '<root>';
36
+ return `${path}: ${issue.message}`;
37
+ })
38
+ .join('; ');
39
+ }
40
+ export function parseCapabilityExchangeResponse(raw) {
41
+ const parsed = CapabilityExchangeResponseSchema.safeParse(raw);
42
+ if (!parsed.success) {
43
+ throw new AbloAuthenticationError(`apiKey exchange response was malformed: ${formatIssues(parsed.error)}`, { code: 'exchange_malformed_response', cause: parsed.error });
44
+ }
45
+ return parsed.data;
46
+ }
47
+ export function parseIdentityResolveResponse(raw) {
48
+ const parsed = IdentityResolveResponseSchema.safeParse(raw);
49
+ if (!parsed.success) {
50
+ throw new AbloAuthenticationError(`identity resolve response was malformed: ${formatIssues(parsed.error)}`, { code: 'identity_resolve_failed', cause: parsed.error });
51
+ }
52
+ return parsed.data;
53
+ }
@@ -11,9 +11,12 @@
11
11
  * const sync = Ablo({ schema, apiKey: process.env.ABLO_API_KEY });
12
12
  *
13
13
  * const reports = sync.reports.list({ where: { status: 'todo' } });
14
- * await sync.reports.create({ title: 'Fix bug' });
15
- * await sync.reports.update(reportId, { status: 'ready' });
16
- * await sync.reports.delete(reportId);
14
+ * await sync.reports.create({ data: { title: 'Fix bug' } });
15
+ * await sync.reports.update({
16
+ * id: reportId,
17
+ * data: { status: 'ready' },
18
+ * });
19
+ * await sync.reports.delete({ id: reportId });
17
20
  */
18
21
  import type { Schema, SchemaRecord, InferModel, InferCreate } from '../schema/schema.js';
19
22
  import type { SyncEngineConfig, SyncLogger, MutationExecutor, MutationDispatcher, SyncObservabilityProvider, SyncAnalytics, SessionErrorDetector, OnlineStatusProvider } from '../interfaces/index.js';
@@ -23,7 +26,7 @@ import type { SyncWebSocket } from '../sync/SyncWebSocket.js';
23
26
  import { type SyncStatus } from '../BaseSyncedStore.js';
24
27
  import type { IntentStream, IntentWaitOptions, PresenceStream, Snapshot } from '../types/streams.js';
25
28
  import type { ParticipantManager } from '../sync/participants.js';
26
- import type { ActiveIntent, Duration, TargetRange } from '../types/streams.js';
29
+ import type { ActiveIntent, Duration, Intent, TargetRange } from '../types/streams.js';
27
30
  import { type AbloApi, type AbloApiClientOptions, type AbloApiIntents } from './ApiClient.js';
28
31
  /**
29
32
  * Handle returned by `engine.beginTurn()`. While alive, every commit
@@ -71,7 +74,7 @@ import { type AbloPersistence } from './persistence.js';
71
74
  * the way you'd reach for the equivalent option on the Stripe / OpenAI
72
75
  * / Anthropic clients: rarely, and deliberately.
73
76
  *
74
- * @see https://docs.ablo.finance — full option reference
77
+ * @see https://docs.abloatai.com — full option reference
75
78
  */
76
79
  export interface AbloOptions<S extends SchemaRecord = SchemaRecord> {
77
80
  /**
@@ -81,13 +84,55 @@ export interface AbloOptions<S extends SchemaRecord = SchemaRecord> {
81
84
  */
82
85
  schema: Schema<S>;
83
86
  /**
84
- * API key used for authentication.
87
+ * API key **the one auth field most apps set.** Server-side this is your
88
+ * secret `sk_` (and it defaults to `process.env['ABLO_API_KEY']`, so you
89
+ * usually pass nothing). A long-lived key needs no refresh; the client uses
90
+ * it as-is.
85
91
  *
86
- * Accepts a static string (`sk_live_...`) or an async function that
87
- * resolves to one. Defaults to `process.env['ABLO_API_KEY']`, so you
88
- * usually don't pass this explicitly server-side.
92
+ * Accepts a static string or an async `() => Promise<string>` resolver if you
93
+ * rotate keys out-of-band (resolved at bootstrap).
94
+ *
95
+ * Browser apps that mint a SHORT-LIVED per-user key (`ek_`) from a login can't
96
+ * ship a secret — those use {@link getToken} (or {@link authEndpoint}) instead,
97
+ * which the client refreshes for you. That's the only case that isn't "just
98
+ * `apiKey`".
89
99
  */
90
100
  apiKey?: string | ApiKeySetter | null | undefined;
101
+ /**
102
+ * Opt-in for the SHORT-LIVED per-user browser case: an async resolver for a
103
+ * fresh bearer (`ek_`/`rk_`) your backend minted for the signed-in user. The
104
+ * client calls it once before connect and then keeps the key fresh for you —
105
+ * a refresh timer ahead of expiry plus re-mint on OS-wake / network-online /
106
+ * tab-focus, and a reactive re-mint when a probe finds the key stale. You
107
+ * never call a refresh method (Supabase `autoRefreshToken` model).
108
+ *
109
+ * Contract: resolve a token, resolve `null` when the login itself is gone
110
+ * (terminal → sign out), or THROW on a transient failure (→ back off, never
111
+ * sign out). Leave unset for the static-`apiKey` path.
112
+ */
113
+ getToken?: (() => Promise<string | null>) | undefined;
114
+ /**
115
+ * Convenience over {@link getToken}: a URL on YOUR backend that returns
116
+ * `{ token }`. The client POSTs to it (with cookies, so it's authed by the
117
+ * user's session) to mint + refresh the bearer. Ignored when `getToken` is
118
+ * set. Pure sugar — `getToken: () => fetch(url).then(r => r.json()).then(b => b.token)`.
119
+ */
120
+ authEndpoint?: string | undefined;
121
+ /**
122
+ * Direct-URL convenience connector: a connection string to your own Postgres
123
+ * that Ablo can register for a dedicated tenant.
124
+ *
125
+ * This is NOT the default Data Source path. For the Zero-shaped default, keep
126
+ * `DATABASE_URL` in your app, expose `dataSource(...)`, and let your server
127
+ * write the database while Ablo coordinates the sync stream.
128
+ *
129
+ * SERVER-ONLY: this carries credentials, so it is never sent from the browser
130
+ * — constructing a client with `databaseUrl` and `dangerouslyAllowBrowser`
131
+ * throws. If you opt into this connector, provide a NON-superuser,
132
+ * non-`BYPASSRLS` role; the direct connector rejects privileged roles that
133
+ * cannot enforce RLS.
134
+ */
135
+ databaseUrl?: string | null | undefined;
91
136
  /**
92
137
  * Local persistence mode. Pass `indexeddb` only when you want offline
93
138
  * queueing and a reload-surviving browser cache.
@@ -112,8 +157,8 @@ export interface AbloOptions<S extends SchemaRecord = SchemaRecord> {
112
157
  defaultQuery?: Record<string, string | undefined> | undefined;
113
158
  /**
114
159
  * Client-side use is disabled by default because private API keys should
115
- * not ship to browsers. Set this only when using a publishable/browser-safe
116
- * key or a controlled server proxy.
160
+ * not ship to browsers. Set this only when the browser holds a minted
161
+ * session token (`ek_`/`rk_`) or you route through a controlled server proxy.
117
162
  */
118
163
  dangerouslyAllowBrowser?: boolean | undefined;
119
164
  }
@@ -144,8 +189,8 @@ export interface InternalAbloOptions<S extends SchemaRecord = SchemaRecord> {
144
189
  authToken?: string | null | undefined;
145
190
  /**
146
191
  * Override the default base URL. Defaults to
147
- * `wss://mesh.ablo.finance` for hosted production; pass an explicit
148
- * URL for self-hosted or staging (e.g. `wss://mesh-staging.ablo.finance`).
192
+ * `wss://api.abloatai.com` for hosted production; pass an explicit
193
+ * URL for self-hosted or private deployments.
149
194
  */
150
195
  baseURL?: string | null | undefined;
151
196
  /**
@@ -168,7 +213,7 @@ export interface InternalAbloOptions<S extends SchemaRecord = SchemaRecord> {
168
213
  * Client-side use of this SDK is disabled by default — your apiKey
169
214
  * would ship to every visitor's network tab. Only set this to
170
215
  * `true` if you've understood the risk and have appropriate
171
- * mitigations (a publishable key, a server-side proxy, etc).
216
+ * mitigations (a minted session token, a server-side proxy, etc).
172
217
  */
173
218
  dangerouslyAllowBrowser?: boolean | undefined;
174
219
  /**
@@ -179,6 +224,18 @@ export interface InternalAbloOptions<S extends SchemaRecord = SchemaRecord> {
179
224
  * only for the advanced Model / Claim / Commit client.
180
225
  */
181
226
  schema: Schema<S>;
227
+ /**
228
+ * Short-lived-bearer resolver for the per-user browser path (mirrors the
229
+ * public {@link AbloOptions.getToken}). The client mints the first token
230
+ * before connect and refreshes it (timer + wake/online/focus) — see
231
+ * {@link resolveCredentialResolver}.
232
+ */
233
+ getToken?: (() => Promise<string | null>) | undefined;
234
+ /**
235
+ * Backend URL returning `{ token }`; sugar over {@link getToken}. Mirrors the
236
+ * public {@link AbloOptions.authEndpoint}.
237
+ */
238
+ authEndpoint?: string | undefined;
182
239
  /**
183
240
  * @deprecated Server derives participant kind from the apiKey's
184
241
  * scope. Pass apiKey only; this option will be removed once the
@@ -274,8 +331,8 @@ export interface InternalAbloOptions<S extends SchemaRecord = SchemaRecord> {
274
331
  * `commit` method against `${url}/graphql`) with one that uses your own
275
332
  * GraphQL client, auth headers, retry policy, and observability hooks.
276
333
  *
277
- * Default: a fetch-based executor that targets `${url}/graphql` with
278
- * `credentials: 'include'` (cookie auth) when no `apiKey` is set.
334
+ * Default: a fetch-based executor that targets `${url}/graphql` and sends
335
+ * the configured bearer (`apiKey` / backend-minted token) as `Authorization`.
279
336
  */
280
337
  mutationExecutor?: MutationExecutor;
281
338
  /**
@@ -321,18 +378,14 @@ export interface InternalAbloOptions<S extends SchemaRecord = SchemaRecord> {
321
378
  * Operations available on each model in the sync engine.
322
379
  *
323
380
  * Naming aligns with Stripe / OpenAI / Anthropic conventions:
324
- * `retrieve(id)` — single entity by id (sync, from local pool)
325
- * `list({where})` — collection with filter (sync, from local pool)
326
- * `count({where})` count (sync, from local pool)
327
- * `load({where})` async hydrate through pool IDB network
328
- * `create / update / delete` — optimistic writes
329
- *
330
- * The old verb set (`findById`, `findMany`, `findFirst`) is kept as
331
- * deprecated aliases for one release cycle so consumers can migrate
332
- * without a flag day.
381
+ * `retrieve({ id })` — async single-row server read
382
+ * `list({ where })` — async collection server read
383
+ * `get(id)` / `getAll(...)` / `getCount(...)` local graph snapshots
384
+ * `create({ data })` / `update({ id, data })` / `delete({ id })` — writes
385
+ * `claim({ id })` — durable claim handle for coordinated writes
333
386
  */
334
- export type { ModelCountOptions, ModelListOptions, ModelListScope, ModelLoadOptions, ClaimOptions, ClaimedRow, ModelOperations, } from './createModelProxy.js';
335
- import type { ModelOperations } from './createModelProxy.js';
387
+ export type { ModelCountOptions, ModelListOptions, ModelListScope, ModelLoadOptions, ModelRetrieveParams, ModelCreateParams, ModelUpdateParams, ModelDeleteParams, ClaimOptions, ClaimParams, ClaimLookupParams, ClaimReorderParams, ClaimHandle, ModelOperations, } from './createModelProxy.js';
388
+ import type { ModelOperations, ClaimOptions, ClaimParams, ClaimLookupParams, ClaimReorderParams, ClaimHandle, ModelLoadOptions } from './createModelProxy.js';
336
389
  export type ModelOperationAction = 'create' | 'update' | 'delete' | 'archive' | 'unarchive';
337
390
  export type CommitWait = 'queued' | 'confirmed';
338
391
  export interface ModelTarget {
@@ -349,6 +402,7 @@ export interface ModelClaim {
349
402
  readonly actor: string;
350
403
  readonly participantKind: ActiveIntent['participantKind'];
351
404
  readonly action: string;
405
+ readonly description?: string;
352
406
  readonly field?: string;
353
407
  readonly status?: 'active' | 'queued';
354
408
  readonly position?: number;
@@ -450,14 +504,130 @@ export interface ModelMutationOptions extends ClaimedOptions {
450
504
  readonly readAt?: number | null;
451
505
  readonly onStale?: 'reject' | 'force' | 'flag' | 'merge' | null;
452
506
  readonly wait?: CommitWait;
507
+ readonly claim?: ClaimHandle | ClaimOptions | null;
508
+ }
509
+ /**
510
+ * The HTTP/stateless claim surface. Normal tools usually put `claim` directly
511
+ * on the write (`update({ id, data, claim })`) and let the SDK release it. Use
512
+ * this namespace for multi-step handles and coordination screens.
513
+ */
514
+ export interface HttpClaimApi<T> {
515
+ /** Take a manual claim handle for multi-step work. Release it when done. */
516
+ (params: ClaimParams<T>): Promise<ClaimHandle<T>>;
517
+ /** Release a manual claim you hold. */
518
+ release(params: ClaimLookupParams<T> | ClaimHandle<T>): Promise<void>;
519
+ /**
520
+ * Current holder of the lease on a row, or `null` when free. For UI badges,
521
+ * preflight checks, and operators.
522
+ */
523
+ state(params: ClaimLookupParams<T>): Promise<Intent | null>;
524
+ /**
525
+ * FIFO wait line behind the holder. Advanced: useful for operator UIs and
526
+ * schedulers.
527
+ */
528
+ queue(params: ClaimLookupParams<T>): Promise<{
529
+ readonly object: 'list';
530
+ readonly data: readonly Intent[];
531
+ }>;
532
+ /**
533
+ * Re-rank the wait line. Advanced and permission-gated.
534
+ */
535
+ reorder(params: ClaimReorderParams<T>): Promise<void>;
453
536
  }
454
537
  export interface ModelClient<T = Record<string, unknown>> {
455
- retrieve(id: string, options?: ModelReadOptions): Promise<ModelRead<T>>;
456
- create(data: Record<string, unknown>, options?: ModelMutationOptions & {
538
+ retrieve(params: ModelReadOptions & {
539
+ readonly id: string;
540
+ }): Promise<ModelRead<T>>;
541
+ /**
542
+ * Collection read over HTTP (server round-trip). Equality `where`, `orderBy`,
543
+ * `limit`. Present on the stateless protocol client; the store-backed
544
+ * `.model(name)` accessor omits it (use the typed `ablo.<model>.list` there).
545
+ */
546
+ list?(options?: ModelLoadOptions<T>): Promise<T[]>;
547
+ create(params: ModelMutationOptions & {
548
+ readonly data: Record<string, unknown>;
457
549
  readonly id?: string | null;
458
550
  }): Promise<CommitReceipt>;
459
- update(id: string, data: Record<string, unknown>, options?: ModelMutationOptions): Promise<CommitReceipt>;
460
- delete(id: string, options?: ModelMutationOptions): Promise<CommitReceipt>;
551
+ update(params: ModelMutationOptions & {
552
+ readonly id: string;
553
+ readonly data: Record<string, unknown>;
554
+ }): Promise<CommitReceipt>;
555
+ delete(params: ModelMutationOptions & {
556
+ readonly id: string;
557
+ }): Promise<CommitReceipt>;
558
+ /**
559
+ * Durable lease + FIFO wait-line over HTTP — coordination without a socket.
560
+ * Present on the stateless protocol client (`Ablo({ schema: null })` /
561
+ * `createAbloHttpClient`); the store-backed `.model(name)` accessor omits it
562
+ * (the typed `ablo.<model>.claim` proxy is the full reactive namespace there).
563
+ */
564
+ claim?: HttpClaimApi<T>;
565
+ }
566
+ /** A single data operation a scoped **agent** session may perform on a model. */
567
+ export type SessionOperation = 'read' | 'create' | 'update' | 'delete';
568
+ /** Mint params for an **end-user** session — full data authority within the
569
+ * org (the Stripe `ephemeralKeys.create` / Supabase session shape). Mints an
570
+ * `ek_` token. `user.id` is your end user's external IdP id (becomes the
571
+ * session's `participantId`); Ablo does not model your users, so it's an
572
+ * honest string at the trust boundary. */
573
+ export interface CreateUserSessionParams {
574
+ /** Your end user. `id` becomes the token's `participantId`. */
575
+ user: {
576
+ id: string;
577
+ };
578
+ /** Sync groups this session may subscribe to. Omit to inherit the key's scope. */
579
+ syncGroups?: readonly string[];
580
+ /** Token lifetime in seconds. Defaults to 900 (15m, the Stripe ephemeral default). */
581
+ ttlSeconds?: number;
582
+ /** Opaque identity blob echoed back to the client as `ablo.user`. */
583
+ userMeta?: Record<string, unknown>;
584
+ agent?: never;
585
+ can?: never;
586
+ }
587
+ /** Mint params for a scoped **agent** session — mints a restricted `rk_` token
588
+ * gated to exactly the operations named in `can`. `can` is typed off your
589
+ * schema (no magic `'task.update'` strings): `{ Task: ['update'], Deck: ['read'] }`
590
+ * — the SDK serializes each entry to the wire allowlist (`task.update`). */
591
+ export interface CreateAgentSessionParams<S extends SchemaRecord> {
592
+ /** Your agent. `id` becomes the token's `participantId`. */
593
+ agent: {
594
+ id: string;
595
+ };
596
+ /** Per-model operation allowlist, typed against the schema's model names. */
597
+ can: {
598
+ [M in keyof S & string]?: readonly SessionOperation[];
599
+ };
600
+ /** Sync groups this session may subscribe to. Omit to inherit the key's scope. */
601
+ syncGroups?: readonly string[];
602
+ /** Token lifetime in seconds. Defaults to 900 (15m, the Stripe ephemeral default). */
603
+ ttlSeconds?: number;
604
+ /** Opaque identity blob echoed back to the client as `ablo.agent`. */
605
+ userMeta?: Record<string, unknown>;
606
+ user?: never;
607
+ }
608
+ /** Params for {@link Ablo.sessions}.create — a discriminated union: pass
609
+ * `{ user }` for a full-authority end-user session (`ek_`) or `{ agent, can }`
610
+ * for a scoped agent session (`rk_`). */
611
+ export type CreateSessionParams<S extends SchemaRecord> = CreateUserSessionParams | CreateAgentSessionParams<S>;
612
+ /** A minted end-user session token — the Stripe ephemeral-key / Supabase
613
+ * session resource. `token` is the secret the browser presents as its bearer. */
614
+ export interface AbloSession {
615
+ object: 'session';
616
+ /** Stable id of the minted credential (for revocation). */
617
+ id: string;
618
+ /** The short-lived `rk_` session token. Hand this to the user's browser. */
619
+ token: string;
620
+ /** ISO-8601 expiry. */
621
+ expiresAt: string;
622
+ organizationId: string;
623
+ scope: {
624
+ organizationId: string;
625
+ syncGroups: readonly string[];
626
+ operations: readonly string[];
627
+ participantKind: 'user' | 'agent' | 'system';
628
+ participantId: string;
629
+ };
630
+ userMeta: Record<string, unknown>;
461
631
  }
462
632
  /** The typed sync engine client — one property per model in the schema */
463
633
  export type Ablo<S extends SchemaRecord> = {
@@ -491,8 +661,8 @@ export type Ablo<S extends SchemaRecord> = {
491
661
  * offline, this waits until reconnect + flush completes.
492
662
  *
493
663
  * ```ts
494
- * await sync.reports.create({ title: 'A' });
495
- * await sync.reports.create({ title: 'B' });
664
+ * await sync.reports.create({ data: { title: 'A' } });
665
+ * await sync.reports.create({ data: { title: 'B' } });
496
666
  * await sync.waitForFlush(); // server has both reports
497
667
  * ```
498
668
  *
@@ -502,6 +672,59 @@ export type Ablo<S extends SchemaRecord> = {
502
672
  waitForFlush(timeoutMs?: number): Promise<void>;
503
673
  /** Disconnect and clean up */
504
674
  dispose(): Promise<void>;
675
+ /**
676
+ * Replace the bearer auth token used for the WebSocket upgrade and HTTP
677
+ * requests, WITHOUT tearing down the engine. Use to push a refreshed
678
+ * short-lived access key (the Stripe-style `ek_`/`rk_`) before it expires —
679
+ * `<AbloProvider>`'s `getToken` refresh loop calls this. Reuses the same
680
+ * rotation path as the internal capability-token refresh; safe to call before
681
+ * `ready()`. Also nudges a parked connection to re-probe with the new token.
682
+ */
683
+ setAuthToken(token: string): void;
684
+ /**
685
+ * Resolve the active bearer credential this engine authenticates with — the
686
+ * live `ek_`/`rk_` the WebSocket and HTTP transports currently carry (kept
687
+ * fresh by the `getToken` refresh loop), falling back to a configured API
688
+ * key. Returns `null` when no credential is set yet. Use it to authenticate
689
+ * a side-band request to the same sync-server (e.g. the S3 presign endpoint)
690
+ * with the very token this client already holds — no extra mint round-trip.
691
+ */
692
+ getAuthToken(): Promise<string | null>;
693
+ /**
694
+ * Register a re-mint hook for the short-lived access key. The connection
695
+ * layer calls it WHEN it finds the key stale (a `credential_stale` probe) or
696
+ * on an external nudge; the hook mints a fresh `ek_`/`rk_` from the still-valid
697
+ * login. Mirrors the `getToken` contract: resolve a token, resolve `null` when
698
+ * the login itself is gone (→ sign out), or THROW on a transient failure (→
699
+ * back off, never sign out). `<AbloProvider>` wires this from its
700
+ * `getToken`/`authEndpoint`. Safe to call before `ready()`.
701
+ */
702
+ setCredentialRefresher(refresher: (() => Promise<string | null>) | null): void;
703
+ /**
704
+ * Ask the connection layer to re-probe and reconnect now, using the current
705
+ * credential. Idempotent and safe in any state (a no-op while connected).
706
+ * Call after an OS-wake signal (Electron `powerMonitor` 'resume') so a
707
+ * connection parked since sleep recovers immediately instead of waiting for
708
+ * the watchdog.
709
+ */
710
+ nudgeReconnect(): void;
711
+ /**
712
+ * Mint a short-lived, scoped **session token** for one end user — the
713
+ * Stripe `ephemeralKeys.create` / Supabase session shape. Call this on YOUR
714
+ * BACKEND (where the `sk_` secret key lives), then hand the returned
715
+ * `token` to that user's browser (typically via an authEndpoint the client
716
+ * fetches). The browser presents it as the bearer; the sync-server verifies
717
+ * the scoped `rk_` token via `apiKeyProvider`.
718
+ *
719
+ * The browser must NEVER see the `sk_` key — only the per-user session token.
720
+ *
721
+ * Pass `{ user: { id } }` for a full-authority end-user session (mints `ek_`),
722
+ * or `{ agent: { id }, can: { Task: ['update'] } }` for a scoped agent
723
+ * session (mints `rk_`); `can` is typed against your schema's model names.
724
+ */
725
+ sessions: {
726
+ create(params: CreateSessionParams<S>): Promise<AbloSession>;
727
+ };
505
728
  /**
506
729
  * Destroy every IndexedDB database owned by this engine. Disconnects
507
730
  * the WebSocket, releases timers, and deletes all `ablo_*` / `ablo-*`
@@ -766,6 +989,8 @@ export declare namespace Ablo {
766
989
  type CapabilityRecord = import('./ApiClient.js').CapabilityRecord;
767
990
  type CapabilityResource = import('./ApiClient.js').CapabilityResource;
768
991
  type CapabilityRevocation = import('./ApiClient.js').CapabilityRevocation;
992
+ type CapabilityRotateOptions = import('./ApiClient.js').CapabilityRotateOptions;
993
+ type RotatedCapability = import('./ApiClient.js').RotatedCapability;
769
994
  type Task = import('./ApiClient.js').Task;
770
995
  type TaskCreateOptions = import('./ApiClient.js').TaskCreateOptions;
771
996
  type TaskCloseOptions = import('./ApiClient.js').TaskCloseOptions;
@@ -838,6 +1063,7 @@ export declare namespace Ablo {
838
1063
  namespace Source {
839
1064
  type Operation = import('../source/index.js').SourceOperation;
840
1065
  type Event = import('../source/index.js').SourceEvent;
1066
+ type EventForOperationOptions = import('../source/index.js').SourceEventForOperationOptions;
841
1067
  type EventsResult = import('../source/index.js').SourceEventsResult;
842
1068
  type Scope = import('../source/index.js').SourceScope;
843
1069
  type ApiKey = import('../source/index.js').SourceApiKey;