@abloatai/ablo 0.5.1 → 0.6.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 (94) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +217 -122
  3. package/dist/BaseSyncedStore.d.ts +2 -2
  4. package/dist/BaseSyncedStore.js +2 -2
  5. package/dist/api/index.d.ts +3 -3
  6. package/dist/api/index.js +1 -1
  7. package/dist/client/Ablo.d.ts +90 -93
  8. package/dist/client/Ablo.js +121 -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 +90 -87
  14. package/dist/client/createModelProxy.js +124 -127
  15. package/dist/client/index.d.ts +6 -7
  16. package/dist/client/index.js +4 -5
  17. package/dist/client/validateAbloOptions.js +3 -3
  18. package/dist/core/index.d.ts +2 -0
  19. package/dist/core/index.js +7 -0
  20. package/dist/errors.d.ts +8 -8
  21. package/dist/errors.js +18 -10
  22. package/dist/index.d.ts +9 -8
  23. package/dist/index.js +7 -11
  24. package/dist/interfaces/index.d.ts +2 -10
  25. package/dist/mutators/Transaction.d.ts +2 -2
  26. package/dist/mutators/Transaction.js +2 -2
  27. package/dist/mutators/mutateActions.d.ts +44 -0
  28. package/dist/{react/useMutate.js → mutators/mutateActions.js} +11 -28
  29. package/dist/mutators/readerActions.d.ts +32 -0
  30. package/dist/{react/useReader.js → mutators/readerActions.js} +2 -18
  31. package/dist/query/types.d.ts +1 -1
  32. package/dist/react/AbloProvider.d.ts +1 -1
  33. package/dist/react/AbloProvider.js +3 -3
  34. package/dist/react/context.d.ts +4 -4
  35. package/dist/react/index.d.ts +4 -5
  36. package/dist/react/index.js +3 -7
  37. package/dist/react/useAblo.d.ts +14 -14
  38. package/dist/react/useAblo.js +26 -26
  39. package/dist/react/useIntent.d.ts +2 -2
  40. package/dist/react/useIntent.js +2 -2
  41. package/dist/react/useMutators.d.ts +1 -1
  42. package/dist/react/usePresence.d.ts +3 -3
  43. package/dist/react/usePresence.js +4 -4
  44. package/dist/react/useUndoScope.d.ts +1 -1
  45. package/dist/schema/diff.d.ts +161 -0
  46. package/dist/schema/diff.js +262 -0
  47. package/dist/schema/generate.d.ts +19 -0
  48. package/dist/schema/generate.js +87 -0
  49. package/dist/schema/index.d.ts +4 -1
  50. package/dist/schema/index.js +7 -1
  51. package/dist/schema/schema.d.ts +83 -32
  52. package/dist/schema/schema.js +58 -12
  53. package/dist/schema/serialize.d.ts +92 -0
  54. package/dist/schema/serialize.js +227 -0
  55. package/dist/sync/SyncWebSocket.d.ts +17 -0
  56. package/dist/sync/SyncWebSocket.js +46 -1
  57. package/dist/sync/awaitIntentGrant.d.ts +26 -0
  58. package/dist/sync/awaitIntentGrant.js +60 -0
  59. package/dist/sync/createIntentStream.js +43 -4
  60. package/dist/sync/createPresenceStream.js +1 -1
  61. package/dist/sync/participants.d.ts +2 -2
  62. package/dist/sync/participants.js +4 -4
  63. package/dist/types/global.d.ts +43 -52
  64. package/dist/types/global.js +16 -18
  65. package/dist/types/streams.d.ts +37 -9
  66. package/docs/api.md +68 -158
  67. package/docs/audit.md +5 -5
  68. package/docs/client-behavior.md +41 -42
  69. package/docs/coordination.md +294 -0
  70. package/docs/data-sources.md +14 -14
  71. package/docs/examples/agent-human.md +30 -32
  72. package/docs/examples/ai-sdk-tool.md +32 -33
  73. package/docs/examples/existing-python-backend.md +35 -33
  74. package/docs/examples/nextjs.md +24 -25
  75. package/docs/examples/server-agent.md +20 -61
  76. package/docs/guarantees.md +30 -55
  77. package/docs/identity.md +458 -0
  78. package/docs/index.md +12 -24
  79. package/docs/integration-guide.md +106 -116
  80. package/docs/interaction-model.md +29 -95
  81. package/docs/mcp/claude-code.md +3 -3
  82. package/docs/mcp/cursor.md +1 -1
  83. package/docs/mcp/windsurf.md +1 -1
  84. package/docs/mcp.md +11 -26
  85. package/docs/quickstart.md +43 -49
  86. package/docs/react.md +73 -23
  87. package/docs/roadmap.md +5 -7
  88. package/llms.txt +34 -39
  89. package/package.json +1 -1
  90. package/dist/react/useMutate.d.ts +0 -83
  91. package/dist/react/useQuery.d.ts +0 -123
  92. package/dist/react/useQuery.js +0 -145
  93. package/dist/react/useReader.d.ts +0 -69
  94. package/docs/capabilities.md +0 -163
@@ -10,13 +10,13 @@
10
10
  *
11
11
  * const sync = Ablo({ schema, apiKey: process.env.ABLO_API_KEY });
12
12
  *
13
- * const tasks = sync.tasks.list({ where: { status: 'todo' } });
14
- * await sync.tasks.create({ title: 'Fix bug' });
15
- * await sync.tasks.update(taskId, { status: 'done' });
16
- * await sync.tasks.delete(taskId);
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);
17
17
  */
18
18
  import { z } from 'zod';
19
- import { AbloBusyError, AbloError, AbloConnectionError, AbloValidationError, translateHttpError } from '../errors.js';
19
+ import { AbloClaimedError, AbloError, AbloConnectionError, AbloValidationError, translateHttpError } from '../errors.js';
20
20
  import { LoadStrategy, PropertyType } from '../types/index.js';
21
21
  import { initSyncEngine } from '../context.js';
22
22
  import { noopObservability, browserOnlineStatus, defaultSessionErrorDetector, noopAnalytics, } from '../SyncEngineContext.js';
@@ -28,6 +28,7 @@ import { Model } from '../Model.js';
28
28
  import { BaseSyncedStore } from '../BaseSyncedStore.js';
29
29
  import { createPresenceStream } from '../sync/createPresenceStream.js';
30
30
  import { createIntentStream } from '../sync/createIntentStream.js';
31
+ import { awaitIntentGrant } from '../sync/awaitIntentGrant.js';
31
32
  import { createSnapshot } from '../sync/createSnapshot.js';
32
33
  import { createParticipantManager } from '../sync/participants.js';
33
34
  import { createProtocolClient, } from './ApiClient.js';
@@ -581,7 +582,8 @@ function createDefaultMutationExecutor(getWs) {
581
582
  ? crypto.randomUUID()
582
583
  : `tx_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`);
583
584
  try {
584
- return await ws.sendCommit(operations, clientTxId, options?.timeout, options?.causedByTaskId);
585
+ return await ws.sendCommit(operations, clientTxId, undefined, // use sendCommit's built-in 15s default; no per-call override
586
+ options?.causedByTaskId);
585
587
  }
586
588
  catch (err) {
587
589
  // Wrap transport-level failures as connection errors so the
@@ -965,7 +967,7 @@ export function Ablo(options) {
965
967
  ? crypto.randomUUID()
966
968
  : `tx_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
967
969
  }
968
- function createResourceId() {
970
+ function createModelId() {
969
971
  return typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'
970
972
  ? crypto.randomUUID()
971
973
  : `id_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
@@ -976,15 +978,15 @@ export function Ablo(options) {
976
978
  return intent?.id;
977
979
  }
978
980
  function normalizeCommitOperation(op, defaults) {
979
- const resource = op.resource ?? op.target?.resource;
980
- if (!resource) {
981
- throw new AbloValidationError('Commit operation requires `resource` or `target.resource`.', { code: 'commit_operation_resource_required' });
981
+ const model = op.model ?? op.target?.model;
982
+ if (!model) {
983
+ throw new AbloValidationError('Commit operation requires `model` or `target.model`.', { code: 'commit_operation_model_required' });
982
984
  }
983
985
  const type = op.action.toUpperCase();
984
986
  const id = op.id ?? op.target?.id ?? '';
985
987
  return {
986
988
  type,
987
- model: resource.toLowerCase(),
989
+ model: model.toLowerCase(),
988
990
  id,
989
991
  input: op.data ?? undefined,
990
992
  transactionId: op.transactionId ?? undefined,
@@ -1004,16 +1006,17 @@ export function Ablo(options) {
1004
1006
  }
1005
1007
  return inputOperations.map((op) => normalizeCommitOperation(op, commitOptions));
1006
1008
  }
1007
- function resourceIntentFromActive(intent) {
1009
+ function modelClaimFromActive(intent) {
1008
1010
  return {
1009
1011
  id: intent.id,
1010
1012
  actor: intent.heldBy,
1011
1013
  participantKind: intent.participantKind,
1012
1014
  action: intent.reason,
1013
1015
  field: intent.target.field,
1016
+ status: 'active',
1014
1017
  expiresAt: intent.expiresAt,
1015
1018
  target: {
1016
- resource: intent.target.type,
1019
+ model: intent.target.type,
1017
1020
  id: intent.target.id,
1018
1021
  path: intent.target.path,
1019
1022
  range: intent.target.range,
@@ -1022,9 +1025,29 @@ export function Ablo(options) {
1022
1025
  },
1023
1026
  };
1024
1027
  }
1025
- function targetMatchesResource(target, intent) {
1026
- if (target.resource &&
1027
- intent.target.type.toLowerCase() !== target.resource.toLowerCase()) {
1028
+ function modelClaimFromQueued(intent) {
1029
+ return {
1030
+ id: intent.id,
1031
+ actor: intent.heldBy,
1032
+ participantKind: intent.participantKind,
1033
+ action: intent.action,
1034
+ field: intent.target.field,
1035
+ status: 'queued',
1036
+ position: intent.position,
1037
+ expiresAt: intent.expiresAt,
1038
+ target: {
1039
+ model: intent.target.type,
1040
+ id: intent.target.id,
1041
+ path: intent.target.path,
1042
+ range: intent.target.range,
1043
+ field: intent.target.field,
1044
+ meta: intent.target.meta,
1045
+ },
1046
+ };
1047
+ }
1048
+ function targetMatchesModel(target, intent) {
1049
+ if (target.model &&
1050
+ intent.target.type.toLowerCase() !== target.model.toLowerCase()) {
1028
1051
  return false;
1029
1052
  }
1030
1053
  if (target.id && intent.target.id !== target.id)
@@ -1033,21 +1056,29 @@ export function Ablo(options) {
1033
1056
  return false;
1034
1057
  return true;
1035
1058
  }
1036
- function listResourceIntents(target) {
1059
+ function listModelClaims(target) {
1037
1060
  return intentStream.others
1038
- .filter((intent) => (target ? targetMatchesResource(target, intent) : true))
1039
- .map(resourceIntentFromActive);
1061
+ .filter((intent) => (target ? targetMatchesModel(target, intent) : true))
1062
+ .map(modelClaimFromActive);
1040
1063
  }
1041
- function busyError(target, intents, code) {
1042
- const label = [target.resource, target.id, target.field].filter(Boolean).join('/');
1043
- const holder = intents[0];
1064
+ function listModelClaimQueue(target) {
1065
+ if (!target?.model || !target.id)
1066
+ return [];
1067
+ return publicIntents
1068
+ .queueFor({ type: target.model, id: target.id })
1069
+ .filter((intent) => (target.field ? intent.target.field === target.field : true))
1070
+ .map(modelClaimFromQueued);
1071
+ }
1072
+ function claimedError(target, claims, code) {
1073
+ const label = [target.model, target.id, target.field].filter(Boolean).join('/');
1074
+ const holder = claims[0];
1044
1075
  const suffix = holder
1045
1076
  ? ` held by ${holder.actor} (${holder.action})`
1046
1077
  : ' held by another participant';
1047
- return new AbloBusyError(`Resource is busy: ${label || 'target'}${suffix}.`, { code, intents });
1078
+ return new AbloClaimedError(`Model row is claimed: ${label || 'target'}${suffix}.`, { code, claims });
1048
1079
  }
1049
- function waitForResourceIdle(target, options) {
1050
- if (listResourceIntents(target).length === 0)
1080
+ function waitForModelUnclaimed(target, options) {
1081
+ if (listModelClaims(target).length === 0)
1051
1082
  return Promise.resolve();
1052
1083
  return new Promise((resolve, reject) => {
1053
1084
  let settled = false;
@@ -1068,7 +1099,7 @@ export function Ablo(options) {
1068
1099
  fn();
1069
1100
  };
1070
1101
  const check = () => {
1071
- if (listResourceIntents(target).length === 0) {
1102
+ if (listModelClaims(target).length === 0) {
1072
1103
  finish(resolve);
1073
1104
  }
1074
1105
  };
@@ -1082,25 +1113,30 @@ export function Ablo(options) {
1082
1113
  onAbort();
1083
1114
  return;
1084
1115
  }
1085
- unsubscribe = intentStream.subscribe(check);
1116
+ unsubscribe = intentStream.onChange(check);
1086
1117
  options?.signal?.addEventListener('abort', onAbort, { once: true });
1087
1118
  if (options?.timeout != null) {
1088
1119
  timeoutId = setTimeout(() => {
1089
- finish(() => reject(busyError(target, listResourceIntents(target), 'resource_busy_timeout')));
1120
+ finish(() => reject(claimedError(target, listModelClaims(target), 'model_claimed_timeout')));
1090
1121
  }, options.timeout);
1091
1122
  }
1092
1123
  });
1093
1124
  }
1094
- async function applyBusyPolicy(target, options) {
1095
- const policy = options?.ifBusy ?? 'return';
1125
+ async function applyClaimedPolicy(target, options) {
1126
+ const policy = options?.ifClaimed ?? 'return';
1096
1127
  if (policy === 'return')
1097
1128
  return;
1098
- const current = listResourceIntents(target);
1129
+ const current = listModelClaims(target);
1099
1130
  if (current.length === 0)
1100
1131
  return;
1101
1132
  if (policy === 'fail')
1102
- throw busyError(target, current, 'resource_busy');
1103
- await waitForResourceIdle(target, { timeout: options?.busyTimeout });
1133
+ throw claimedError(target, current, 'model_claimed');
1134
+ const queue = listModelClaimQueue(target);
1135
+ if (options?.maxQueueDepth !== undefined &&
1136
+ queue.length >= options.maxQueueDepth) {
1137
+ throw claimedError(target, current, 'queue_too_deep');
1138
+ }
1139
+ await waitForModelUnclaimed(target, { timeout: options?.claimedTimeout });
1104
1140
  }
1105
1141
  function wrapIntentHandle(claim) {
1106
1142
  const release = async () => {
@@ -1117,24 +1153,51 @@ export function Ablo(options) {
1117
1153
  async create(intentOptions) {
1118
1154
  await ready();
1119
1155
  const claim = intentStream.claim({
1120
- type: intentOptions.target.resource,
1156
+ type: intentOptions.target.model,
1121
1157
  id: intentOptions.target.id,
1122
1158
  path: intentOptions.target.path,
1123
1159
  range: intentOptions.target.range,
1124
1160
  field: intentOptions.target.field,
1125
1161
  meta: intentOptions.target.meta,
1126
- }, { reason: intentOptions.action, ttl: intentOptions.ttl });
1162
+ }, {
1163
+ reason: intentOptions.action,
1164
+ ttl: intentOptions.ttl,
1165
+ queue: intentOptions.queue,
1166
+ });
1167
+ // With `queue`, the claim is only really *ours* once the server says
1168
+ // so (`intent_acquired` if the target was free, `intent_granted` once
1169
+ // we reach the head of the FIFO line). Block here on that grant so
1170
+ // callers — chiefly `ablo.<model>.claim` — get a handle that already
1171
+ // holds the lease, never a half-claimed one racing the queue.
1172
+ if (intentOptions.queue) {
1173
+ const ws = store.getSyncWebSocket();
1174
+ if (ws) {
1175
+ try {
1176
+ await awaitIntentGrant(ws, claim.id, {
1177
+ timeoutMs: intentOptions.waitTimeoutMs,
1178
+ maxQueueDepth: intentOptions.maxQueueDepth,
1179
+ });
1180
+ }
1181
+ catch (err) {
1182
+ // Gave up waiting (queue too deep, timed out, or lost) — abandon
1183
+ // the queued intent so we don't leave a phantom entry in the
1184
+ // line that would block or mislead other claimers.
1185
+ claim.revoke();
1186
+ throw err;
1187
+ }
1188
+ }
1189
+ }
1127
1190
  return wrapIntentHandle(claim);
1128
1191
  },
1129
1192
  list(target) {
1130
- return listResourceIntents(target);
1193
+ return listModelClaims(target);
1131
1194
  },
1132
1195
  waitFor(target, options) {
1133
- return waitForResourceIdle(target, options);
1196
+ return waitForModelUnclaimed(target, options);
1134
1197
  },
1135
1198
  });
1136
1199
  // Build the typed proxy — one property per model. Done after publicIntents
1137
- // exists so model resources can expose workflow helpers such as
1200
+ // exists so model clients can expose workflow helpers such as
1138
1201
  // `ablo.files.edit(...)` without importing protocol wiring.
1139
1202
  const modelProxies = {};
1140
1203
  for (const [schemaKey, modelDef] of Object.entries(schema.models)) {
@@ -1147,13 +1210,14 @@ export function Ablo(options) {
1147
1210
  getLastSyncId: () => store.getSyncWebSocket()?.getLastSyncId() ?? store.lastSyncId ?? 0,
1148
1211
  entities: { [modelKey]: id },
1149
1212
  }),
1213
+ queue: (target) => publicIntents.queueFor({ type: target.model, id: target.id }),
1150
1214
  observe: (target) => {
1151
1215
  // The live intent stream only tracks *open* (active) claims;
1152
1216
  // terminal states (committed / expired / canceled) drop out of
1153
1217
  // the list entirely — exactly the ephemeral coordination model.
1154
1218
  // So a present entry is, by definition, `status: 'active'`.
1155
1219
  const held = publicIntents.list({
1156
- resource: target.resource,
1220
+ model: target.model,
1157
1221
  id: target.id,
1158
1222
  })[0];
1159
1223
  if (!held)
@@ -1163,7 +1227,7 @@ export function Ablo(options) {
1163
1227
  id: held.id,
1164
1228
  status: 'active',
1165
1229
  target: {
1166
- type: held.target.resource,
1230
+ type: held.target.model,
1167
1231
  id: held.target.id,
1168
1232
  ...(held.target.path ? { path: held.target.path } : {}),
1169
1233
  ...(held.target.range ? { range: held.target.range } : {}),
@@ -1176,7 +1240,7 @@ export function Ablo(options) {
1176
1240
  expiresAt: held.expiresAt,
1177
1241
  };
1178
1242
  },
1179
- waitFor: (target, waitOptions) => publicIntents.waitFor({ resource: target.resource, id: target.id }, waitOptions),
1243
+ waitFor: (target, waitOptions) => publicIntents.waitFor({ model: target.model, id: target.id }, waitOptions),
1180
1244
  selfParticipantId: participantId,
1181
1245
  });
1182
1246
  }
@@ -1208,8 +1272,8 @@ export function Ablo(options) {
1208
1272
  return { id: clientTxId, status: 'confirmed', lastSyncId };
1209
1273
  },
1210
1274
  };
1211
- async function retrieveResource(resourceName, id, options) {
1212
- await applyBusyPolicy({ resource: resourceName, id }, options);
1275
+ async function retrieveModel(modelName, id, options) {
1276
+ await applyClaimedPolicy({ model: modelName, id }, options);
1213
1277
  await ready();
1214
1278
  const res = await fetchImpl(`${bootstrapHelper.baseUrl}/sync/query`, {
1215
1279
  method: 'POST',
@@ -1218,7 +1282,7 @@ export function Ablo(options) {
1218
1282
  body: JSON.stringify({
1219
1283
  queries: [
1220
1284
  {
1221
- model: resourceName,
1285
+ model: modelName,
1222
1286
  where: [['id', '=', id]],
1223
1287
  limit: 1,
1224
1288
  },
@@ -1236,14 +1300,14 @@ export function Ablo(options) {
1236
1300
  }
1237
1301
  }
1238
1302
  if (!res.ok) {
1239
- throw translateHttpError(res.status, body || `Resource retrieve failed: ${res.status} ${res.statusText}`, res.headers.get('x-request-id') ?? undefined);
1303
+ throw translateHttpError(res.status, body || `Model retrieve failed: ${res.status} ${res.statusText}`, res.headers.get('x-request-id') ?? undefined);
1240
1304
  }
1241
1305
  const parsed = body;
1242
1306
  const slot = parsed.results?.[0];
1243
1307
  const rows = Array.isArray(slot) ? slot : [];
1244
1308
  const data = rows[0];
1245
1309
  if (!data) {
1246
- throw new AbloValidationError(`Resource not found: ${resourceName}/${id}`, { code: 'resource_not_found' });
1310
+ throw new AbloValidationError(`Model row not found: ${modelName}/${id}`, { code: 'model_not_found' });
1247
1311
  }
1248
1312
  const stamp = typeof parsed.lastSyncId === 'number'
1249
1313
  ? parsed.lastSyncId
@@ -1251,28 +1315,27 @@ export function Ablo(options) {
1251
1315
  return {
1252
1316
  data,
1253
1317
  stamp,
1254
- intents: listResourceIntents({ resource: resourceName, id }),
1318
+ claims: listModelClaims({ model: modelName, id }),
1255
1319
  };
1256
1320
  }
1257
- function resource(name) {
1321
+ function model(name) {
1258
1322
  return {
1259
1323
  retrieve(id, options) {
1260
- return retrieveResource(name, id, options);
1324
+ return retrieveModel(name, id, options);
1261
1325
  },
1262
1326
  async create(data, mutationOptions) {
1263
- const id = mutationOptions?.id ?? createResourceId();
1264
- await applyBusyPolicy({ resource: name, id }, mutationOptions);
1327
+ const id = mutationOptions?.id ?? createModelId();
1328
+ await applyClaimedPolicy({ model: name, id }, mutationOptions);
1265
1329
  return commits.create({
1266
1330
  intent: mutationOptions?.intent,
1267
1331
  idempotencyKey: mutationOptions?.idempotencyKey,
1268
1332
  readAt: mutationOptions?.readAt,
1269
1333
  onStale: mutationOptions?.onStale,
1270
1334
  wait: mutationOptions?.wait,
1271
- timeout: mutationOptions?.timeout,
1272
1335
  operations: [
1273
1336
  {
1274
1337
  action: 'create',
1275
- resource: name,
1338
+ model: name,
1276
1339
  id,
1277
1340
  data,
1278
1341
  },
@@ -1280,18 +1343,17 @@ export function Ablo(options) {
1280
1343
  });
1281
1344
  },
1282
1345
  async update(id, data, mutationOptions) {
1283
- await applyBusyPolicy({ resource: name, id }, mutationOptions);
1346
+ await applyClaimedPolicy({ model: name, id }, mutationOptions);
1284
1347
  return commits.create({
1285
1348
  intent: mutationOptions?.intent,
1286
1349
  idempotencyKey: mutationOptions?.idempotencyKey,
1287
1350
  readAt: mutationOptions?.readAt,
1288
1351
  onStale: mutationOptions?.onStale,
1289
1352
  wait: mutationOptions?.wait,
1290
- timeout: mutationOptions?.timeout,
1291
1353
  operations: [
1292
1354
  {
1293
1355
  action: 'update',
1294
- resource: name,
1356
+ model: name,
1295
1357
  id,
1296
1358
  data,
1297
1359
  },
@@ -1299,18 +1361,17 @@ export function Ablo(options) {
1299
1361
  });
1300
1362
  },
1301
1363
  async delete(id, mutationOptions) {
1302
- await applyBusyPolicy({ resource: name, id }, mutationOptions);
1364
+ await applyClaimedPolicy({ model: name, id }, mutationOptions);
1303
1365
  return commits.create({
1304
1366
  intent: mutationOptions?.intent,
1305
1367
  idempotencyKey: mutationOptions?.idempotencyKey,
1306
1368
  readAt: mutationOptions?.readAt,
1307
1369
  onStale: mutationOptions?.onStale,
1308
1370
  wait: mutationOptions?.wait,
1309
- timeout: mutationOptions?.timeout,
1310
1371
  operations: [
1311
1372
  {
1312
1373
  action: 'delete',
1313
- resource: name,
1374
+ model: name,
1314
1375
  id,
1315
1376
  },
1316
1377
  ],
@@ -1387,7 +1448,7 @@ export function Ablo(options) {
1387
1448
  /** Intent livestream — same socket. Stable reference. */
1388
1449
  intents: publicIntents,
1389
1450
  commits,
1390
- resource,
1451
+ model,
1391
1452
  /** Structured multiplayer participation — target-first, no
1392
1453
  * sync-group strings in the common path. */
1393
1454
  participants: participantManager,
@@ -2,10 +2,10 @@
2
2
  * Stateless API client for `Ablo({ apiKey })`.
3
3
  *
4
4
  * This is the hosted-API product surface: no schema, no object pool, no
5
- * IndexedDB, no WebSocket. It maps the public Resource / Intent / Commit
5
+ * IndexedDB, no WebSocket. It maps the public Model / Claim / Commit
6
6
  * nouns directly to HTTP routes on sync-server.
7
7
  */
8
- import type { AbloOptions, CommitReceipt, CommitResource, IntentCreateOptions, IntentHandle, IntentWaitOptions, ResourceClient, ResourceIntent, ResourceMutationOptions, ResourceReadOptions, ResourceRead, ResourceTarget, Turn } from './Ablo.js';
8
+ import type { AbloOptions, CommitReceipt, CommitResource, IntentCreateOptions, IntentHandle, IntentWaitOptions, ModelClient, ModelClaim, ModelMutationOptions, ModelReadOptions, ModelRead, ModelTarget, Turn } from './Ablo.js';
9
9
  import type { Duration } from '../utils/duration.js';
10
10
  export type AbloApiClientOptions = Omit<AbloOptions, 'schema'> & {
11
11
  readonly schema?: null | undefined;
@@ -13,8 +13,8 @@ export type AbloApiClientOptions = Omit<AbloOptions, 'schema'> & {
13
13
  };
14
14
  export interface AbloApiIntents {
15
15
  create(options: IntentCreateOptions): Promise<IntentHandle>;
16
- list(target?: Partial<ResourceTarget>): Promise<readonly ResourceIntent[]>;
17
- waitFor(target: Partial<ResourceTarget>, options?: IntentWaitOptions): Promise<void>;
16
+ list(target?: Partial<ModelTarget>): Promise<readonly ModelClaim[]>;
17
+ waitFor(target: Partial<ModelTarget>, options?: IntentWaitOptions): Promise<void>;
18
18
  }
19
19
  export type CapabilityParticipantKind = 'agent' | 'system';
20
20
  export interface CapabilityCreateBaseOptions {
@@ -157,28 +157,28 @@ export interface AgentIntentOptions {
157
157
  readonly action: string;
158
158
  readonly field?: string;
159
159
  readonly ttl?: Duration;
160
- readonly target?: Partial<ResourceTarget>;
160
+ readonly target?: Partial<ModelTarget>;
161
161
  }
162
162
  export type AgentIntentInput = string | AgentIntentOptions;
163
- export interface AgentResourceReadOptions extends ResourceReadOptions {
163
+ export interface AgentModelReadOptions extends ModelReadOptions {
164
164
  }
165
- export interface AgentResourceMutationOptions extends Omit<ResourceMutationOptions, 'intent'> {
165
+ export interface AgentModelMutationOptions extends Omit<ModelMutationOptions, 'intent'> {
166
166
  readonly intent?: AgentIntentInput | {
167
167
  readonly id: string;
168
168
  } | null;
169
169
  }
170
- export interface AgentResourceClient<T = Record<string, unknown>> {
171
- retrieve(id: string, options?: AgentResourceReadOptions): Promise<ResourceRead<T>>;
172
- create(data: Record<string, unknown>, options?: AgentResourceMutationOptions & {
170
+ export interface AgentModelClient<T = Record<string, unknown>> {
171
+ retrieve(id: string, options?: AgentModelReadOptions): Promise<ModelRead<T>>;
172
+ create(data: Record<string, unknown>, options?: AgentModelMutationOptions & {
173
173
  readonly id?: string | null;
174
174
  }): Promise<CommitReceipt>;
175
- update(id: string, data: Record<string, unknown>, options?: AgentResourceMutationOptions): Promise<CommitReceipt>;
176
- delete(id: string, options?: AgentResourceMutationOptions): Promise<CommitReceipt>;
175
+ update(id: string, data: Record<string, unknown>, options?: AgentModelMutationOptions): Promise<CommitReceipt>;
176
+ delete(id: string, options?: AgentModelMutationOptions): Promise<CommitReceipt>;
177
177
  }
178
178
  export interface AgentRunContext {
179
179
  readonly task: Task;
180
180
  readonly ablo: AbloApi;
181
- resource<T = Record<string, unknown>>(name: string): AgentResourceClient<T>;
181
+ model<T = Record<string, unknown>>(name: string): AgentModelClient<T>;
182
182
  }
183
183
  export interface Agent {
184
184
  readonly id: string;
@@ -194,7 +194,7 @@ export interface AbloApi {
194
194
  readonly intents: AbloApiIntents;
195
195
  readonly commits: CommitResource;
196
196
  agent(id: string, options: AgentOptions): Agent;
197
- resource<T = Record<string, unknown>>(name: string): ResourceClient<T>;
197
+ model<T = Record<string, unknown>>(name: string): ModelClient<T>;
198
198
  beginTurn(options: TaskCreateOptions): Promise<Turn>;
199
199
  }
200
200
  export declare function createProtocolClient(options: AbloApiClientOptions): AbloApi;