@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
@@ -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,15 @@ 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 }),
1214
+ reorder: (target, order) => publicIntents.reorder({ type: target.model, id: target.id }, order),
1150
1215
  observe: (target) => {
1151
1216
  // The live intent stream only tracks *open* (active) claims;
1152
1217
  // terminal states (committed / expired / canceled) drop out of
1153
1218
  // the list entirely — exactly the ephemeral coordination model.
1154
1219
  // So a present entry is, by definition, `status: 'active'`.
1155
1220
  const held = publicIntents.list({
1156
- resource: target.resource,
1221
+ model: target.model,
1157
1222
  id: target.id,
1158
1223
  })[0];
1159
1224
  if (!held)
@@ -1163,7 +1228,7 @@ export function Ablo(options) {
1163
1228
  id: held.id,
1164
1229
  status: 'active',
1165
1230
  target: {
1166
- type: held.target.resource,
1231
+ type: held.target.model,
1167
1232
  id: held.target.id,
1168
1233
  ...(held.target.path ? { path: held.target.path } : {}),
1169
1234
  ...(held.target.range ? { range: held.target.range } : {}),
@@ -1176,7 +1241,7 @@ export function Ablo(options) {
1176
1241
  expiresAt: held.expiresAt,
1177
1242
  };
1178
1243
  },
1179
- waitFor: (target, waitOptions) => publicIntents.waitFor({ resource: target.resource, id: target.id }, waitOptions),
1244
+ waitFor: (target, waitOptions) => publicIntents.waitFor({ model: target.model, id: target.id }, waitOptions),
1180
1245
  selfParticipantId: participantId,
1181
1246
  });
1182
1247
  }
@@ -1208,8 +1273,8 @@ export function Ablo(options) {
1208
1273
  return { id: clientTxId, status: 'confirmed', lastSyncId };
1209
1274
  },
1210
1275
  };
1211
- async function retrieveResource(resourceName, id, options) {
1212
- await applyBusyPolicy({ resource: resourceName, id }, options);
1276
+ async function retrieveModel(modelName, id, options) {
1277
+ await applyClaimedPolicy({ model: modelName, id }, options);
1213
1278
  await ready();
1214
1279
  const res = await fetchImpl(`${bootstrapHelper.baseUrl}/sync/query`, {
1215
1280
  method: 'POST',
@@ -1218,7 +1283,7 @@ export function Ablo(options) {
1218
1283
  body: JSON.stringify({
1219
1284
  queries: [
1220
1285
  {
1221
- model: resourceName,
1286
+ model: modelName,
1222
1287
  where: [['id', '=', id]],
1223
1288
  limit: 1,
1224
1289
  },
@@ -1236,14 +1301,14 @@ export function Ablo(options) {
1236
1301
  }
1237
1302
  }
1238
1303
  if (!res.ok) {
1239
- throw translateHttpError(res.status, body || `Resource retrieve failed: ${res.status} ${res.statusText}`, res.headers.get('x-request-id') ?? undefined);
1304
+ throw translateHttpError(res.status, body || `Model retrieve failed: ${res.status} ${res.statusText}`, res.headers.get('x-request-id') ?? undefined);
1240
1305
  }
1241
1306
  const parsed = body;
1242
1307
  const slot = parsed.results?.[0];
1243
1308
  const rows = Array.isArray(slot) ? slot : [];
1244
1309
  const data = rows[0];
1245
1310
  if (!data) {
1246
- throw new AbloValidationError(`Resource not found: ${resourceName}/${id}`, { code: 'resource_not_found' });
1311
+ throw new AbloValidationError(`Model row not found: ${modelName}/${id}`, { code: 'model_not_found' });
1247
1312
  }
1248
1313
  const stamp = typeof parsed.lastSyncId === 'number'
1249
1314
  ? parsed.lastSyncId
@@ -1251,28 +1316,27 @@ export function Ablo(options) {
1251
1316
  return {
1252
1317
  data,
1253
1318
  stamp,
1254
- intents: listResourceIntents({ resource: resourceName, id }),
1319
+ claims: listModelClaims({ model: modelName, id }),
1255
1320
  };
1256
1321
  }
1257
- function resource(name) {
1322
+ function model(name) {
1258
1323
  return {
1259
1324
  retrieve(id, options) {
1260
- return retrieveResource(name, id, options);
1325
+ return retrieveModel(name, id, options);
1261
1326
  },
1262
1327
  async create(data, mutationOptions) {
1263
- const id = mutationOptions?.id ?? createResourceId();
1264
- await applyBusyPolicy({ resource: name, id }, mutationOptions);
1328
+ const id = mutationOptions?.id ?? createModelId();
1329
+ await applyClaimedPolicy({ model: name, id }, mutationOptions);
1265
1330
  return commits.create({
1266
1331
  intent: mutationOptions?.intent,
1267
1332
  idempotencyKey: mutationOptions?.idempotencyKey,
1268
1333
  readAt: mutationOptions?.readAt,
1269
1334
  onStale: mutationOptions?.onStale,
1270
1335
  wait: mutationOptions?.wait,
1271
- timeout: mutationOptions?.timeout,
1272
1336
  operations: [
1273
1337
  {
1274
1338
  action: 'create',
1275
- resource: name,
1339
+ model: name,
1276
1340
  id,
1277
1341
  data,
1278
1342
  },
@@ -1280,18 +1344,17 @@ export function Ablo(options) {
1280
1344
  });
1281
1345
  },
1282
1346
  async update(id, data, mutationOptions) {
1283
- await applyBusyPolicy({ resource: name, id }, mutationOptions);
1347
+ await applyClaimedPolicy({ model: name, id }, mutationOptions);
1284
1348
  return commits.create({
1285
1349
  intent: mutationOptions?.intent,
1286
1350
  idempotencyKey: mutationOptions?.idempotencyKey,
1287
1351
  readAt: mutationOptions?.readAt,
1288
1352
  onStale: mutationOptions?.onStale,
1289
1353
  wait: mutationOptions?.wait,
1290
- timeout: mutationOptions?.timeout,
1291
1354
  operations: [
1292
1355
  {
1293
1356
  action: 'update',
1294
- resource: name,
1357
+ model: name,
1295
1358
  id,
1296
1359
  data,
1297
1360
  },
@@ -1299,18 +1362,17 @@ export function Ablo(options) {
1299
1362
  });
1300
1363
  },
1301
1364
  async delete(id, mutationOptions) {
1302
- await applyBusyPolicy({ resource: name, id }, mutationOptions);
1365
+ await applyClaimedPolicy({ model: name, id }, mutationOptions);
1303
1366
  return commits.create({
1304
1367
  intent: mutationOptions?.intent,
1305
1368
  idempotencyKey: mutationOptions?.idempotencyKey,
1306
1369
  readAt: mutationOptions?.readAt,
1307
1370
  onStale: mutationOptions?.onStale,
1308
1371
  wait: mutationOptions?.wait,
1309
- timeout: mutationOptions?.timeout,
1310
1372
  operations: [
1311
1373
  {
1312
1374
  action: 'delete',
1313
- resource: name,
1375
+ model: name,
1314
1376
  id,
1315
1377
  },
1316
1378
  ],
@@ -1387,7 +1449,7 @@ export function Ablo(options) {
1387
1449
  /** Intent livestream — same socket. Stable reference. */
1388
1450
  intents: publicIntents,
1389
1451
  commits,
1390
- resource,
1452
+ model,
1391
1453
  /** Structured multiplayer participation — target-first, no
1392
1454
  * sync-group strings in the common path. */
1393
1455
  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;