@abloatai/ablo 0.5.1 → 0.7.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 (129) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/README.md +248 -124
  3. package/dist/BaseSyncedStore.d.ts +3 -3
  4. package/dist/BaseSyncedStore.js +3 -3
  5. package/dist/api/index.d.ts +3 -3
  6. package/dist/api/index.js +1 -1
  7. package/dist/client/Ablo.d.ts +91 -93
  8. package/dist/client/Ablo.js +122 -60
  9. package/dist/client/ApiClient.d.ts +14 -14
  10. package/dist/client/ApiClient.js +81 -55
  11. package/dist/client/createInternalComponents.d.ts +2 -3
  12. package/dist/client/createInternalComponents.js +2 -3
  13. package/dist/client/createModelProxy.d.ts +116 -90
  14. package/dist/client/createModelProxy.js +128 -128
  15. package/dist/client/index.d.ts +6 -7
  16. package/dist/client/index.js +4 -5
  17. package/dist/client/validateAbloOptions.js +5 -5
  18. package/dist/coordination/index.d.ts +6 -0
  19. package/dist/coordination/index.js +6 -0
  20. package/dist/coordination/schema.d.ts +329 -0
  21. package/dist/coordination/schema.js +209 -0
  22. package/dist/core/QueryView.d.ts +4 -1
  23. package/dist/core/QueryView.js +1 -1
  24. package/dist/core/index.d.ts +2 -0
  25. package/dist/core/index.js +7 -0
  26. package/dist/core/query-utils.d.ts +7 -10
  27. package/dist/core/query-utils.js +2 -3
  28. package/dist/errorCodes.d.ts +264 -0
  29. package/dist/errorCodes.js +251 -0
  30. package/dist/errors.d.ts +59 -14
  31. package/dist/errors.js +73 -12
  32. package/dist/index.d.ts +11 -9
  33. package/dist/index.js +8 -12
  34. package/dist/interfaces/index.d.ts +2 -10
  35. package/dist/mutators/Transaction.d.ts +2 -2
  36. package/dist/mutators/Transaction.js +2 -2
  37. package/dist/mutators/mutateActions.d.ts +44 -0
  38. package/dist/{react/useMutate.js → mutators/mutateActions.js} +11 -28
  39. package/dist/mutators/readerActions.d.ts +32 -0
  40. package/dist/{react/useReader.js → mutators/readerActions.js} +2 -18
  41. package/dist/policy/index.d.ts +1 -1
  42. package/dist/policy/index.js +1 -1
  43. package/dist/policy/types.d.ts +31 -0
  44. package/dist/policy/types.js +15 -0
  45. package/dist/query/types.d.ts +1 -1
  46. package/dist/react/AbloProvider.d.ts +13 -1
  47. package/dist/react/AbloProvider.js +14 -6
  48. package/dist/react/context.d.ts +4 -4
  49. package/dist/react/index.d.ts +4 -5
  50. package/dist/react/index.js +3 -7
  51. package/dist/react/useAblo.d.ts +14 -14
  52. package/dist/react/useAblo.js +26 -26
  53. package/dist/react/useIntent.d.ts +2 -2
  54. package/dist/react/useIntent.js +2 -2
  55. package/dist/react/useMutators.d.ts +1 -1
  56. package/dist/react/usePresence.d.ts +3 -3
  57. package/dist/react/usePresence.js +4 -4
  58. package/dist/react/useUndoScope.d.ts +1 -1
  59. package/dist/schema/ddl.d.ts +62 -0
  60. package/dist/schema/ddl.js +317 -0
  61. package/dist/schema/diff.d.ts +167 -0
  62. package/dist/schema/diff.js +280 -0
  63. package/dist/schema/field.d.ts +16 -19
  64. package/dist/schema/field.js +30 -17
  65. package/dist/schema/generate.d.ts +19 -0
  66. package/dist/schema/generate.js +87 -0
  67. package/dist/schema/index.d.ts +9 -3
  68. package/dist/schema/index.js +14 -2
  69. package/dist/schema/model.d.ts +87 -25
  70. package/dist/schema/model.js +33 -3
  71. package/dist/schema/relation.d.ts +17 -0
  72. package/dist/schema/roles.d.ts +148 -0
  73. package/dist/schema/roles.js +149 -0
  74. package/dist/schema/schema.d.ts +10 -69
  75. package/dist/schema/schema.js +58 -24
  76. package/dist/schema/select.d.ts +25 -0
  77. package/dist/schema/select.js +55 -0
  78. package/dist/schema/serialize.d.ts +96 -0
  79. package/dist/schema/serialize.js +231 -0
  80. package/dist/schema/sugar.d.ts +20 -3
  81. package/dist/schema/sugar.js +5 -1
  82. package/dist/schema/tenancy.d.ts +66 -0
  83. package/dist/schema/tenancy.js +58 -0
  84. package/dist/sync/HydrationCoordinator.d.ts +2 -0
  85. package/dist/sync/HydrationCoordinator.js +23 -17
  86. package/dist/sync/SyncWebSocket.d.ts +17 -0
  87. package/dist/sync/SyncWebSocket.js +46 -1
  88. package/dist/sync/awaitIntentGrant.d.ts +26 -0
  89. package/dist/sync/awaitIntentGrant.js +60 -0
  90. package/dist/sync/createIntentStream.d.ts +2 -1
  91. package/dist/sync/createIntentStream.js +89 -5
  92. package/dist/sync/createPresenceStream.js +1 -1
  93. package/dist/sync/participants.d.ts +2 -2
  94. package/dist/sync/participants.js +9 -18
  95. package/dist/types/global.d.ts +43 -52
  96. package/dist/types/global.js +16 -18
  97. package/dist/types/streams.d.ts +90 -42
  98. package/docs/api-keys.md +44 -0
  99. package/docs/api.md +72 -173
  100. package/docs/audit.md +5 -5
  101. package/docs/cli.md +212 -0
  102. package/docs/client-behavior.md +42 -43
  103. package/docs/coordination.md +343 -0
  104. package/docs/data-sources.md +16 -16
  105. package/docs/examples/agent-human.md +30 -32
  106. package/docs/examples/ai-sdk-tool.md +32 -33
  107. package/docs/examples/existing-python-backend.md +38 -36
  108. package/docs/examples/nextjs.md +24 -25
  109. package/docs/examples/scoped-agent.md +78 -0
  110. package/docs/examples/server-agent.md +20 -61
  111. package/docs/guarantees.md +34 -56
  112. package/docs/identity.md +529 -0
  113. package/docs/index.md +18 -24
  114. package/docs/integration-guide.md +130 -144
  115. package/docs/interaction-model.md +32 -95
  116. package/docs/mcp/claude-code.md +3 -3
  117. package/docs/mcp/cursor.md +1 -1
  118. package/docs/mcp/windsurf.md +1 -1
  119. package/docs/mcp.md +11 -26
  120. package/docs/quickstart.md +43 -49
  121. package/docs/react.md +74 -24
  122. package/docs/roadmap.md +17 -7
  123. package/llms.txt +34 -39
  124. package/package.json +8 -1
  125. package/dist/react/useMutate.d.ts +0 -83
  126. package/dist/react/useQuery.d.ts +0 -123
  127. package/dist/react/useQuery.js +0 -145
  128. package/dist/react/useReader.d.ts +0 -69
  129. package/docs/capabilities.md +0 -163
@@ -1,26 +1,26 @@
1
1
  /**
2
- * Per-model resource factory.
2
+ * Per-model client factory.
3
3
  *
4
- * Mirrors Anthropic SDK's `resources/messages.ts` / `resources/models.ts`
5
- * pattern: each resource has its own file, the client just instantiates
4
+ * Mirrors Anthropic SDK's per-endpoint module pattern: each model client
5
+ * has its own file, and the root client just instantiates
6
6
  * one per model. Extracted from `Ablo.ts` so the proxy logic is
7
7
  * testable in isolation and the constructor doesn't carry it.
8
8
  *
9
9
  * Each schema model gets one `ModelOperations<T, CreateInput>` —
10
10
  * exposes `retrieve`, `list`, `count`, `create`, `update`, `delete`,
11
- * `intent` (the coordination handle), `subscribe`, and `load`. The
12
- * factory returns a plain object; the client assembles the
11
+ * `claim`, `claimState`, `queue`, `release`, `subscribe`, and `load`.
12
+ * The factory returns a plain object; the client assembles the
13
13
  * `ablo.<model>` lookup table from these.
14
14
  */
15
15
  import { autorun } from 'mobx';
16
- import { AbloStaleContextError, AbloValidationError } from '../errors.js';
16
+ import { AbloClaimedError, AbloValidationError } from '../errors.js';
17
17
  import { Model, modelAsRow } from '../Model.js';
18
18
  import { ModelScope } from '../types/index.js';
19
- const modelResourceMeta = new WeakMap();
20
- export function getModelResourceMeta(resource) {
21
- if (typeof resource !== 'object' || resource === null)
19
+ const modelClientMeta = new WeakMap();
20
+ export function getModelClientMeta(modelClient) {
21
+ if (typeof modelClient !== 'object' || modelClient === null)
22
22
  return undefined;
23
- return modelResourceMeta.get(resource);
23
+ return modelClientMeta.get(modelClient);
24
24
  }
25
25
  export function createModelProxy(schemaKey, registeredModelName, objectPool, syncClient, registry, hydration, collaboration) {
26
26
  const ModelClass = registry.getModelByName(registeredModelName);
@@ -42,12 +42,99 @@ export function createModelProxy(schemaKey, registeredModelName, objectPool, syn
42
42
  await syncClient.syncNow();
43
43
  await syncClient.waitForConfirmation(model.getModelName(), model.id);
44
44
  };
45
+ // Claims this proxy currently holds, keyed by entity id. Lets the flat
46
+ // `release(id)` and `update(id)` find the lease + snapshot a `claim(id)`
47
+ // took — no per-call handle. Released on dispose, explicit release, or TTL.
48
+ const activeClaims = new Map();
49
+ const releaseClaim = async (id) => {
50
+ const held = activeClaims.get(id);
51
+ if (!held)
52
+ return;
53
+ activeClaims.delete(id);
54
+ await held.lease.release();
55
+ };
56
+ const takeClaim = async (id, options) => {
57
+ if (!collaboration) {
58
+ throw new AbloValidationError(`Model "${schemaKey}" cannot claim a row without collaboration wiring.`, { code: 'model_claim_not_configured' });
59
+ }
60
+ // Is someone ELSE already on this target? Read the local coordination
61
+ // snapshot up front — it decides whether we'll need to re-read after the
62
+ // claim (a free / already-mine target can't have changed under us).
63
+ const held = collaboration.observe({ model: schemaKey, id });
64
+ const contended = !!held && held.heldBy !== collaboration.selfParticipantId;
65
+ const failFast = options?.wait === false;
66
+ // Fail-fast (`wait: false`): if another participant already holds it,
67
+ // reject now instead of queuing. Best-effort at the client (a racing
68
+ // claim not yet synced into our snapshot slips through here) — the
69
+ // commit-time intent guard is the authoritative backstop that rejects
70
+ // the loser's first write. For work-distribution dedup that's exactly
71
+ // right: don't wait (that would double-process), skip.
72
+ if (failFast && contended) {
73
+ throw new AbloClaimedError(`${registeredModelName}/${id} is held by ${held?.heldBy ?? 'another participant'}.`, { code: 'entity_claimed' });
74
+ }
75
+ // Ensure the row exists locally before claiming.
76
+ let model = objectPool.get(id);
77
+ if (!model) {
78
+ await load({ where: { id } });
79
+ model = objectPool.get(id);
80
+ }
81
+ if (!model) {
82
+ throw new AbloValidationError(`Entity not found: ${registeredModelName}/${id}`, { code: 'entity_not_found' });
83
+ }
84
+ // Acquire the lease. Default (`wait` !== false) goes through the server's
85
+ // fair FIFO queue — `queue: true` resolves only once the lease is genuinely
86
+ // ours, blocking behind any current holder, with no TOCTOU gap (the server
87
+ // orders contenders). Fail-fast skips the queue: we already rejected an
88
+ // observed conflict above, so this just records our lease.
89
+ const lease = await collaboration.createIntent({
90
+ target: {
91
+ model: schemaKey,
92
+ id,
93
+ ...(options?.field ? { field: options.field } : {}),
94
+ },
95
+ action: options?.action ?? 'editing',
96
+ ttl: options?.ttl,
97
+ queue: !failFast,
98
+ maxQueueDepth: options?.maxQueueDepth,
99
+ });
100
+ // Only when we actually waited behind another holder can the row have
101
+ // changed underneath us — re-read so the claimed snapshot reflects what
102
+ // they committed before releasing.
103
+ if (contended && !failFast) {
104
+ await load({ where: { id } });
105
+ model = objectPool.get(id) ?? model;
106
+ }
107
+ const snapshot = collaboration.createSnapshot(schemaKey, id);
108
+ activeClaims.set(id, { lease, snapshot });
109
+ const row = modelAsRow(model);
110
+ // `await using` calls this on scope exit; releases the claim.
111
+ Object.defineProperty(row, Symbol.asyncDispose, {
112
+ value: () => releaseClaim(id),
113
+ enumerable: false,
114
+ configurable: true,
115
+ });
116
+ return row;
117
+ };
118
+ function claim(id, a, b) {
119
+ if (typeof a === 'function') {
120
+ return (async () => {
121
+ const row = await takeClaim(id, b);
122
+ try {
123
+ return await a(row);
124
+ }
125
+ finally {
126
+ await releaseClaim(id);
127
+ }
128
+ })();
129
+ }
130
+ return takeClaim(id, a);
131
+ }
45
132
  const operations = {
46
133
  retrieve(id) {
47
134
  return objectPool.get(id);
48
135
  },
49
136
  list(options) {
50
- const all = objectPool.getByType(ModelClass, (options?.scope ?? ModelScope.live));
137
+ const all = objectPool.getByType(ModelClass, (options?.state ?? ModelScope.live));
51
138
  let result = all;
52
139
  if (options?.where) {
53
140
  const where = options.where;
@@ -99,9 +186,21 @@ export function createModelProxy(schemaKey, registeredModelName, objectPool, syn
99
186
  const model = objectPool.get(id);
100
187
  if (!model)
101
188
  throw new AbloValidationError(`Entity not found: ${registeredModelName}/${id}`, { code: 'entity_not_found' });
189
+ // If we hold a claim on this row, guard the write with its snapshot
190
+ // watermark + lease so it's stale-rejected and attributed to the claim.
191
+ const claimed = activeClaims.get(id);
192
+ const effective = claimed
193
+ ? {
194
+ wait: 'confirmed',
195
+ readAt: claimed.snapshot.stamp,
196
+ onStale: 'reject',
197
+ intent: claimed.lease,
198
+ ...options,
199
+ }
200
+ : options;
102
201
  model.updateFromData(data);
103
- syncClient.update(model, options);
104
- await waitForMutation(model, options);
202
+ syncClient.update(model, effective);
203
+ await waitForMutation(model, effective);
105
204
  return modelAsRow(model);
106
205
  },
107
206
  async delete(id, options) {
@@ -111,122 +210,23 @@ export function createModelProxy(schemaKey, registeredModelName, objectPool, syn
111
210
  syncClient.delete(model, options);
112
211
  await waitForMutation(model, options);
113
212
  },
114
- intent(id) {
115
- const target = { resource: schemaKey, id };
116
- let acquired = null;
117
- let snapshot = null;
118
- let released = false;
119
- let acquireWait;
120
- // Public `cancel()` — drop the claim without committing. Calls the
121
- // lower-level lease handle's `revoke()` (a different API; leave it).
122
- const cancel = () => {
123
- if (released)
124
- return;
125
- released = true;
126
- if (snapshot)
127
- snapshot.signal.removeEventListener('abort', cancel);
128
- acquired?.revoke();
129
- };
130
- // Public `finish()` — give the claim back after committing. Calls the
131
- // lower-level lease handle's `release()` (a different API; leave it).
132
- const finish = async () => {
133
- if (released)
134
- return;
135
- released = true;
136
- if (snapshot)
137
- snapshot.signal.removeEventListener('abort', cancel);
138
- await acquired?.release();
139
- };
140
- const whenFree = async (options) => {
141
- if (!collaboration)
142
- return;
143
- await collaboration.waitFor(target, options);
144
- };
145
- const claim = async (options) => {
146
- if (!collaboration) {
147
- throw new AbloValidationError(`Model "${schemaKey}" cannot claim an intent without collaboration wiring.`, { code: 'model_intent_not_configured' });
148
- }
149
- if (acquired)
150
- return;
151
- acquireWait = options?.wait;
152
- // Load the row so update() has a snapshot to guard against.
153
- let model = objectPool.get(id);
154
- if (!model) {
155
- await load({ where: { id } });
156
- model = objectPool.get(id);
157
- }
158
- if (!model) {
159
- throw new AbloValidationError(`Entity not found: ${registeredModelName}/${id}`, { code: 'entity_not_found' });
160
- }
161
- const snap = collaboration.createSnapshot(schemaKey, id);
162
- snap.signal.addEventListener('abort', cancel, { once: true });
163
- snapshot = snap;
164
- released = false;
165
- acquired = await collaboration.createIntent({
166
- target: {
167
- resource: schemaKey,
168
- id,
169
- ...(options?.field ? { field: options.field } : {}),
170
- },
171
- action: options?.action ?? 'editing',
172
- ttl: options?.ttl,
173
- });
174
- };
175
- const claimOrWait = async (options) => {
176
- if (!collaboration) {
177
- throw new AbloValidationError(`Model "${schemaKey}" cannot claim an intent without collaboration wiring.`, { code: 'model_intent_not_configured' });
178
- }
179
- const held = collaboration.observe(target);
180
- // A foreign holder: wait for them to finish, then re-read before
181
- // claiming. Our own claim (or a free target) goes straight to claim.
182
- if (held && held.heldBy !== collaboration.selfParticipantId) {
183
- await whenFree();
184
- await load({ where: { id } });
185
- }
186
- await claim(options);
187
- };
188
- const handle = {
189
- id,
190
- get current() {
191
- return collaboration?.observe(target) ?? null;
192
- },
193
- get status() {
194
- return collaboration?.observe(target)?.status ?? 'idle';
195
- },
196
- claim,
197
- claimOrWait,
198
- async update(data, updateOptions) {
199
- if (!acquired || !snapshot) {
200
- throw new AbloValidationError(`Call claim() before update() on ablo.${schemaKey}.intent(${id}).`, { code: 'intent_not_acquired' });
201
- }
202
- if (snapshot.signal.aborted) {
203
- throw new AbloStaleContextError(`Intent context is stale for ${schemaKey}/${id}. Re-read the row and retry.`, {
204
- code: 'edit_context_stale',
205
- readAt: snapshot.stamp,
206
- cause: snapshot.signal.reason,
207
- });
208
- }
209
- try {
210
- return await operations.update(id, data, {
211
- wait: acquireWait ?? 'confirmed',
212
- readAt: snapshot.stamp,
213
- onStale: 'reject',
214
- ...updateOptions,
215
- intent: acquired,
216
- });
217
- }
218
- finally {
219
- await finish();
220
- }
221
- },
222
- finish,
223
- whenFree,
224
- cancel,
225
- [Symbol.asyncDispose]: finish,
213
+ claim,
214
+ claimState(id) {
215
+ return collaboration?.observe({ model: schemaKey, id }) ?? null;
216
+ },
217
+ queue(id) {
218
+ return {
219
+ object: 'list',
220
+ data: collaboration?.queue({ model: schemaKey, id }) ?? [],
226
221
  };
227
- return handle;
228
222
  },
229
- subscribe(callback, options) {
223
+ reorder(id, order) {
224
+ collaboration?.reorder({ model: schemaKey, id }, order);
225
+ },
226
+ release(id) {
227
+ return releaseClaim(id);
228
+ },
229
+ onChange(callback, options) {
230
230
  return autorun(() => {
231
231
  const entities = this.list(options);
232
232
  callback(entities);
@@ -234,7 +234,7 @@ export function createModelProxy(schemaKey, registeredModelName, objectPool, syn
234
234
  },
235
235
  load,
236
236
  };
237
- modelResourceMeta.set(operations, {
237
+ modelClientMeta.set(operations, {
238
238
  key: schemaKey,
239
239
  typename: registeredModelName,
240
240
  });
@@ -15,22 +15,21 @@
15
15
  * apiKey: process.env.ABLO_API_KEY,
16
16
  * });
17
17
  *
18
- * const tasks = ablo.tasks.list({ where: { status: 'todo' } });
19
- * await ablo.tasks.create({ title: 'New task' });
18
+ * const reports = ablo.weatherReports.list({ where: { status: 'pending' } });
19
+ * await ablo.weatherReports.create({ location: 'Stockholm', status: 'pending' });
20
20
  * ```
21
21
  *
22
- * For headless agents (workers, bots), pass `kind: 'agent'` plus a
23
- * restricted (`rk_`) API key as `capabilityToken`:
22
+ * For headless agents (workers, bots), pass the same schema and an API key
23
+ * scoped for that server runtime:
24
24
  *
25
25
  * ```ts
26
26
  * const bot = Ablo({
27
27
  * schema,
28
28
  * apiKey: process.env.ABLO_API_KEY,
29
- * kind: 'agent',
30
29
  * });
31
30
  * ```
32
31
  */
33
- export { Ablo, computeFKDepthPriority, type AbloOptions, type InternalAbloOptions, type BusyOptions, type BusyPolicy, type IntentWaitOptions, type ModelCountOptions, type ModelListOptions, type ModelListScope, type ModelLoadOptions, type ModelOperations, type ResourceReadOptions, } from './Ablo.js';
32
+ export { Ablo, computeFKDepthPriority, type AbloOptions, type InternalAbloOptions, type ClaimedOptions, type IfClaimedPolicy, type IntentWaitOptions, type ModelCountOptions, type ModelListOptions, type ModelListScope, type ModelLoadOptions, type ModelOperations, type ModelReadOptions, } from './Ablo.js';
34
33
  export type { AbloPersistence } from './persistence.js';
35
- export type { AbloApi, AbloApiClientOptions, AbloApiIntents, Agent, AgentIntentInput, AgentIntentOptions, AgentOptions, AgentResourceClient, AgentResourceReadOptions, AgentResourceMutationOptions, AgentRunContext, AgentRunDone, AgentRunFailed, AgentRunCancelled, AgentRunOptions, AgentRunResult, AgentRunStatus, Capability, CapabilityCreateOptions, CapabilityParticipantKind, CapabilityRecord, CapabilityResource, CapabilityRevocation, CapabilityScope, Task, TaskCloseOptions, TaskCloseResult, TaskCreateOptions, TaskResource, } from './ApiClient.js';
34
+ export type { AbloApi, AbloApiClientOptions, AbloApiIntents, Agent, AgentIntentInput, AgentIntentOptions, AgentOptions, AgentModelClient, AgentModelReadOptions, AgentModelMutationOptions, AgentRunContext, AgentRunDone, AgentRunFailed, AgentRunCancelled, AgentRunOptions, AgentRunResult, AgentRunStatus, Capability, CapabilityCreateOptions, CapabilityParticipantKind, CapabilityRecord, CapabilityResource, CapabilityRevocation, CapabilityScope, Task, TaskCloseOptions, TaskCloseResult, TaskCreateOptions, TaskResource, } from './ApiClient.js';
36
35
  export type { EngineParticipant, JoinedParticipant, ParticipantJoinOptions, ParticipantManager, ParticipantScope, ParticipantStatus, ScopedIntents, ScopedPresence, } from '../sync/participants.js';
@@ -15,18 +15,17 @@
15
15
  * apiKey: process.env.ABLO_API_KEY,
16
16
  * });
17
17
  *
18
- * const tasks = ablo.tasks.list({ where: { status: 'todo' } });
19
- * await ablo.tasks.create({ title: 'New task' });
18
+ * const reports = ablo.weatherReports.list({ where: { status: 'pending' } });
19
+ * await ablo.weatherReports.create({ location: 'Stockholm', status: 'pending' });
20
20
  * ```
21
21
  *
22
- * For headless agents (workers, bots), pass `kind: 'agent'` plus a
23
- * restricted (`rk_`) API key as `capabilityToken`:
22
+ * For headless agents (workers, bots), pass the same schema and an API key
23
+ * scoped for that server runtime:
24
24
  *
25
25
  * ```ts
26
26
  * const bot = Ablo({
27
27
  * schema,
28
28
  * apiKey: process.env.ABLO_API_KEY,
29
- * kind: 'agent',
30
29
  * });
31
30
  * ```
32
31
  */
@@ -15,11 +15,11 @@ export function validateAbloOptions(input) {
15
15
  const kind = options.kind ?? 'user';
16
16
  if (!url) {
17
17
  return new Error('Ablo: `url` is required. Pass the sync server URL, e.g. ' +
18
- `Ablo({ baseURL: 'wss://sync.ablo.dev', schema, user })`);
18
+ `Ablo({ baseURL: 'wss://sync.abloatai.com', schema, user })`);
19
19
  }
20
- // Schema is optional for the resource-first API:
21
- // Ablo({ apiKey }).resource('clauses').retrieve(...)
22
- // Passing a schema only enables typed model sugar (`ablo.tasks.update(...)`).
20
+ // Schema is optional for the model-first API:
21
+ // Ablo({ apiKey }).model('clauses').retrieve(...)
22
+ // Passing a schema only enables typed model sugar (`ablo.weatherReports.update(...)`).
23
23
  if (!configuredApiKey &&
24
24
  !configuredAuthToken &&
25
25
  !options.capabilityToken &&
@@ -37,7 +37,7 @@ export function validateAbloOptions(input) {
37
37
  if (!configuredApiKey && !configuredAuthToken && kind === 'agent' && !options.capabilityToken) {
38
38
  return new Error('Ablo: provide either `apiKey` (hosted cloud — SDK exchanges internally) ' +
39
39
  'or `capabilityToken` (self-hosted — your auth layer mints + hands in). ' +
40
- 'See https://ablo.dev/docs/api-keys for the full pattern.');
40
+ 'See https://abloatai.com/docs/api-keys for the full pattern.');
41
41
  }
42
42
  return null;
43
43
  }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * `@abloatai/ablo/coordination` — the canonical wire schema for the three
3
+ * coordination layers (presence, pessimistic claims, optimistic stale-context).
4
+ * See `./schema.ts` for the model and the per-layer schemas.
5
+ */
6
+ export * from './schema.js';
@@ -0,0 +1,6 @@
1
+ /**
2
+ * `@abloatai/ablo/coordination` — the canonical wire schema for the three
3
+ * coordination layers (presence, pessimistic claims, optimistic stale-context).
4
+ * See `./schema.ts` for the model and the per-layer schemas.
5
+ */
6
+ export * from './schema.js';