@abloatai/ablo 0.34.1 → 0.36.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 (536) hide show
  1. package/AGENTS.md +2 -1
  2. package/CHANGELOG.md +758 -5
  3. package/README.md +56 -502
  4. package/bin/ablo.cjs +39 -0
  5. package/dist/BaseSyncedStore.d.ts +176 -48
  6. package/dist/BaseSyncedStore.js +346 -214
  7. package/dist/Database.d.ts +17 -44
  8. package/dist/Database.js +96 -79
  9. package/dist/InstanceCache.d.ts +31 -6
  10. package/dist/InstanceCache.js +65 -30
  11. package/dist/LazyReferenceCollection.d.ts +3 -3
  12. package/dist/LazyReferenceCollection.js +4 -4
  13. package/dist/Model.d.ts +23 -13
  14. package/dist/Model.js +27 -17
  15. package/dist/ModelRegistry.d.ts +8 -4
  16. package/dist/ModelRegistry.js +20 -18
  17. package/dist/NetworkMonitor.d.ts +3 -1
  18. package/dist/NetworkMonitor.js +7 -5
  19. package/dist/{SyncEngineContext.d.ts → RuntimeContext.d.ts} +20 -13
  20. package/dist/{SyncEngineContext.js → RuntimeContext.js} +11 -12
  21. package/dist/SyncClient.d.ts +47 -47
  22. package/dist/SyncClient.js +215 -156
  23. package/dist/ai-sdk/coordinatedTool.d.ts +2 -2
  24. package/dist/ai-sdk/coordinatedTool.js +1 -1
  25. package/dist/ai-sdk/coordinationContext.d.ts +2 -2
  26. package/dist/ai-sdk/coordinationContext.js +1 -1
  27. package/dist/ai-sdk/wrap.d.ts +3 -3
  28. package/dist/ai-sdk/wrap.js +2 -2
  29. package/dist/auth/index.d.ts +1 -156
  30. package/dist/auth/index.js +8 -301
  31. package/dist/client/Ablo.d.ts +42 -287
  32. package/dist/client/Ablo.js +129 -963
  33. package/dist/client/abloClient.d.ts +309 -0
  34. package/dist/client/abloClient.js +13 -0
  35. package/dist/client/clientPrelude.d.ts +52 -0
  36. package/dist/client/clientPrelude.js +60 -0
  37. package/dist/client/consoleLogger.d.ts +2 -2
  38. package/dist/client/coreClient.d.ts +60 -0
  39. package/dist/client/coreClient.js +118 -0
  40. package/dist/client/createInternalComponents.d.ts +8 -4
  41. package/dist/client/createInternalComponents.js +17 -10
  42. package/dist/client/createModelProxy.d.ts +98 -373
  43. package/dist/client/createModelProxy.js +233 -139
  44. package/dist/client/humans.d.ts +69 -0
  45. package/dist/client/humans.js +78 -0
  46. package/dist/client/modelRegistration.d.ts +1 -1
  47. package/dist/client/modelRegistration.js +9 -9
  48. package/dist/client/options.d.ts +73 -17
  49. package/dist/client/reactiveEngine.d.ts +53 -0
  50. package/dist/client/reactiveEngine.js +688 -0
  51. package/dist/client/resourceTypes.d.ts +9 -250
  52. package/dist/client/resourceTypes.js +8 -5
  53. package/dist/client/schemaConfig.d.ts +4 -4
  54. package/dist/client/schemaConfig.js +6 -2
  55. package/dist/client/storeCluster.d.ts +47 -0
  56. package/dist/client/storeCluster.js +118 -0
  57. package/dist/client/storeLifecycle.d.ts +61 -0
  58. package/dist/client/storeLifecycle.js +231 -0
  59. package/dist/client/validateAbloOptions.d.ts +3 -2
  60. package/dist/client/validateAbloOptions.js +1 -1
  61. package/dist/client/wsMutationExecutor.d.ts +3 -3
  62. package/dist/client/wsMutationExecutor.js +3 -3
  63. package/dist/context.d.ts +22 -9
  64. package/dist/context.js +33 -9
  65. package/dist/coordination/ClaimLog.d.ts +26 -0
  66. package/dist/coordination/ClaimLog.js +32 -0
  67. package/dist/coordination/index.d.ts +1 -15
  68. package/dist/coordination/index.js +8 -31
  69. package/dist/core/index.d.ts +3 -3
  70. package/dist/core/index.js +2 -2
  71. package/dist/docs/catalog.d.ts +72 -0
  72. package/dist/docs/catalog.js +230 -0
  73. package/dist/docs/index.d.ts +10 -0
  74. package/dist/docs/index.js +10 -0
  75. package/dist/environment.d.ts +1 -40
  76. package/dist/environment.js +8 -37
  77. package/dist/index.d.ts +44 -36
  78. package/dist/index.js +30 -22
  79. package/dist/interfaces/index.d.ts +44 -134
  80. package/dist/keys/index.d.ts +1 -77
  81. package/dist/keys/index.js +8 -190
  82. package/dist/mutators/{RecordingTransaction.d.ts → RecordingMutation.d.ts} +4 -4
  83. package/dist/mutators/{RecordingTransaction.js → RecordingMutation.js} +2 -2
  84. package/dist/mutators/Transaction.d.ts +1 -1
  85. package/dist/mutators/Transaction.js +1 -1
  86. package/dist/mutators/UndoManager.d.ts +6 -6
  87. package/dist/mutators/UndoManager.js +5 -5
  88. package/dist/mutators/defineMutators.d.ts +3 -3
  89. package/dist/mutators/defineMutators.js +1 -1
  90. package/dist/mutators/inverseOp.js +2 -2
  91. package/dist/mutators/mutateActions.d.ts +3 -3
  92. package/dist/mutators/mutateActions.js +1 -1
  93. package/dist/mutators/readerActions.d.ts +1 -1
  94. package/dist/mutators/undoApply.d.ts +1 -1
  95. package/dist/mutators/undoApply.js +1 -1
  96. package/dist/policy/index.d.ts +2 -2
  97. package/dist/policy/index.js +1 -1
  98. package/dist/query/client.d.ts +5 -2
  99. package/dist/query/client.js +10 -9
  100. package/dist/query/types.d.ts +6 -41
  101. package/dist/query/types.js +2 -2
  102. package/dist/react/AbloProvider.d.ts +18 -8
  103. package/dist/react/AbloProvider.js +10 -9
  104. package/dist/react/context.d.ts +3 -3
  105. package/dist/react/context.js +1 -1
  106. package/dist/react/createAbloReact.d.ts +56 -0
  107. package/dist/react/createAbloReact.js +51 -0
  108. package/dist/react/index.d.ts +6 -5
  109. package/dist/react/index.js +6 -3
  110. package/dist/react/internalContext.d.ts +1 -1
  111. package/dist/react/useAblo.d.ts +12 -5
  112. package/dist/react/useAblo.js +26 -8
  113. package/dist/react/useCurrentUserId.js +1 -1
  114. package/dist/react/useErrorListener.js +1 -1
  115. package/dist/react/useMutationFailureListener.d.ts +2 -2
  116. package/dist/react/useMutationFailureListener.js +1 -1
  117. package/dist/react/useMutators.d.ts +3 -3
  118. package/dist/react/useMutators.js +3 -3
  119. package/dist/react/useUndoScope.d.ts +5 -5
  120. package/dist/react/useUndoScope.js +1 -1
  121. package/dist/schema/coordination.d.ts +69 -10
  122. package/dist/schema/coordination.js +90 -9
  123. package/dist/schema/ddl.js +2 -2
  124. package/dist/schema/diff.d.ts +1 -1
  125. package/dist/schema/generate.js +1 -1
  126. package/dist/schema/index.d.ts +11 -10
  127. package/dist/schema/index.js +22 -18
  128. package/dist/schema/queries.d.ts +27 -27
  129. package/dist/schema/queries.js +23 -23
  130. package/dist/schema/select.d.ts +3 -3
  131. package/dist/schema/select.js +6 -3
  132. package/dist/schema/serialize.d.ts +15 -6
  133. package/dist/schema/serialize.js +20 -3
  134. package/dist/schema/sugar.d.ts +6 -7
  135. package/dist/schema/sugar.js +9 -12
  136. package/dist/schema/syncDeltaRow.d.ts +4 -152
  137. package/dist/schema/syncDeltaRow.js +4 -105
  138. package/dist/server/adapter.d.ts +18 -1
  139. package/dist/server/commit.d.ts +10 -16
  140. package/dist/server/index.d.ts +1 -1
  141. package/dist/server/index.js +1 -1
  142. package/dist/server/readConfig.d.ts +1 -1
  143. package/dist/source/adapter.d.ts +7 -5
  144. package/dist/source/adapter.js +7 -5
  145. package/dist/source/adapters/drizzle.d.ts +1 -1
  146. package/dist/source/adapters/drizzle.js +2 -2
  147. package/dist/source/adapters/kysely.d.ts +1 -1
  148. package/dist/source/adapters/kysely.js +1 -1
  149. package/dist/source/adapters/kyselyMutationCore.d.ts +1 -1
  150. package/dist/source/adapters/kyselyMutationCore.js +2 -2
  151. package/dist/source/adapters/memory.js +1 -1
  152. package/dist/source/adapters/prisma.d.ts +8 -3
  153. package/dist/source/adapters/prisma.js +1 -1
  154. package/dist/source/connector.js +1 -1
  155. package/dist/source/connectorProtocol.d.ts +2 -8
  156. package/dist/source/connectorProtocol.js +3 -2
  157. package/dist/source/contract.d.ts +29 -17
  158. package/dist/source/contract.js +27 -22
  159. package/dist/source/factory.d.ts +1 -1
  160. package/dist/source/idempotency.js +2 -2
  161. package/dist/source/index.d.ts +1 -0
  162. package/dist/source/index.js +3 -0
  163. package/dist/source/next.d.ts +1 -1
  164. package/dist/source/signing.d.ts +9 -2
  165. package/dist/source/signing.js +4 -1
  166. package/dist/source/types.d.ts +6 -4
  167. package/dist/source/types.js +1 -1
  168. package/dist/{core/storeContract.d.ts → storeContract.d.ts} +6 -6
  169. package/dist/{core → stores}/DatabaseManager.d.ts +3 -1
  170. package/dist/{core → stores}/DatabaseManager.js +14 -13
  171. package/dist/stores/ObjectStore.d.ts +1 -1
  172. package/dist/{core → stores}/StoreManager.d.ts +9 -26
  173. package/dist/{core → stores}/StoreManager.js +29 -77
  174. package/dist/stores/SyncActionStore.d.ts +4 -2
  175. package/dist/stores/SyncActionStore.js +11 -17
  176. package/dist/stores/syncAction.d.ts +26 -0
  177. package/dist/stores/syncAction.js +16 -0
  178. package/dist/surface.d.ts +3 -3
  179. package/dist/surface.js +6 -4
  180. package/dist/sync/BootstrapFetcher.d.ts +127 -6
  181. package/dist/sync/BootstrapFetcher.js +511 -83
  182. package/dist/sync/ConnectionManager.d.ts +6 -198
  183. package/dist/sync/ConnectionManager.js +6 -677
  184. package/dist/sync/OnDemandLoader.d.ts +5 -2
  185. package/dist/sync/OnDemandLoader.js +61 -21
  186. package/dist/sync/SubscriptionManager.d.ts +13 -2
  187. package/dist/sync/SubscriptionManager.js +23 -5
  188. package/dist/sync/SyncWebSocket.d.ts +27 -510
  189. package/dist/sync/SyncWebSocket.js +76 -954
  190. package/dist/sync/awaitClaimGrant.d.ts +4 -44
  191. package/dist/sync/awaitClaimGrant.js +4 -109
  192. package/dist/sync/bootstrapApply.d.ts +3 -0
  193. package/dist/sync/bootstrapApply.js +2 -2
  194. package/dist/sync/commitFrames.d.ts +6 -40
  195. package/dist/sync/commitFrames.js +6 -97
  196. package/dist/sync/contextPorts.d.ts +18 -0
  197. package/dist/sync/contextPorts.js +31 -0
  198. package/dist/sync/createClaimStream.d.ts +5 -49
  199. package/dist/sync/createClaimStream.js +5 -469
  200. package/dist/sync/createPresenceStream.d.ts +26 -4
  201. package/dist/sync/createPresenceStream.js +28 -20
  202. package/dist/sync/createSnapshot.d.ts +2 -2
  203. package/dist/sync/createSnapshot.js +1 -1
  204. package/dist/sync/credentialLifecycle.d.ts +5 -173
  205. package/dist/sync/credentialLifecycle.js +5 -320
  206. package/dist/sync/deltaPipeline.d.ts +13 -12
  207. package/dist/sync/deltaPipeline.js +21 -4
  208. package/dist/sync/groupChange.d.ts +3 -0
  209. package/dist/sync/groupChange.js +16 -14
  210. package/dist/sync/participants.d.ts +24 -6
  211. package/dist/sync/participants.js +32 -23
  212. package/dist/sync/schemaDrift.d.ts +55 -0
  213. package/dist/sync/schemaDrift.js +53 -0
  214. package/dist/sync/schemas.d.ts +23 -33
  215. package/dist/sync/schemas.js +29 -20
  216. package/dist/sync/syncPlan.d.ts +3 -3
  217. package/dist/sync/wsFrameHandlers.d.ts +6 -114
  218. package/dist/sync/wsFrameHandlers.js +6 -392
  219. package/dist/syncLog/contract.d.ts +20 -0
  220. package/dist/syncLog/contract.js +19 -0
  221. package/dist/syncLog/index.d.ts +1 -0
  222. package/dist/syncLog/index.js +1 -0
  223. package/dist/transaction/ablo.d.ts +88 -0
  224. package/dist/transaction/ablo.js +33 -0
  225. package/dist/{client/auth.d.ts → transaction/auth/apiKey.d.ts} +43 -8
  226. package/dist/{client/auth.js → transaction/auth/apiKey.js} +19 -0
  227. package/dist/transaction/auth/bootstrapScope.d.ts +15 -0
  228. package/dist/transaction/auth/bootstrapScope.js +1 -0
  229. package/dist/transaction/auth/capability.d.ts +212 -0
  230. package/dist/transaction/auth/capability.js +224 -0
  231. package/dist/{auth → transaction/auth}/credentialSource.d.ts +8 -1
  232. package/dist/{client → transaction/auth}/identity.d.ts +8 -7
  233. package/dist/{client → transaction/auth}/identity.js +1 -1
  234. package/dist/transaction/auth/index.d.ts +162 -0
  235. package/dist/transaction/auth/index.js +304 -0
  236. package/dist/{auth → transaction/auth}/schemas.d.ts +1 -1
  237. package/dist/{auth → transaction/auth}/schemas.js +13 -13
  238. package/dist/{client → transaction/auth}/sessionMint.d.ts +8 -8
  239. package/dist/{client → transaction/auth}/sessionMint.js +4 -7
  240. package/dist/transaction/coordination/awaitClaimGrant.d.ts +56 -0
  241. package/dist/transaction/coordination/awaitClaimGrant.js +124 -0
  242. package/dist/{client → transaction/coordination}/claimHeartbeatLoop.d.ts +34 -0
  243. package/dist/{client → transaction/coordination}/claimHeartbeatLoop.js +20 -0
  244. package/dist/transaction/coordination/claimMeta.d.ts +49 -0
  245. package/dist/transaction/coordination/claimMeta.js +52 -0
  246. package/dist/transaction/coordination/createClaimStream.d.ts +64 -0
  247. package/dist/transaction/coordination/createClaimStream.js +475 -0
  248. package/dist/transaction/coordination/events.d.ts +74 -0
  249. package/dist/transaction/coordination/events.js +7 -0
  250. package/dist/transaction/coordination/index.d.ts +19 -0
  251. package/dist/transaction/coordination/index.js +45 -0
  252. package/dist/transaction/coordination/locator.d.ts +104 -0
  253. package/dist/transaction/coordination/locator.js +102 -0
  254. package/dist/transaction/coordination/schema.d.ts +1536 -0
  255. package/dist/transaction/coordination/schema.js +1177 -0
  256. package/dist/transaction/coordination/targetConflict.d.ts +2 -0
  257. package/dist/transaction/coordination/targetConflict.js +107 -0
  258. package/dist/{coordination → transaction/coordination}/trace.d.ts +7 -18
  259. package/dist/{coordination → transaction/coordination}/trace.js +18 -25
  260. package/dist/transaction/durableWrites.d.ts +62 -0
  261. package/dist/{client → transaction}/durableWrites.js +28 -3
  262. package/dist/transaction/environment.d.ts +105 -0
  263. package/dist/transaction/environment.js +108 -0
  264. package/dist/{errorCodes.d.ts → transaction/errorCodes.d.ts} +12 -12
  265. package/dist/{errorCodes.js → transaction/errorCodes.js} +45 -18
  266. package/dist/{errors.d.ts → transaction/errors.d.ts} +37 -13
  267. package/dist/{errors.js → transaction/errors.js} +85 -16
  268. package/dist/transaction/footprint.d.ts +111 -0
  269. package/dist/transaction/footprint.js +0 -0
  270. package/dist/transaction/index.d.ts +20 -0
  271. package/dist/transaction/index.js +20 -0
  272. package/dist/transaction/keys/index.d.ts +87 -0
  273. package/dist/transaction/keys/index.js +207 -0
  274. package/dist/transaction/log/syncDeltaRow.d.ts +158 -0
  275. package/dist/transaction/log/syncDeltaRow.js +95 -0
  276. package/dist/{sync/syncPosition.d.ts → transaction/logPosition.d.ts} +22 -8
  277. package/dist/{sync/syncPosition.js → transaction/logPosition.js} +15 -6
  278. package/dist/transaction/logger.d.ts +16 -0
  279. package/dist/transaction/logger.js +7 -0
  280. package/dist/transaction/observability.d.ts +53 -0
  281. package/dist/transaction/observability.js +19 -0
  282. package/dist/transaction/plugin.d.ts +285 -0
  283. package/dist/transaction/plugin.js +106 -0
  284. package/dist/{policy → transaction/policy}/types.d.ts +3 -3
  285. package/dist/{policy → transaction/policy}/types.js +2 -0
  286. package/dist/transaction/resources/httpResources.d.ts +321 -0
  287. package/dist/transaction/resources/httpResources.js +7 -0
  288. package/dist/transaction/resources/modelOperations.d.ts +427 -0
  289. package/dist/transaction/resources/modelOperations.js +12 -0
  290. package/dist/transaction/resources/mutationOptions.d.ts +66 -0
  291. package/dist/transaction/resources/mutationOptions.js +9 -0
  292. package/dist/transaction/resources/where.d.ts +101 -0
  293. package/dist/transaction/resources/where.js +115 -0
  294. package/dist/{client → transaction/resources}/writeOptionsSchema.d.ts +2 -6
  295. package/dist/{client → transaction/resources}/writeOptionsSchema.js +9 -5
  296. package/dist/{schema → transaction/schema}/field.d.ts +17 -23
  297. package/dist/{schema → transaction/schema}/field.js +5 -5
  298. package/dist/transaction/schema/fieldRef.d.ts +38 -0
  299. package/dist/transaction/schema/fieldRef.js +11 -0
  300. package/dist/transaction/schema/loadStrategy.d.ts +45 -0
  301. package/dist/transaction/schema/loadStrategy.js +46 -0
  302. package/dist/{schema → transaction/schema}/model.d.ts +50 -35
  303. package/dist/{schema → transaction/schema}/model.js +30 -20
  304. package/dist/transaction/schema/openapi.d.ts +58 -0
  305. package/dist/transaction/schema/openapi.js +501 -0
  306. package/dist/{schema → transaction/schema}/relation.d.ts +21 -16
  307. package/dist/{schema → transaction/schema}/relation.js +7 -7
  308. package/dist/{schema → transaction/schema}/residency.d.ts +0 -9
  309. package/dist/{schema → transaction/schema}/residency.js +0 -5
  310. package/dist/{schema → transaction/schema}/roles.d.ts +5 -5
  311. package/dist/{schema → transaction/schema}/roles.js +5 -5
  312. package/dist/{schema → transaction/schema}/schema.d.ts +39 -10
  313. package/dist/{schema → transaction/schema}/schema.js +24 -3
  314. package/dist/{schema → transaction/schema}/tenancy.d.ts +1 -1
  315. package/dist/{schema → transaction/schema}/tenancy.js +7 -4
  316. package/dist/transaction/transactionLayer.d.ts +82 -0
  317. package/dist/transaction/transactionLayer.js +24 -0
  318. package/dist/{transactions → transaction/transactions/settlement}/commitEnvelope.d.ts +5 -6
  319. package/dist/{transactions → transaction/transactions/settlement}/commitEnvelope.js +9 -13
  320. package/dist/{transactions → transaction/transactions/settlement}/httpCommitEnvelope.d.ts +9 -2
  321. package/dist/{transactions → transaction/transactions/settlement}/httpCommitEnvelope.js +5 -9
  322. package/dist/{transactions/durableWriteStore.d.ts → transaction/transactions/settlement/pendingWrite.d.ts} +11 -37
  323. package/dist/transaction/transactions/settlement/pendingWrite.js +20 -0
  324. package/dist/transaction/transport/commitFrames.d.ts +90 -0
  325. package/dist/transaction/transport/commitFrames.js +134 -0
  326. package/dist/transaction/transport/connectionManager.d.ts +215 -0
  327. package/dist/transaction/transport/connectionManager.js +673 -0
  328. package/dist/transaction/transport/credentialLifecycle.d.ts +177 -0
  329. package/dist/transaction/transport/credentialLifecycle.js +324 -0
  330. package/dist/{sync → transaction/transport}/heartbeat.d.ts +3 -1
  331. package/dist/{sync → transaction/transport}/heartbeat.js +6 -4
  332. package/dist/transaction/transport/httpClient.d.ts +131 -0
  333. package/dist/{client → transaction/transport}/httpClient.js +6 -5
  334. package/dist/transaction/transport/httpOptions.d.ts +33 -0
  335. package/dist/transaction/transport/httpOptions.js +12 -0
  336. package/dist/{client → transaction/transport}/httpTransport.js +295 -97
  337. package/dist/{sync/NetworkProbe.d.ts → transaction/transport/networkProbe.d.ts} +7 -4
  338. package/dist/{sync/NetworkProbe.js → transaction/transport/networkProbe.js} +14 -13
  339. package/dist/transaction/transport/wsFrameHandlers.d.ts +128 -0
  340. package/dist/transaction/transport/wsFrameHandlers.js +429 -0
  341. package/dist/transaction/transport/wsTransport.d.ts +574 -0
  342. package/dist/transaction/transport/wsTransport.js +1023 -0
  343. package/dist/transaction/types/assertExact.d.ts +17 -0
  344. package/dist/transaction/types/assertExact.js +1 -0
  345. package/dist/{types → transaction/types}/global.d.ts +17 -2
  346. package/dist/{types → transaction/types}/global.js +2 -1
  347. package/dist/{types → transaction/types}/index.d.ts +14 -46
  348. package/dist/{types → transaction/types}/index.js +7 -16
  349. package/dist/{types → transaction/types}/streams.d.ts +73 -45
  350. package/dist/transaction/utils/duration.d.ts +50 -0
  351. package/dist/{utils → transaction/utils}/duration.js +32 -0
  352. package/dist/{utils → transaction/utils}/json.d.ts +18 -0
  353. package/dist/transaction/utils/json.js +276 -0
  354. package/dist/transaction/wire/accountResponses.d.ts +420 -0
  355. package/dist/transaction/wire/accountResponses.js +290 -0
  356. package/dist/transaction/wire/auth.d.ts +56 -0
  357. package/dist/transaction/wire/auth.js +63 -0
  358. package/dist/transaction/wire/claimEvent.d.ts +76 -0
  359. package/dist/transaction/wire/claimEvent.js +73 -0
  360. package/dist/transaction/wire/claims.d.ts +530 -0
  361. package/dist/transaction/wire/claims.js +327 -0
  362. package/dist/{wire → transaction/wire}/commit.d.ts +125 -193
  363. package/dist/{wire → transaction/wire}/commit.js +68 -47
  364. package/dist/{wire → transaction/wire}/delta.d.ts +66 -17
  365. package/dist/{wire → transaction/wire}/delta.js +37 -13
  366. package/dist/transaction/wire/errorEnvelope.d.ts +72 -0
  367. package/dist/{wire → transaction/wire}/errorEnvelope.js +36 -5
  368. package/dist/transaction/wire/feedCursor.d.ts +60 -0
  369. package/dist/transaction/wire/feedCursor.js +82 -0
  370. package/dist/transaction/wire/feedEvent.d.ts +204 -0
  371. package/dist/transaction/wire/feedEvent.js +65 -0
  372. package/dist/transaction/wire/frames.d.ts +194 -0
  373. package/dist/transaction/wire/frames.js +50 -0
  374. package/dist/transaction/wire/inboundFrames.d.ts +562 -0
  375. package/dist/transaction/wire/inboundFrames.js +116 -0
  376. package/dist/transaction/wire/index.d.ts +54 -0
  377. package/dist/transaction/wire/index.js +83 -0
  378. package/dist/{wire → transaction/wire}/listEnvelope.d.ts +13 -14
  379. package/dist/transaction/wire/listEnvelope.js +42 -0
  380. package/dist/transaction/wire/modelMutations.d.ts +31 -0
  381. package/dist/transaction/wire/modelMutations.js +52 -0
  382. package/dist/transaction/wire/modelResponses.d.ts +85 -0
  383. package/dist/transaction/wire/modelResponses.js +43 -0
  384. package/dist/transaction/wire/modelShape.d.ts +78 -0
  385. package/dist/transaction/wire/modelShape.js +74 -0
  386. package/dist/transactions/{TransactionQueue.d.ts → mutations/MutationQueue.d.ts} +85 -38
  387. package/dist/transactions/{TransactionQueue.js → mutations/MutationQueue.js} +141 -80
  388. package/dist/transactions/{TransactionStore.d.ts → mutations/MutationStore.d.ts} +8 -8
  389. package/dist/transactions/{TransactionStore.js → mutations/MutationStore.js} +2 -2
  390. package/dist/transactions/{coalesceRules.d.ts → mutations/coalesceRules.d.ts} +10 -10
  391. package/dist/transactions/{coalesceRules.js → mutations/coalesceRules.js} +1 -1
  392. package/dist/transactions/mutations/commitLatency.d.ts +52 -0
  393. package/dist/transactions/mutations/commitLatency.js +130 -0
  394. package/dist/transactions/{commitOutboxStore.d.ts → mutations/commitOutboxStore.d.ts} +1 -5
  395. package/dist/transactions/{commitOutboxStore.js → mutations/commitOutboxStore.js} +1 -1
  396. package/dist/transactions/{commitPayload.d.ts → mutations/commitPayload.d.ts} +18 -16
  397. package/dist/transactions/{commitPayload.js → mutations/commitPayload.js} +15 -15
  398. package/dist/transactions/{deltaConfirmation.d.ts → mutations/deltaConfirmation.d.ts} +15 -11
  399. package/dist/transactions/{deltaConfirmation.js → mutations/deltaConfirmation.js} +14 -12
  400. package/dist/transactions/mutations/durableWriteStore.d.ts +14 -0
  401. package/dist/transactions/mutations/durableWriteStore.js +12 -0
  402. package/dist/transactions/{optimisticApply.d.ts → mutations/optimisticApply.d.ts} +7 -7
  403. package/dist/transactions/{replayValidation.d.ts → mutations/replayValidation.d.ts} +4 -3
  404. package/dist/transactions/{replayValidation.js → mutations/replayValidation.js} +7 -5
  405. package/dist/utils/mobxSetup.d.ts +1 -1
  406. package/dist/utils/mobxSetup.js +5 -2
  407. package/dist/{core → views}/QueryView.d.ts +2 -2
  408. package/dist/{core → views}/QueryView.js +2 -2
  409. package/dist/{core → views}/ViewRegistry.d.ts +1 -1
  410. package/dist/{core/queryUtils.d.ts → views/incrementalView.d.ts} +6 -6
  411. package/dist/{core/queryUtils.js → views/incrementalView.js} +6 -6
  412. package/dist/webhooks/events.d.ts +2 -2
  413. package/dist/wire/index.d.ts +1 -34
  414. package/dist/wire/index.js +8 -49
  415. package/docs/agent-messaging.md +3 -3
  416. package/docs/agents.md +20 -13
  417. package/docs/api-keys.md +14 -10
  418. package/docs/api.md +27 -61
  419. package/docs/audit.md +6 -3
  420. package/docs/cli.md +41 -13
  421. package/docs/client-behavior.md +11 -9
  422. package/docs/concurrency-convention.md +49 -57
  423. package/docs/coordination.md +283 -121
  424. package/docs/data-sources.md +7 -5
  425. package/docs/debugging.md +39 -15
  426. package/docs/deployment.md +267 -0
  427. package/docs/examples/agent-human.md +49 -42
  428. package/docs/examples/ai-sdk-tool.md +69 -44
  429. package/docs/examples/existing-python-backend.md +8 -6
  430. package/docs/examples/nextjs.md +129 -47
  431. package/docs/examples/scoped-agent.md +46 -45
  432. package/docs/examples/server-agent.md +46 -26
  433. package/docs/groups.md +87 -30
  434. package/docs/guarantees.md +41 -12
  435. package/docs/how-it-works.md +38 -12
  436. package/docs/idempotency.md +126 -0
  437. package/docs/identity.md +77 -74
  438. package/docs/index.md +172 -86
  439. package/docs/integration-guide.md +31 -19
  440. package/docs/mcp.md +46 -21
  441. package/docs/migration.md +95 -18
  442. package/docs/operating-on-your-database.md +3 -1
  443. package/docs/projects.md +3 -1
  444. package/docs/quickstart.md +22 -5
  445. package/docs/react.md +31 -18
  446. package/docs/schema-contract.md +5 -3
  447. package/docs/session-settings.md +108 -0
  448. package/docs/sessions.md +4 -2
  449. package/docs/webhooks.md +12 -10
  450. package/llms.txt +48 -18
  451. package/package.json +21 -26
  452. package/dist/agent/Agent.d.ts +0 -366
  453. package/dist/agent/Agent.js +0 -514
  454. package/dist/agent/index.d.ts +0 -115
  455. package/dist/agent/index.js +0 -128
  456. package/dist/agent/session.d.ts +0 -93
  457. package/dist/agent/session.js +0 -149
  458. package/dist/agent/types.d.ts +0 -68
  459. package/dist/agent/types.js +0 -9
  460. package/dist/cli.cjs +0 -286329
  461. package/dist/client/durableWrites.d.ts +0 -21
  462. package/dist/client/httpClient.d.ts +0 -80
  463. package/dist/coordination/schema.d.ts +0 -722
  464. package/dist/coordination/schema.js +0 -578
  465. package/dist/schema/openapi.d.ts +0 -29
  466. package/dist/schema/openapi.js +0 -124
  467. package/dist/testing/fixtures/bootstrap.d.ts +0 -49
  468. package/dist/testing/fixtures/bootstrap.js +0 -59
  469. package/dist/testing/fixtures/deltas.d.ts +0 -83
  470. package/dist/testing/fixtures/deltas.js +0 -136
  471. package/dist/testing/fixtures/models.d.ts +0 -83
  472. package/dist/testing/fixtures/models.js +0 -272
  473. package/dist/testing/helpers/reactWrapper.d.ts +0 -69
  474. package/dist/testing/helpers/reactWrapper.js +0 -67
  475. package/dist/testing/helpers/syncEngineHarness.d.ts +0 -54
  476. package/dist/testing/helpers/syncEngineHarness.js +0 -73
  477. package/dist/testing/helpers/wait.d.ts +0 -30
  478. package/dist/testing/helpers/wait.js +0 -49
  479. package/dist/testing/index.d.ts +0 -23
  480. package/dist/testing/index.js +0 -33
  481. package/dist/testing/mocks/FakeDatabase.d.ts +0 -18
  482. package/dist/testing/mocks/FakeDatabase.js +0 -10
  483. package/dist/testing/mocks/MockMutationExecutor.d.ts +0 -87
  484. package/dist/testing/mocks/MockMutationExecutor.js +0 -192
  485. package/dist/testing/mocks/MockNetworkMonitor.d.ts +0 -20
  486. package/dist/testing/mocks/MockNetworkMonitor.js +0 -46
  487. package/dist/testing/mocks/MockSyncContext.d.ts +0 -51
  488. package/dist/testing/mocks/MockSyncContext.js +0 -71
  489. package/dist/testing/mocks/MockSyncStore.d.ts +0 -88
  490. package/dist/testing/mocks/MockSyncStore.js +0 -171
  491. package/dist/testing/mocks/MockWebSocket.d.ts +0 -71
  492. package/dist/testing/mocks/MockWebSocket.js +0 -118
  493. package/dist/transactions/durableWriteStore.js +0 -30
  494. package/dist/utils/duration.d.ts +0 -25
  495. package/dist/utils/json.js +0 -88
  496. package/dist/wire/errorEnvelope.d.ts +0 -55
  497. package/dist/wire/frames.d.ts +0 -197
  498. package/dist/wire/frames.js +0 -49
  499. package/dist/wire/listEnvelope.js +0 -18
  500. package/docs/interaction-model.md +0 -97
  501. /package/dist/{core → query}/QueryProcessor.d.ts +0 -0
  502. /package/dist/{core → query}/QueryProcessor.js +0 -0
  503. /package/dist/{core/storeContract.js → storeContract.js} +0 -0
  504. /package/dist/{core → stores}/openIDBWithTimeout.d.ts +0 -0
  505. /package/dist/{core → stores}/openIDBWithTimeout.js +0 -0
  506. /package/dist/{client → transaction/auth}/credentialEndpoint.d.ts +0 -0
  507. /package/dist/{client → transaction/auth}/credentialEndpoint.js +0 -0
  508. /package/dist/{auth → transaction/auth}/credentialPolicy.d.ts +0 -0
  509. /package/dist/{auth → transaction/auth}/credentialPolicy.js +0 -0
  510. /package/dist/{auth → transaction/auth}/credentialSource.js +0 -0
  511. /package/dist/{client → transaction/auth}/hostedEndpoints.d.ts +0 -0
  512. /package/dist/{client → transaction/auth}/hostedEndpoints.js +0 -0
  513. /package/dist/{client → transaction}/persistence.d.ts +0 -0
  514. /package/dist/{client → transaction}/persistence.js +0 -0
  515. /package/dist/{client → transaction/resources}/functionalUpdate.d.ts +0 -0
  516. /package/dist/{client → transaction/resources}/functionalUpdate.js +0 -0
  517. /package/dist/{transactions → transaction/transactions/settlement}/idempotencyKey.d.ts +0 -0
  518. /package/dist/{transactions → transaction/transactions/settlement}/idempotencyKey.js +0 -0
  519. /package/dist/{client → transaction/transport}/httpTransport.d.ts +0 -0
  520. /package/dist/{types → transaction/types}/modelData.d.ts +0 -0
  521. /package/dist/{types → transaction/types}/modelData.js +0 -0
  522. /package/dist/{types → transaction/types}/participant.d.ts +0 -0
  523. /package/dist/{types → transaction/types}/participant.js +0 -0
  524. /package/dist/{types → transaction/types}/streams.js +0 -0
  525. /package/dist/{utils → transaction/utils}/asyncIterator.d.ts +0 -0
  526. /package/dist/{utils → transaction/utils}/asyncIterator.js +0 -0
  527. /package/dist/{wire → transaction/wire}/bootstrapReason.d.ts +0 -0
  528. /package/dist/{wire → transaction/wire}/bootstrapReason.js +0 -0
  529. /package/dist/{wire → transaction/wire}/protocol.d.ts +0 -0
  530. /package/dist/{wire → transaction/wire}/protocol.js +0 -0
  531. /package/dist/{wire → transaction/wire}/protocolVersion.d.ts +0 -0
  532. /package/dist/{wire → transaction/wire}/protocolVersion.js +0 -0
  533. /package/dist/transactions/{UnconfirmedWrites.d.ts → mutations/UnconfirmedWrites.d.ts} +0 -0
  534. /package/dist/transactions/{UnconfirmedWrites.js → mutations/UnconfirmedWrites.js} +0 -0
  535. /package/dist/transactions/{optimisticApply.js → mutations/optimisticApply.js} +0 -0
  536. /package/dist/{core → views}/ViewRegistry.js +0 -0
@@ -1,11 +1,15 @@
1
1
  # Server Agent
2
2
 
3
+ > A stateless schema-backed worker: wake, claim, commit, go idle.
4
+
3
5
  A server agent is backend code — a cron job, a queue worker, an AI task — that
4
6
  reads and writes your app's records outside the browser. The hard part is doing
5
- it without racing the live UI: if your worker and a user edit the same report at
6
- once, one write clobbers the other. This is what `claim()` is for. Below, a
7
- worker finishes a weather report by claiming it, writing the result, and
8
- releasing it automatically when the claim goes out of scope.
7
+ it without racing whatever else is working: if two workers pick up the same task
8
+ at once, one write clobbers the other. This is what `claim()` is for.
9
+
10
+ Agents hold no socket, so pass `transport: 'http'` and import the same schema the
11
+ rest of the app uses. Below, a worker finishes a task by claiming it, writing the
12
+ result, and releasing it automatically when the claim goes out of scope.
9
13
 
10
14
  `claim({ id })` takes the record for your worker and returns a disposable handle:
11
15
  the fresh post-lease row is on `claim.data`, and holding the handle with
@@ -18,53 +22,69 @@ import Ablo from '@abloatai/ablo';
18
22
  import { defineSchema, model, z } from '@abloatai/ablo/schema';
19
23
 
20
24
  const schema = defineSchema({
21
- weatherReports: model({
22
- location: z.string(),
23
- status: z.enum(['pending', 'ready']),
24
- forecast: z.string().optional(),
25
+ tasks: model({
26
+ title: z.string(),
27
+ status: z.enum(['todo', 'doing', 'done']),
28
+ summary: z.string().optional(),
25
29
  }),
26
30
  });
27
31
 
28
32
  const ablo = Ablo({
29
33
  schema,
30
34
  apiKey: process.env.ABLO_API_KEY,
35
+ transport: 'http',
31
36
  });
32
37
 
33
- export async function completeReport(reportId: string) {
38
+ export async function completeTask(taskId: string) {
34
39
  await ablo.ready();
35
40
 
36
- const report = await ablo.weatherReports.retrieve({ id: reportId });
37
- if (!report) return { status: 'not_found' };
41
+ const task = await ablo.tasks.retrieve({ id: taskId });
42
+ if (!task) return { status: 'not_found' };
38
43
 
39
- await using claim = await ablo.weatherReports.claim({
40
- id: reportId,
44
+ await using claim = await ablo.tasks.claim({
45
+ id: taskId,
41
46
  queue: false,
42
47
  description: 'completing',
43
48
  });
44
- const claimed = claim.data;
45
49
 
46
- const updated = await ablo.weatherReports.update({
47
- id: claimed.id,
48
- data: { status: 'ready' },
50
+ const updated = await ablo.tasks.update({
51
+ id: claim.data.id,
52
+ data: { status: 'done' },
49
53
  wait: 'confirmed',
50
54
  });
51
55
 
52
- return { status: 'ready', report: updated };
56
+ return { status: 'done', task: updated };
57
+ // claim auto-releases as the function returns
53
58
  }
54
59
  ```
55
60
 
56
61
  `retrieve({ id })` is an async server read — it hits the server and returns the
57
- row (or `null`, which the early `not_found` guard handles). The update runs while
58
- the claim is held, and `wait: 'confirmed'` makes that update resolve only once
59
- the server has accepted it.
62
+ row (or `undefined`, which the early `not_found` guard handles). The update runs
63
+ while the claim is held, and `wait: 'confirmed'` makes it resolve only once your
64
+ database has confirmed the row landed.
60
65
 
61
66
  The two options on the claim:
62
67
 
63
68
  - `queue: false` — skip this record if another claim is already in progress,
64
- rather than queueing behind it. (The default queues.)
65
- - `description: 'completing'` a human-readable label for what your worker is doing,
69
+ rather than queueing behind it. Fail-fast dedup: *if someone else has this job,
70
+ skip it.* (The default queues.)
71
+ - `description: 'completing'` — a readable label for what your worker is doing,
66
72
  visible to anyone reading `claim.state({ id })`.
67
73
 
68
- Because the worker uses the same schema and `claim()` as the UI, its writes sync
69
- to every connected client in real time and never collide with edits already in
70
- progress.
74
+ ## Atomic batches
75
+
76
+ When several rows must change together, submit one atomic commit through the same
77
+ schema-backed client:
78
+
79
+ ```ts
80
+ await ablo.commits.create({
81
+ operations: [
82
+ { action: 'update', model: 'tasks', id: 'task_123', data: { status: 'done' } },
83
+ ],
84
+ wait: 'confirmed',
85
+ });
86
+ ```
87
+
88
+ Because the worker uses the same schema and `claim()` as everything else, its
89
+ writes reach every connected client in real time and never collide with work
90
+ already in progress.
package/docs/groups.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Change Propagation
2
2
 
3
+ > How one row's change reaches the rows and actors that depend on it.
4
+
3
5
  > How a change to one row reaches the rows and actors that depend on it, and how
4
6
  > to keep a chain of dependent work fresh. This is the propagation half of sync
5
7
  > groups; [`identity.md`](./identity.md) is the access half (who may read a
@@ -10,8 +12,8 @@
10
12
 
11
13
  ## Start from the problem
12
14
 
13
- An agent reads deck `A` to write slide `B`. A moment later it reads `B` to write
14
- layer `C`. Between those steps someone else edits `A`. The agent is now building
15
+ An agent reads workspace `A` to write document `B`. A moment later it reads `B` to write
16
+ block `C`. Between those steps someone else edits `A`. The agent is now building
15
17
  `C` on a premise that has moved — and nothing about writing `C` looks wrong in
16
18
  isolation. That is stale context, and it is the thing sync groups let you catch.
17
19
 
@@ -19,23 +21,73 @@ The recipe is one field on the commit: declare the group you read as a premise,
19
21
  and say what should happen if it moved.
20
22
 
21
23
  ```ts
22
- // The agent read everything under deck:abc to compose this write.
23
- await ablo.layers.update({
24
- id: 'layer-C',
24
+ // The agent read everything under workspace:abc to compose this write.
25
+ await ablo.blocks.update({
26
+ id: 'block-C',
25
27
  data: { text: revised },
26
- reads: [{ group: 'deck:abc', readAt: watermark, onStale: 'notify' }],
28
+ reads: [{ group: 'workspace:abc', readAt: watermark, onStale: 'notify' }],
27
29
  });
28
30
  ```
29
31
 
30
32
  At commit, inside the write transaction, the engine asks a single question: *did
31
- any delta routed to `deck:abc` land after `watermark`?* If nothing moved, the
33
+ any delta routed to `workspace:abc` land after `watermark`?* If nothing moved, the
32
34
  write applies. If something moved, `onStale` decides — `notify` holds the write
33
35
  and hands the agent a `StaleNotification` naming the group, so it re-reads
34
- `deck:abc` and regenerates; `reject` aborts the batch with a `409`. The agent
36
+ `workspace:abc` and regenerates; `reject` aborts the batch with a `409`. The agent
35
37
  never persists work built on a premise it can no longer see.
36
38
 
37
39
  ---
38
40
 
41
+ ## How you hear about it
42
+
43
+ Four channels carry "something changed", and they answer four different
44
+ questions. Pick by the question you have.
45
+
46
+ ```ts
47
+ // A screen that stays current.
48
+ ablo.documents.onChange((docs) => render(docs));
49
+
50
+ // Who else is in here, and what are they holding.
51
+ await using room = await ablo.documents.join(documentIds, { ttl: '5m' });
52
+ room.peers;
53
+
54
+ // Stop this write if the thing I read moved while I composed it.
55
+ await ablo.blocks.update({ id, data, reads: [{ group: 'workspace:abc', readAt, onStale: 'notify' }] });
56
+
57
+ // Tell me later if this moves, even though I am not writing now.
58
+ await ablo.documents.track({ id: 's-1' });
59
+ ```
60
+
61
+ | Question | Channel | Arrives |
62
+ | --- | --- | --- |
63
+ | What do the rows say right now? | `onChange` | As deltas land, on the socket |
64
+ | Who else is working here? | `join`, then `room.peers` and `room.claims` | As participants come and go, on the socket |
65
+ | Did the premise for **this** write move? | `reads` on the write | On that write's receipt, before it applies |
66
+ | Has anything I read moved since? | `track` | On your next commit's receipt |
67
+
68
+ Two distinctions do most of the work here.
69
+
70
+ **`join` is about people; `track` is about data.** Both open a subscription and
71
+ both are scoped by sync group, which is why they look alike. `join` reports
72
+ participants: who is present, what they are doing, which rows they hold. `track`
73
+ reports the rows themselves: something you said you cared about moved, here is
74
+ the watermark to re-read it at. A tool that wants to avoid duplicating a peer's
75
+ work needs `join`. A tool whose output goes stale when its inputs change needs
76
+ `track`.
77
+
78
+ **`reads` guards one write; `track` outlives it.** They speak the same
79
+ vocabulary and produce the same `StaleNotification`. A `reads` entry is checked
80
+ once, at the commit that carried it, and discarded. A `track` is persisted and
81
+ re-checked against every delta after it, so a long-running actor hears about a
82
+ change that landed while it was thinking, on the next commit it makes.
83
+
84
+ `onChange` and `join` need a live socket, so they are available on the default
85
+ WebSocket client. `reads` and `track` ride the commit, so they reach a socketless
86
+ actor over HTTP too, which is what makes them the notification path for agents
87
+ and workers.
88
+
89
+ ---
90
+
39
91
  ## Three ways a change reaches other rows
40
92
 
41
93
  "A affects B and C" means three different things. The engine does the first two
@@ -43,13 +95,13 @@ for you and leaves the third to you — on purpose.
43
95
 
44
96
  **Routing — who hears about a change.** Every row belongs to one or more sync
45
97
  groups, and a write fans out to all of them. A row also inherits its ancestors'
46
- groups: editing a layer stamps the delta with `layer:…`, `slide:…`, *and*
47
- `deck:…`, so everyone watching the deck sees the layer move. This is delivery,
98
+ groups: editing a block stamps the delta with `block:…`, `document:…`, *and*
99
+ `workspace:…`, so everyone watching the workspace sees the block move. This is delivery,
48
100
  resolved by walking the ownership tree at commit time. It routes the change; it
49
101
  never recomputes a value.
50
102
 
51
- **Structural cascade — what disappears with a change.** Deleting a deck removes
52
- its slides and layers. The database does that through `ON DELETE CASCADE`, but a
103
+ **Structural cascade — what disappears with a change.** Deleting a workspace removes
104
+ its documents and blocks. The database does that through `ON DELETE CASCADE`, but a
53
105
  database-level cascade emits no delta, so open clients would quietly hold rows
54
106
  that no longer exist. The engine closes that gap: before the delete it snapshots
55
107
  the subtree and emits a tombstone for each descendant, routed to the right
@@ -85,8 +137,8 @@ reaches `C`.
85
137
 
86
138
  The direction matters. The signal flows forward, A to B to C, and each hop is a
87
139
  real write an actor chose to make. The engine supplies the edges (group
88
- membership) and a stale signal on each edge (the read-dependency check); the
89
- actors are the runtime that walks them. It is closer to a spreadsheet an analyst
140
+ membership) and a stale signal on each edge (the premise check); the actors are
141
+ the runtime that walks them. It is closer to a spreadsheet an analyst
90
142
  recalculates cell by cell than to a reactive engine that recomputes the whole
91
143
  column for you.
92
144
 
@@ -102,16 +154,17 @@ Two consequences worth designing around:
102
154
 
103
155
  ---
104
156
 
105
- ## Declaring a read premise
157
+ ## Declaring the batch premise
106
158
 
107
- A read-dependency is a premise for the *whole* batch: its disposition governs
108
- every write in the commit, not just one operation. You choose the granularity per
159
+ `reads[]` declares what the commit was based on. Each entry is a premise, and
160
+ each governs the *whole* commit: if one goes stale, its disposition applies to
161
+ every write in the batch, not just one operation. You choose the granularity per
109
162
  entry.
110
163
 
111
164
  ```ts
112
165
  reads: [
113
- { group: 'deck:abc', readAt: N, onStale: 'notify' }, // did anything in the deck move?
114
- { model: 'Slide', id: 's-1', readAt: N, fields: ['title'] }, // did this row (this field) move?
166
+ { group: 'workspace:abc', readAt: N, onStale: 'notify' }, // did anything in the workspace move?
167
+ { model: 'Document', id: 's-1', readAt: N, fields: ['title'] }, // did this row (this field) move?
115
168
  ]
116
169
  ```
117
170
 
@@ -137,7 +190,7 @@ the same row don't collide.
137
190
 
138
191
  ## Staying subscribed across commits: `track`
139
192
 
140
- A read premise guards a single commit: you state what you read, the engine
193
+ A batch premise guards a single commit: you state what you read, the engine
141
194
  checks it, the premise is gone. That fits an actor that reads and writes in one
142
195
  breath. It does not fit a long-running one — an agent that reads a row now,
143
196
  works for a few minutes, and writes much later. By the time it commits, the
@@ -152,10 +205,10 @@ you, arriving on the write you were going to make anyway.
152
205
 
153
206
  ```ts
154
207
  // Register interest and walk away — no write required.
155
- await ablo.slides.track({ id: 's-1' });
208
+ await ablo.documents.track({ id: 's-1' });
156
209
 
157
210
  // …minutes of other work later, on your next commit…
158
- const res = await ablo.layers.update({ id: 'layer-C', data: { text: revised } });
211
+ const res = await ablo.blocks.update({ id: 'block-C', data: { text: revised } });
159
212
  res.notifications; // populated if s-1 moved under you in the meantime
160
213
  ```
161
214
 
@@ -169,11 +222,11 @@ You can also register a track as part of a write you are already making, the
169
222
  persisted companion to `reads`:
170
223
 
171
224
  ```ts
172
- await ablo.slides.update({
225
+ await ablo.documents.update({
173
226
  id: 's-1',
174
227
  data: { title: revised },
175
- reads: [{ group: 'deck:abc', readAt: N, onStale: 'notify' }], // guards THIS commit
176
- track: [{ group: 'deck:abc' }], // and keeps watching after it
228
+ reads: [{ group: 'workspace:abc', readAt: N, onStale: 'notify' }], // guards THIS commit
229
+ track: [{ group: 'workspace:abc' }], // and keeps watching after it
177
230
  });
178
231
  ```
179
232
 
@@ -194,8 +247,8 @@ broad wakes actors for changes they don't care about, and one that is too narrow
194
247
  misses the dependency you meant to track.
195
248
 
196
249
  The rule of thumb: **make a group the smallest set of rows that must stay
197
- mutually consistent.** A deck and its slides belong together because editing one
198
- changes what the others mean; two unrelated decks do not. Reach for finer,
250
+ mutually consistent.** A workspace and its documents belong together because editing one
251
+ changes what the others mean; two unrelated workspaces do not. Reach for finer,
199
252
  overlapping groups when you genuinely have a dependency chain to track, and keep
200
253
  them coarse everywhere else.
201
254
 
@@ -203,8 +256,12 @@ them coarse everywhere else.
203
256
 
204
257
  ## Where this is defined
205
258
 
206
- - **Access** who may read or write a group is [`identity.md`](./identity.md).
207
- - **The convention** — non-coercion, the read-set, and the notification — is
259
+ - **Access**, meaning who may read or write a group, is
260
+ [`identity.md`](./identity.md).
261
+ - **The convention** behind non-coercion, the premise, and the notification is
208
262
  [`concurrency-convention.md`](./concurrency-convention.md) (§4 and §5).
209
- - **The mechanics** the three coordination layers underneath are
263
+ - **The mechanics**, the three coordination blocks underneath, are
210
264
  [`coordination.md`](./coordination.md).
265
+ - **`join` and presence**, the participant half of the table above, are
266
+ [`coordination.md`](./coordination.md) for the claim stream and
267
+ [`react.md`](./react.md) for `useJoin`.
@@ -1,7 +1,9 @@
1
1
  # Guarantees
2
2
 
3
- When an Ablo write succeeds, the server has accepted it and when two people or
4
- agents touch the same row, Ablo coordinates them instead of letting one silently
3
+ > Exactly what a confirmed write, a rejected stale write, and a held claim each promise.
4
+
5
+ When an Ablo write succeeds, the server has accepted it — and when two agents
6
+ touch the same row, Ablo coordinates them instead of letting one silently
5
7
  overwrite the other. This page is the precise list of what you can count on:
6
8
  confirmed writes, stale-write protection, claims, and the audit trail behind
7
9
  every change.
@@ -63,11 +65,13 @@ await ablo.weatherReports.update({
63
65
  `onStale: 'reject'` prevents lost updates. If the target changed after the
64
66
  snapshot, the server rejects the write instead of applying stale reasoning.
65
67
 
66
- Advanced policies exist for controlled product flows:
68
+ Two other dispositions exist. `overwrite` applies the write with no stale check
69
+ at all. `notify` **holds** the write, so the row is left as it stands, and hands
70
+ back a `StaleNotification` carrying the current value for the actor to reconcile
71
+ and re-issue; the rest of the batch still commits.
67
72
 
68
- - `reject` fails the write when state moved.
69
- - `overwrite` applies the write without stale protection.
70
- - `notify` accepts the write and marks it for product review.
73
+ See [Concurrency Convention](./concurrency-convention.md) for the full taxonomy,
74
+ what each disposition is checked against, and where the convention stops.
71
75
 
72
76
  ## Claim Coordination
73
77
 
@@ -97,14 +101,39 @@ Agents should import the same schema as the app and write through
97
101
 
98
102
  ## Audit Trail
99
103
 
100
- Accepted writes can be attributed to:
104
+ Attribution is not a separate log you opt into. It rides on the change itself.
105
+ Every broadcast delta names the actor, the authority it acted under, the
106
+ credential that authorized it, and the approval stage it was in:
107
+
108
+ ```ts
109
+ {
110
+ modelName: 'weatherReports',
111
+ modelId: 'report_stockholm',
112
+ actionType: 'U',
113
+ actor: { kind: 'agent', id: 'weather-agent-v3' },
114
+ onBehalfOf: { kind: 'user', id: 'user_8f2a' },
115
+ capabilityId: '…', // the key the write was authorized by
116
+ confirmationState: 'auto', // previewed | approved | required_human_approval
117
+ createdAt: '2026-05-14T14:22:01.034Z',
118
+ }
119
+ ```
120
+
121
+ `actor` and `onBehalfOf` are derived from the credential, not from the call site,
122
+ so an agent cannot name a different actor in its own write. `capabilityId` is
123
+ non-null for every agent and system commit, so a write can always be traced to
124
+ the key that made it, and from that key to the person it was issued to.
125
+
126
+ The stored history goes one step further than recording. Audit rows are chained
127
+ with a keyed hash, so the log is tamper-*evident*: `verify-chain` walks the chain
128
+ and, if it breaks, names the sequence number and the hashes that disagree. No
129
+ chain roots at an agent. The delegation root is always the person who set the
130
+ work in motion.
101
131
 
102
- - the actor that wrote,
103
- - the human or system the actor worked on behalf of,
104
- - the model, operation, and state cursor.
132
+ For agent work this is what answers, after the fact: what changed, who authorized
133
+ it, which run did it, and whether a human was in the loop.
105
134
 
106
- For agent work, this is what lets an audit surface answer: "what changed, who
107
- authorized it, which run did it, and what state was it based on?"
135
+ See [Audit Log](./audit.md) for the stored row shape, the filters, verification,
136
+ and export.
108
137
 
109
138
  ## Persistence
110
139
 
@@ -1,5 +1,7 @@
1
1
  # How Ablo Works
2
2
 
3
+ > You write through Ablo, Ablo writes to your Postgres, and the write-ahead log confirms it.
4
+
3
5
  You write through Ablo, and Ablo writes to your Postgres. That one sentence is the
4
6
  whole model — everything below explains what it means and how to use it.
5
7
 
@@ -8,16 +10,16 @@ whole model — everything below explains what it means and how to use it.
8
10
  await ablo.tasks.update({ id: 'task_42', data: { status: 'done' }, wait: 'confirmed' });
9
11
 
10
12
  // Reads come back live, kept current from your database.
11
- const task = ablo.tasks.get('task_42');
13
+ const task = ablo.tasks.local.retrieve('task_42');
12
14
  ```
13
15
 
14
- ## The mental model read this once
16
+ ## The mental model: read this once
15
17
 
16
- Ablo is a **coordination layer in front of your Postgres**. Humans, agents, and
17
- background jobs all change the same application data through one API, and Ablo
18
- makes sure their writes don't clobber each other.
18
+ Ablo is a **coordination layer in front of your Postgres**. Agents, background
19
+ jobs, and the people alongside them all change the same application data through
20
+ one API, and Ablo makes sure their writes don't clobber each other.
19
21
 
20
- - **Writes go through Ablo.** `ablo.<model>.create / update / delete` enter Ablo's
22
+ - **Writes go through Ablo:** `ablo.<model>.create / update / delete` enter Ablo's
21
23
  commit chokepoint — where claims, ordering, and idempotency are enforced — and
22
24
  Ablo applies the change to your Postgres through a scoped writer role. The commit
23
25
  is accepted (`queued`) the moment Ablo takes it.
@@ -35,17 +37,41 @@ makes sure their writes don't clobber each other.
35
37
  That's the shape: **you write through Ablo → it lands in your Postgres → the WAL
36
38
  echo confirms it → everyone connected sees it live.**
37
39
 
40
+ ## The primitives
41
+
42
+ | Primitive | Plane | Purpose |
43
+ |---|---|---|
44
+ | `Schema` | State | Declares typed models the app and agents can read and write. |
45
+ | `Model` | State | The generated `ablo.<model>` model. Use `retrieve`/`list` (async reads), `local.retrieve`/`local.list`/`local.count` (the same verbs, synchronous and local-only), `create`, `update`, and `delete`. |
46
+ | `Claim` | Coordination | Who is working on a target. Taken via `ablo.<model>.claim({ id })` and read via `ablo.<model>.claim.state({ id })`. Ephemeral, never persisted. |
47
+ | `Commit` | Protocol | The durable write underneath model updates. Most users do not call it directly. |
48
+ | `Receipt` | Protocol | The lower-level durable result for custom runtimes. Schema writes use `wait: 'confirmed'`. |
49
+
50
+ ### Why each primitive is separate
51
+
52
+ Why are `Claim`, `Commit`, and `Receipt` separate things instead of one? Each
53
+ does a job the others cannot. If you are coming from Replicache or Yjs you would
54
+ expect just `Commit`. Here is what the other two buy you over that minimum:
55
+
56
+ - **`Claim` is not a read lock.** Reads stay open. Claims serialize
57
+ acting-on-the-row, so slow work can wait in FIFO order, re-read, and write
58
+ from fresh state.
59
+ - **`Receipt` is not a `200 OK`.** It is the durable artifact a commit produced:
60
+ accepted commit id, server-assigned timestamps, stale-check outcome. It is
61
+ addressable after the fact and replayable into a different client. A status
62
+ code cannot be re-read by a sub-agent that was not on the original call.
63
+
38
64
  ## Where your data lives
39
65
 
40
66
  You point Ablo at a Postgres database, and that's where its rows live. Only *which*
41
67
  database differs by environment — the code is identical.
42
68
 
43
- - **Production** your Postgres. `ablo connect` sets up a scoped writer role and
69
+ - **Production:** your Postgres. `ablo connect` sets up a scoped writer role and
44
70
  logical replication; your rows live in your database, and Ablo writes to them
45
71
  through that role.
46
- - **Sandbox and local dev** a separate or local Postgres you can throw away. Same
72
+ - **Sandbox and local dev:** a separate or local Postgres you can throw away. Same
47
73
  models, same code, a different database behind them.
48
- - **Before you connect one** Ablo keeps state in its own log, so you can build the
74
+ - **Before you connect one.** Ablo keeps state in its own log, so you can build the
49
75
  whole app today and point it at a real database when you're ready.
50
76
 
51
77
  Registering the database is the whole switch. There is no tier or flag to choose.
@@ -57,7 +83,7 @@ Registering the database is the whole switch. There is no tier or flag to choose
57
83
  npm install @abloatai/ablo
58
84
  npx ablo init
59
85
 
60
- # 2. Push your schema (the models humans and agents edit together).
86
+ # 2. Push your schema (the models your agents edit together).
61
87
  npx ablo push
62
88
 
63
89
  # 3. Connect your database — one command, admin credential used once and discarded.
@@ -80,13 +106,13 @@ export const ablo = Ablo({ schema, apiKey: process.env.ABLO_API_KEY });
80
106
  await ablo.tasks.update({ id: 'task_42', data: { status: 'done' }, wait: 'confirmed' });
81
107
 
82
108
  // 6. Read — live, no fetch loop.
83
- const task = ablo.tasks.get('task_42');
109
+ const task = ablo.tasks.local.retrieve('task_42');
84
110
 
85
111
  // 7. Coordinate when more than one actor can touch a row. Hold a claim and Ablo
86
112
  // serializes writes on that key against everyone else; read after claiming,
87
113
  // then write. The lease releases automatically at the end of the scope.
88
114
  await using _hold = await ablo.tasks.claim('task_42');
89
- const latest = ablo.tasks.get('task_42'); // read after claiming, not from memory
115
+ const latest = ablo.tasks.local.retrieve('task_42'); // read after claiming, not from memory
90
116
  await ablo.tasks.update({ id: 'task_42', data: { status: 'done' } });
91
117
  ```
92
118
 
@@ -0,0 +1,126 @@
1
+ # Idempotency
2
+
3
+ > Make a retried write safe: the same key never applies the same change twice.
4
+
5
+ An agent retries. A socket drops mid-commit, a worker restarts, a queue redelivers — and the write
6
+ you already sent arrives again. An idempotency key is how Ablo tells a retry from a new intention.
7
+
8
+ Every model write carries one. The SDK generates a key when you omit it, which makes an in-process
9
+ retry safe automatically. It cannot make a retry across a process restart safe, because a new
10
+ process generates a new key — so for anything that must survive a crash, supply your own.
11
+
12
+ ```ts
13
+ await ablo.tasks.update({
14
+ id: taskId,
15
+ data: { status: 'done' },
16
+ idempotencyKey: `task:${taskId}:mark-done:v1`,
17
+ wait: 'confirmed',
18
+ });
19
+ ```
20
+
21
+ ## The one rule
22
+
23
+ **Derive the key from the business event, not from the attempt.** A key built from
24
+ `crypto.randomUUID()` at the call site is regenerated on every retry, so it protects nothing — each
25
+ attempt looks like a new intention and the write lands twice. A key built from the thing that
26
+ happened (`task:42:mark-done:v1`) is identical on every retry by construction, which is the whole
27
+ point.
28
+
29
+ The same rule stated as its failure: never derive a key from a timestamp, an attempt counter, or a
30
+ random value. If two retries of one operation can produce different keys, you have no idempotency.
31
+
32
+ ## How it works
33
+
34
+ The key is not a lookup that happens before the write — it is the **execution lock on the write
35
+ itself**. Ablo inserts a pending row keyed by the caller and the key inside the same transaction as
36
+ the mutation, and a unique index makes that insert the lock:
37
+
38
+ - **Insert wins:** this transaction owns the execution and runs the write.
39
+ - **Insert conflicts:** someone else owns it. The second caller waits for the owner to finish and
40
+ then replays its recorded result.
41
+ - **Insert conflicts, different request:** the key was reused to mean something else. Rejected.
42
+
43
+ Because the lock and the write share a transaction, there is no window in which a write has happened
44
+ but its key has not been recorded.
45
+
46
+ Keys are scoped to **the organization and the participant**, not globally. Two agents can use the
47
+ same key string without colliding, and one agent can never replay another's result.
48
+
49
+ ## The four outcomes
50
+
51
+ | You send | Ablo does |
52
+ |---|---|
53
+ | A new key | Runs the write. |
54
+ | The same key, the same request, already finished | Replays the recorded result. The write does not run again. |
55
+ | The same key, the same request, still running | Waits for the in-flight attempt, then replays its result. If the original is still running after a short wait, rejects with `idempotency_conflict` (409): retry the same key. |
56
+ | The same key, a **different** request | Rejects with `idempotency_conflict` (409). A key is bound to the request it first arrived with. |
57
+
58
+ Both conflict cases return the same code, so tell them apart by what your own
59
+ client did. If you retried an identical request, the original is still in flight
60
+ — wait and retry the same key. If you changed the request, that is a client bug:
61
+ use a new key.
62
+
63
+ ## Failures are not replayed: they re-run
64
+
65
+ This is where Ablo deliberately differs from Stripe and from most payment APIs, and it is the
66
+ behaviour most likely to surprise you.
67
+
68
+ **Only successful writes are recorded.** A write that failed leaves no idempotency record, so
69
+ retrying it with the same key **executes fresh** rather than replaying the error.
70
+
71
+ That is the right default here because most failures are ones you can fix and legitimately want to
72
+ re-attempt — a validation error, a stale premise, a claim held by someone else. Replaying the
73
+ original error for 24 hours would strand the caller behind a decision that is no longer true.
74
+
75
+ The consequence to hold onto: a retry after a failure is a real execution. If a write failed in a
76
+ way that leaves you unsure whether it landed — a timeout, a dropped socket — do not assume the retry
77
+ is a no-op. Retry with the **same key**: if the original did land, the recorded success replays; if
78
+ it did not, the write runs now. That is exactly the case idempotency exists for.
79
+
80
+ ## The window
81
+
82
+ A recorded result is retained for **24 hours**, then expires. Within that window a repeated key
83
+ replays. After it, the key is forgotten and reusing it starts a genuinely new write.
84
+
85
+ Treat 24 hours as *how long a retry is guaranteed safe*, not as permanent deduplication. A nightly
86
+ job that reuses yesterday's key will execute again.
87
+
88
+ Writes routed to a registered data source are the exception: their intent is retained **permanently**
89
+ rather than expiring, because letting that record lapse would make a reused key indistinguishable
90
+ from old work against your database. A key whose retained intent has expired is rejected with
91
+ `idempotency_key_expired` (409) rather than being silently re-executed.
92
+
93
+ ## Route pinning
94
+
95
+ A key is bound to the route its first attempt took. If an earlier attempt was applied through a
96
+ direct data source and a retry arrives when the endpoint fallback is active, Ablo rejects it with
97
+ `source_transport_pinned` (409) instead of switching.
98
+
99
+ That refusal is deliberate: switching routes on a retry risks applying a write that the first route
100
+ may already have committed. Restore the original route and retry the same key.
101
+
102
+ ## When to retry
103
+
104
+ | Situation | Do |
105
+ |---|---|
106
+ | Timeout or dropped connection, no response | Retry with the **same** key, with backoff. You get the recorded success, or the write runs now. |
107
+ | `source_unreachable` (503) | Retry with the **same** key once connectivity recovers. The write stays pinned to its route. |
108
+ | `replication_lag_timeout` (504) | The write may have materialized. Retry with the **same** key, or wait for source ingestion to catch up. |
109
+ | `AbloStaleContextError` | Re-read the row, regenerate, then write under a **new** key: the new write is a new intention. |
110
+ | `AbloClaimedError` | Someone else holds the row. Wait or yield; the key is unused, so reuse it when you retry. |
111
+ | `idempotency_conflict` (409) after an identical retry | The original is still in flight. Wait, then retry the **same** key. |
112
+ | `idempotency_conflict` (409) after changing the request | A client bug: a key is bound to the first request sent under it. Use a **new** key. |
113
+ | `idempotency_key_too_long` (400) | The key exceeds 255 characters. A UUID or a short business string works. |
114
+
115
+ ## Keys
116
+
117
+ - Up to **255 characters**. Longer is rejected, not truncated.
118
+ - Unique per logical operation, identical across every retry of that operation.
119
+ - Generate a new one only when a genuinely new operation begins.
120
+ - Never put secrets in a key — it is stored and appears in support diagnostics.
121
+
122
+ ## Related
123
+
124
+ - [Client Behavior](./client-behavior.md) — every write option, and which errors retry.
125
+ - [Guarantees](./guarantees.md) — what `queued` and `confirmed` promise.
126
+ - [Errors](./errors.md) — the full code registry, including every code named above.