@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
package/CHANGELOG.md CHANGED
@@ -1,5 +1,758 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.36.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 816081b: `claim({ id })` now means the same thing on every transport: serialize me
8
+ behind whoever holds this row. The stateless HTTP client used to reject a
9
+ contended claim with `claim_queued` and leave the wait to you; now it holds
10
+ your place in the server's FIFO line — heartbeating the queued ticket under
11
+ the hood — and resolves to the same held claim the realtime client hands
12
+ back, with the row re-read after the grant so your snapshot reflects what the
13
+ previous holder committed. The contended-agent snippet is copy-pastable
14
+ between a browser tab and a Lambda without edits. Shape the wait the same way
15
+ on either transport: the new `waitTimeoutMs` caps it (rejects `grant_timeout`
16
+ and leaves the line), the new `signal` cancels it from outside (an
17
+ `AbortSignal` — a cancelled agent task or an unmounted component takes its
18
+ queued claim with it, rejecting `claim_wait_aborted`), `maxQueueDepth` bounds
19
+ the line you'll join, and `queue: false` is now the try-claim: a held
20
+ target resolves `null` — a declined try is an expected outcome, not an
21
+ error — so claim-or-skip dedup reads `if (!claim) return` with no
22
+ try/catch. (Previously it rejected with `entity_claimed`; a _write_ to a
23
+ held row still does.)
24
+
25
+ For callers that manage the wait themselves, the HTTP client gains an
26
+ `ablo.claims` namespace — the ticket surface: `retrieve({ claimId })` polls a
27
+ ticket to its grant (fence token included), `heartbeat({ claimId, ttl? })`
28
+ keeps a held or queued lease alive by its id, `heartbeatAll({ ttl? })`
29
+ extends every lease your identity holds in one round trip, and
30
+ `release({ claimId })` gives a ticket back, promoting the waiters behind you.
31
+
32
+ Claims can also target part of a row — a `path` into a document, a `range` of
33
+ text, a `field` or set of `fields` — and two claims on non-overlapping parts
34
+ of the same row are both granted. The engine has enforced this all along; it
35
+ is now documented (the coordination reference leads with it), proven by
36
+ grant-level tests, and gated so the docs can never again describe a narrower
37
+ target than the wire accepts. The part names also read your schema now:
38
+ `field` and `fields` autocomplete from the model's own field names, and an
39
+ app-defined part — a cell, a section — has an explicit spelling: `part('B2')`
40
+ (exported from the package root), which will become the only spelling for
41
+ non-field names once the typed react binding removes the last loose arm.
42
+ Inverted ranges (`startLine > endLine`) are rejected at the boundary instead
43
+ of granting a lease that excludes no one.
44
+
45
+ The claim options themselves now show their structure: every option sits on
46
+ one of four axes — what you claim (`field`/`fields`/`path`/`range`), what
47
+ others see (`description`/`meta`), how you wait
48
+ (`queue`/`maxQueueDepth`/`waitTimeoutMs`/`signal`), and how long you hold
49
+ (`ttl`/`heartbeat`) — and the docs and the type read in that order. The
50
+ lease axis slims from four members to two:
51
+ `heartbeat: { every?, onBeat?, onLost? }` carries the cadence and both
52
+ callbacks in one place, with `true` and a bare duration still the
53
+ shorthands. Removed: the flat `onHeartbeat` and `onHeartbeatLost` options —
54
+ move each callback onto the plan, unchanged: `heartbeat: { onBeat, onLost }`.
55
+
56
+ The react layer gains its typed binding: `createAbloReact(schema)` (from
57
+ `@abloatai/ablo/react`) returns `{ AbloProvider, useAblo }` with the schema
58
+ captured once — the one-binding-file convention of tRPC and typed
59
+ react-redux hooks. Components import `useAblo` from your `lib/ablo` and
60
+ never spell a type argument; the global `AbloProvider`/`useAblo<R>` keep
61
+ working unchanged.
62
+
63
+ Coordination errors now name the next call: `grant_timeout` says how to
64
+ rejoin or proceed, `stale_context` points at the functional update, and
65
+ `queue_too_deep` distinguishes its claim meaning from its write meaning.
66
+ Removed: the `claim_wait_poll_interval_required` error code — no code path
67
+ ever raised it, so no caller can be affected.
68
+
69
+ - 7950669: A new `@abloatai/ablo/syncLog` subpath exposes the canonical sync-log contracts:
70
+ the Zod schemas and types for a delta's position and identity (`deltaPosition`,
71
+ `clientMutationId`, `sourceCorrelationId`, `sourceChangeId`, `replicationLsn`),
72
+ and the `commitDispatchMarker` that names the delta range a commit wrote. These
73
+ are the single definition the client and server both read, so a tool that
74
+ follows or reconciles the log validates positions and markers against the same
75
+ shapes the engine enforces.
76
+ - 61e4daf: The `/testing` subpath no longer ships. It carried the package's own test
77
+ scaffolding — mocks, fixtures, and harnesses built on internals that were never
78
+ a stable contract. If your tests need a stand-in for the write path, implement
79
+ the public `MutationExecutor` interface from the `/core` subpath; a recording
80
+ executor is about eighty lines and stays type-checked against the same contract
81
+ the real client uses. For storage-adapter authors, `/source/conformance` remains
82
+ the supported suite for proving an adapter correct.
83
+
84
+ ## 0.35.0
85
+
86
+ ### Minor Changes
87
+
88
+ The `@abloatai/ablo/agent` entry point is removed, along with the single name it exported, `Agent`.
89
+
90
+ There is no separate agent SDK, and in practice there has not been one for some time: an agent is
91
+ the same client with a different transport. `Ablo({ schema, apiKey, transport: 'http' })` returns a
92
+ stateless, request/response client whose credential is its identity, carrying the same typed
93
+ `ablo.<model>` surface a browser or a server action uses, minus the parts that need a socket — the
94
+ `local` reads and `onChange`, which are absent from the type rather than failing at runtime. A
95
+ second entry point offered a second way in and, mostly, a second thing to keep in step with the
96
+ first. If you imported from `@abloatai/ablo/agent`, import from `@abloatai/ablo` and select the
97
+ transport.
98
+
99
+ `ClaimEvent.participantKind` is also removed. A claim event is one transition in a lease's
100
+ lifecycle — acquired, queued, granted, lost, rejected, expired — and it identifies the actor that
101
+ moved it. Whether that actor was a person, an agent, or the system is a property of the holder, not
102
+ of the transition, and stating it on every event meant maintaining the same fact in two shapes. Read
103
+ it where the holder is described instead: `ablo.<model>.claim.state({ id })` returns the current
104
+ holder with its `participantKind`, which is what the "an agent is editing this" affordance in a UI
105
+ has always read.
106
+
107
+ The client is now the coordination layer, and the things built on top of it are
108
+ capabilities you install rather than parts of the client you get whether you use
109
+ them or not.
110
+
111
+ ```ts
112
+ import { Ablo, humans } from '@abloatai/ablo';
113
+
114
+ const ablo = Ablo({ schema, apiKey, plugins: [humans()] });
115
+ ```
116
+
117
+ `humans()` declares the local, watchable copy: the offline store, live queries,
118
+ presence, and the framework bindings — everything that exists because a person
119
+ is looking at the screen. What remains underneath is what coordination actually
120
+ needs: commit, read, observe, claim. Omit `plugins` and `humans()` is installed
121
+ for you, so an application that renders anything today keeps working with no
122
+ change; naming it in the list only makes visible what was already true.
123
+
124
+ There is no `agents()` counterpart, and the absence is the point. An agent is
125
+ not a special caller that needs its own mode — it is the ordinary one, and what
126
+ it needs is the client without the materialiser. That is the same observation
127
+ behind removing the `/agent` entry point in this release: there was never a
128
+ second SDK to enter, only a client with a transport that does not open a socket.
129
+ `Ablo({ schema, apiKey, transport: 'http' })` is the whole of it, and because
130
+ `humans()` requires a duplex connection, asking for it on that client fails while
131
+ the client is being constructed, with an error naming the plugin, rather than
132
+ resolving to a presence stream that can never receive a frame. A plugin listed
133
+ twice fails the same way.
134
+
135
+ The rearrangement is also why the reactive `ablo.<model>` surface composes from
136
+ the transport-independent one rather than restating it — the note on the renamed
137
+ local reads describes what that buys — and why `track`, a durable premise that
138
+ keeps no local copy, sits in the core where an agent can reach it without
139
+ installing a materialiser it has no use for.
140
+
141
+ This is a seam, not a finished migration. `humans()` today constructs the
142
+ presence stream and declares the constraint that keeps it off a stateless
143
+ client; the rest of the materialiser — the store, the bootstrap, the delta
144
+ pipeline, the framework bindings — still lives in the composition root and moves
145
+ behind the same door as the plugin context grows to carry what each piece needs.
146
+ The contract those pieces will arrive through is the one published here, so a
147
+ capability written against it now keeps working as they do.
148
+
149
+ The shape of a claim's `meta` is now declared once, on `Register`, beside your
150
+ schema:
151
+
152
+ ```ts
153
+ declare module '@abloatai/ablo' {
154
+ interface Register {
155
+ Schema: typeof schema;
156
+ ClaimMeta: { blocks: string[] };
157
+ }
158
+ }
159
+ ```
160
+
161
+ From then on `claim.state({ id })?.target.meta`, every entry in `claim.queue`,
162
+ and the claim you hold under `await using` all read that shape, and the `typeof`
163
+ guard each of those reads used to need is gone. The same declaration types what
164
+ you write — `claim({ id, meta: { blocks } })` — so the writer and every reader
165
+ agree by construction rather than by convention, which is the half a per-call
166
+ type argument could never reach: naming the shape at the read site said nothing
167
+ about what the write site was allowed to put there.
168
+
169
+ Prefer the registration to the per-call `claim.state<MyMeta>({ id })`, because
170
+ one declaration is one place to change when the metadata grows a field. The
171
+ type argument remains supported and is still the honest answer for a program
172
+ carrying more than one metadata shape; only its default moved, from a bare
173
+ record to whatever you registered. A program that registers nothing sees no
174
+ change at all — `target.meta` stays the loose record it has always been.
175
+
176
+ What travels between participants is untouched. Claim metadata is carried
177
+ verbatim and never interpreted, and the protocol keeps accepting any object it
178
+ is given, so a peer on a newer build can send a field this one has never heard
179
+ of and it still arrives intact. The declaration is a promise about what your own
180
+ program writes, not a filter on what it receives.
181
+
182
+ A model's conflict stance can be written as a chain through one name.
183
+
184
+ ```ts
185
+ import { coordination, model, z } from '@abloatai/ablo/schema';
186
+
187
+ export const cards = model(
188
+ {
189
+ title: z.string(),
190
+ },
191
+ {
192
+ conflict: coordination.humansOverwrite().agentsReject(),
193
+ }
194
+ );
195
+ ```
196
+
197
+ The nine disposition rules were the only loose functions left in the schema DSL.
198
+ Everything else there arrives under a noun that owns it — `relation.belongsTo`,
199
+ `field.string`, `mutable.lazy` — but declaring that humans overwrite and agents
200
+ yield meant importing three separate names to say one thing, and a schema with a
201
+ couple of stances spent more of its import list on conflict rules than on the rest
202
+ of the language combined.
203
+
204
+ So the rules now hang off `coordination`, and each one returns an axis that is
205
+ still open to the next, the way a field chains its own modifiers. The rules
206
+ themselves are unchanged, and each remains the single place its disposition map is
207
+ written; the chain delegates to the standalone function rather than restating it.
208
+ Those standalone functions stay exported, and `coordination()` still merges them,
209
+ which is the form to reach for when the rules are assembled at runtime rather than
210
+ written out:
211
+
212
+ ```ts
213
+ const stance = coordination(humansOverwrite(), agentsReject());
214
+ ```
215
+
216
+ Both forms produce the same plain, serializable map keyed by the committer's
217
+ participant kind — `{ user: 'overwrite', agent: 'reject' }` — and the engine reads
218
+ it at the commit chokepoint exactly as before. A kind left unnamed still falls
219
+ through to the engine default, and a kind named twice still takes the later rule.
220
+
221
+ `captureTransactionFailure` on the observability provider is renamed to `captureMutationFailure`.
222
+ If you pass an `observability` provider to `Ablo(...)`, rename that one method; its argument is
223
+ unchanged.
224
+
225
+ The rename frees a word that had been doing two jobs. A transaction, in this system, is a unit of
226
+ write that settles — the thing you author, hand to the engine, and get a receipt for. But the same
227
+ word also named the client's optimistic queue, the buffer of writes that have been applied locally
228
+ and are still waiting for the server to agree. The callback belonged to the second meaning: it
229
+ fires when a queued write has exhausted its attempts and will never land, so the local change is
230
+ rolled back and something upstream needs to hear about it. Calling that a transaction failure
231
+ invited the reading that a settlement had failed, which is a different event with a different
232
+ remedy — and as the settlement layer grew a vocabulary of its own, the collision stopped being
233
+ cosmetic.
234
+
235
+ There is no compatibility alias, and the reason is worth stating, because it is the opposite of
236
+ the usual one. `captureMutationFailure` is a required member of the provider interface. A provider
237
+ still carrying the old spelling therefore does not satisfy that interface at all: the compiler
238
+ stops the build and names the missing member, at the line where the provider is constructed. An
239
+ optional alias would have made that build succeed while the engine went on calling a method the
240
+ provider does not implement — quiet in exactly the way a renamed callback must not be. The
241
+ compiler is the better messenger here, and this note is the rest of the message.
242
+
243
+ `debug`, `logLevel`, and `persistence` are no longer accepted on the stateless HTTP client. They
244
+ remain exactly where they have always worked — on the default client, over the socket.
245
+
246
+ They were reachable on `Ablo({ transport: 'http' })` only because that client's options type was
247
+ assembled from the whole composition bag rather than from the fields the transport actually reads,
248
+ and nothing on the HTTP path ever read them. `persistence` configures a local store a stateless
249
+ client does not keep. `debug` and `logLevel` turn on the `[Ablo]` coordination trace, which
250
+ narrates presence, claim grants, and queueing — frames that arrive over a socket an HTTP client
251
+ never opens. Passing any of the three was accepted and did nothing, which is the kind of option
252
+ that costs a reader an afternoon before they conclude the feature is broken.
253
+
254
+ If you passed one to an HTTP client, delete it: nothing changes, because nothing was happening.
255
+ If what you wanted was the trace from a server-side worker, it is not a setting you were missing —
256
+ a stateless agent holds no live session to narrate, and its writes are visible in the audit log and
257
+ in `ablo logs` instead.
258
+
259
+ A model's load strategy is now `'instant'` or `'lazy'`, and nothing else. The
260
+ word `'manual'` is gone, along with the `mutable.manual(...)` helper that set
261
+ it.
262
+
263
+ The strategy had drifted into two declarations that never agreed. The authoring
264
+ side, reached through `@abloatai/ablo/schema`, offered `'instant' | 'lazy' |
265
+ 'manual'`. The runtime side, reached through `@abloatai/ablo/core` under the
266
+ same name, offered `instant`, `lazy`, `partial`, `explicitlyRequested`, and
267
+ `local`. Importing `LoadStrategy` from the two paths gave you two different
268
+ types, and no compiler anywhere would tell you which one you had.
269
+
270
+ Only `instant` and `lazy` were ever real. `'manual'` promised loading a model
271
+ "only when you explicitly call `sync.model.load()`", a method that was never
272
+ built; a model declared manual resolved to `lazy` and loaded on first access
273
+ like any other. `partial` had live branches in the client but no way to declare
274
+ it, and `explicitlyRequested` and `local` had neither. So the correction is
275
+ mostly the removal of vocabulary that described behavior the engine did not
276
+ have — a model that said `manual` was already behaving as `lazy`, and continues
277
+ to, which is why this changes what your schema is allowed to say rather than
278
+ what your program does.
279
+
280
+ Replace `load: 'manual'` with `load: 'lazy'`, and `mutable.manual(...)` with
281
+ `mutable.lazy(...)`. `readOnly.internal(...)` stays, and its guarantee is
282
+ unchanged and worth restating plainly: it is a write boundary, not a read one.
283
+ Clients cannot commit to an internal model, and the server rejects any attempt.
284
+ Its rows still reach a client that reads the model.
285
+
286
+ `LoadStrategy` now has one declaration behind both import paths. It reads as a
287
+ value where the engine branches on it — `LoadStrategy.instant` — and as the
288
+ plain string an author types in `model(…, { load: 'instant' })`, and the two
289
+ can no longer name different sets.
290
+
291
+ Two names join it on `@abloatai/ablo/schema`. `DEFAULT_LOAD_STRATEGY` is the
292
+ strategy a model gets when it declares none, and `loadsAtBootstrap(load)`
293
+ answers whether a model's rows arrive in the bootstrap payload or on first
294
+ access. The predicate is exported because both halves of the system have to
295
+ agree on it: the client builds its bootstrap subscription from the schema and
296
+ the server assembles the payload from the same schema, and they had been
297
+ asking the question in three different spellings — `load !== 'lazy'` in one
298
+ place, `load === 'instant'` in another. Against two members those agree. A
299
+ third member would have had one side enrolling it and the other withholding it,
300
+ which surfaces as rows that never arrive, a long way from the line responsible.
301
+
302
+ - The three synchronous reads on `ablo.<model>` are renamed. `get(id)`, `getAll(options)`,
303
+ and `getCount(options)` become `local.retrieve(id)`, `local.list(options)`, and
304
+ `local.count(options)`.
305
+
306
+ The old names were a pair that carried no information. `get` and `retrieve` are
307
+ synonyms in English, so a reader who found `ablo.tasks.get(id)` beside
308
+ `ablo.tasks.retrieve({ id })` had no way to tell from the words which one waited
309
+ on the network and which one did not — the difference had to be memorised, and
310
+ it was memorised imperfectly. Two bugs reached review that way, both of them a
311
+ synchronous read standing in for an asynchronous one against a graph that had
312
+ not warmed up yet, both of them invisible in the diff because the line read
313
+ exactly like the call it should have been.
314
+
315
+ The new names say the thing out loud. Every verb is the verb it already was on
316
+ the asynchronous side; the only addition anywhere in the design is the word
317
+ `local`, and it is a narrowing rather than a claim about its sibling.
318
+ `retrieve` consults the local graph first and falls back to the network;
319
+ `local.retrieve` is restricted to what is already resident, which is also why it
320
+ can hand back a value instead of a promise. There is nothing to await. Reading
321
+ `ablo.tasks.local.list({ where })`, you know both what it does and what it costs
322
+ without leaving the line.
323
+
324
+ The reads themselves are unchanged — same options, same lifecycle `state`
325
+ filter, same reactivity inside `useAblo` selectors and in render. Only the path
326
+ to them moved, and it is the only path: the local reads are not also exposed at
327
+ the top level, because a namespace that duplicates what sits beside it stops
328
+ disambiguating anything.
329
+
330
+ Underneath, the rename arrives with a structural change that will keep the
331
+ surface honest from here on. The reactive `ablo.<model>` type was hand-written as
332
+ a second list that happened to agree with the transport-independent one, and
333
+ nothing linked them: a verb added to one said nothing about the other. It is now
334
+ composed from that base rather than restated, so a verb added there arrives on
335
+ the reactive client on its own. `track` — registering a durable premise on a
336
+ row — is the first to arrive that way.
337
+
338
+ Migrating is mechanical, but reach for the compiler rather than a search. `getAll`
339
+ and `getCount` are distinctive enough to find by name; `get` is not, and in a
340
+ codebase of any size it hides among the `Map.get` and `headers.get` calls that a
341
+ search cannot tell it from. Removing the old names from the type turns the
342
+ question into a list of type errors, each one a site that genuinely needs to
343
+ move.
344
+
345
+ A capability is declared in the vocabulary you write, and every other spelling
346
+ of it derives from that declaration.
347
+
348
+ ```ts
349
+ import { grantedOperations, modelWireNames } from '@abloatai/ablo/auth';
350
+
351
+ const operations = grantedOperations(
352
+ { documents: ['read', 'update'] },
353
+ modelWireNames(schema.models)
354
+ );
355
+ ```
356
+
357
+ The same grant used to be spelled five times: a literal union in the resource
358
+ types, a `z.array(z.string())` on the wire, a field-by-field parser in the mint
359
+ route, an object literal in the response type, and a hand-written `model.verb`
360
+ array at every caller that mints without the SDK. Nothing failed when those
361
+ drifted apart. The drift surfaced later as `capability_scope_denied` on a grant
362
+ the caller was certain it held, at a call site with no view of the spelling that
363
+ had gone stale.
364
+
365
+ They are now one module on `@abloatai/ablo/auth`. `capabilityOperationSchema`
366
+ holds the verb vocabulary; `grantedOperationSchema` derives the wire pattern
367
+ `<model>.<verb>` from it as a template literal, so tightening the verbs cannot
368
+ leave a stale regex or a stale published contract behind. `CapabilityCan<S>`
369
+ narrows the declaration to one schema's models, so `can: { tasks: ['update'] }`
370
+ fails to compile against a schema with no `tasks` model.
371
+
372
+ `modelWireNames` is the derivation worth knowing about, because it is the one
373
+ people got wrong by hand. A model whose type name is overridden — schema key
374
+ `documents`, type name `Document` — must be granted as `document.update`, and a
375
+ caller who works that out by eye learns otherwise at enforcement time. Pass your
376
+ schema's models and the map is built for you. It is a required argument rather
377
+ than an optional one: an omitted map would mint the schema key verbatim, which
378
+ is right for most models and wrong for exactly the ones that need it.
379
+
380
+ Two more derivations ride along. `expandReadYourWrites` appends `<model>.read`
381
+ for every model the grant can write, because a scoped agent that may update a
382
+ row has to be able to read it or the read gate starves the writes the grant
383
+ allows — write verbs stay the source of truth, reads are derived and deduped,
384
+ and a model the grant cannot write stays unreadable. And a grant is now checked
385
+ against the schema **at the mint**: `capabilityModelAliases` accepts any of the
386
+ three names one model answers to (type name, schema key, table name), and
387
+ `unresolvableOperations` reports the ones that name nothing, so a typo is a
388
+ rejected mint rather than a credential that looks healthy until its first write.
389
+
390
+ ### Breaking
391
+
392
+ `ExchangeApiKeyRequest.operations` narrows from `readonly string[]` to
393
+ `readonly GrantedOperation[]`. If you assembled that array by hand, the compiler
394
+ will now reject any entry that is not `<model>.<verb>` — including the `'*'`
395
+ wildcard, which was never a member of the wire form and is no longer typable as
396
+ one. Build the array with `grantedOperations(can, modelWireNames(schema.models))`
397
+ instead of composing the strings; that is the same call the SDK makes, and it
398
+ resolves the type-name override you would otherwise have to remember.
399
+
400
+ Three error codes carried the word "intent", left over from the vocabulary the
401
+ coordination layer used before it settled on claims. The word had come to mean
402
+ two unrelated things in the same registry, so both are now named for what they
403
+ are.
404
+
405
+ `invalid_intent` is removed. It was raised when a claim request failed to name
406
+ the model and row it targeted — which is what `malformed_claim` already
407
+ described, in the same words, for the same failure arriving over the WebSocket
408
+ instead of over HTTP. The two have been merged into `malformed_claim`; a caller
409
+ switching on `invalid_intent` should switch on `malformed_claim` instead, and
410
+ the HTTP status is 400 either way.
411
+
412
+ `slide_intent_missing_deck_id` and `slide_intent_unknown_sibling` are renamed to
413
+ `position_missing_parent` and `position_unknown_sibling`. These were never about
414
+ claims at all: they report that a row created at a position stated relative to
415
+ its neighbours — "before this one" — could not have that position resolved,
416
+ because the parent it belongs to was not named or because the neighbour is not
417
+ in it. An earlier rename had swept the word "claim" through their descriptions
418
+ and left them reading as contention errors. The slide and the deck have gone the
419
+ same way as the intent: nothing in the rule is particular to a deck, and a
420
+ registry that every schema's callers switch on should not name one consumer's
421
+ models, so the codes and their messages now speak of a row and its parent. Both
422
+ remain `validation` errors carrying a 400.
423
+
424
+ `malformed_claim` moves from the `claim` category to `validation`, alongside
425
+ `malformed_subscription`. The code, its 400 status, and its meaning are
426
+ unchanged — only the grouping it appears under in the error documentation. The
427
+ `claim` category now holds only genuine contention, which is what its name
428
+ promised.
429
+
430
+ `model()` now takes a shape and one options object. Relations, which used to hold a
431
+ positional argument of their own in between, are the `relations` key on those options.
432
+
433
+ ```ts
434
+ documents: model(
435
+ {
436
+ title: z.string(),
437
+ projectId: z.string(),
438
+ },
439
+ {
440
+ relations: { project: relation.belongsTo('projects', 'projectId') },
441
+ conflict: coordination(humansOverwrite(), agentsReject()),
442
+ load: 'lazy',
443
+ }
444
+ ),
445
+ ```
446
+
447
+ The middle argument was the problem. Most models have no relations at all, yet nearly
448
+ every model has something to say about itself — a table it maps to, a load strategy, a
449
+ conflict disposition — and the only way to reach that third argument was to write the
450
+ second one first. So the schema filled up with empty braces standing in for edges that
451
+ did not exist. In this repository alone the placeholder appeared 104 times, which made
452
+ `{}` the single most common thing anyone ever passed in that position: more often than
453
+ any real relations object. A reader opening a schema met the empty braces before they
454
+ met the model.
455
+
456
+ Nothing about relations themselves changes. They are built with the same
457
+ `relation.belongsTo` and `relation.hasMany` factories, they carry the same options, and
458
+ the engine reads them exactly as before — to index foreign keys, to order inserts so a
459
+ parent lands before its children, and to generate the accessors behind `task.project`.
460
+ They have simply stopped being a position and become a name, which is what they always
461
+ were to everything downstream.
462
+
463
+ Migrating is mechanical, and the compiler finds every site. A call that passed an empty
464
+ placeholder drops it:
465
+
466
+ ```ts
467
+ -model({ title: z.string() }, {}, { groups: { root: 'workspace' } }) +
468
+ model({ title: z.string() }, { groups: { root: 'workspace' } });
469
+ ```
470
+
471
+ A call that passed real relations folds them in under their own key:
472
+
473
+ ```ts
474
+ - model({ title: z.string(), projectId: z.string() }, {
475
+ - project: relation.belongsTo('projects', 'projectId'),
476
+ - }, { load: 'lazy' })
477
+ + model({ title: z.string(), projectId: z.string() }, {
478
+ + relations: { project: relation.belongsTo('projects', 'projectId') },
479
+ + load: 'lazy',
480
+ + })
481
+ ```
482
+
483
+ A call that passed only fields is untouched.
484
+
485
+ `app.current_user_id` is now a reserved session setting, so a schema can no
486
+ longer claim it — and a read that cannot be scoped to the person asking says so
487
+ with `user_scope_not_enforced` instead of quietly returning rows.
488
+
489
+ Ablo's direct-write connection already applied a fixed bundle of settings before
490
+ your DML — the organization, the project, the environment, the sandbox, the
491
+ acting participant — and `sessionSettings` let a schema forward any of those
492
+ into a differently-named setting its own policies read. What a schema could
493
+ never do was reassign one of the engine's own settings, because that would let a
494
+ schema push relax the scoping under which Ablo writes. `RESERVED_SESSION_SETTINGS`
495
+ is the list that prevents it, and the engine now sets one more: the acting user.
496
+
497
+ The setting carries a person's id when a person is behind the write, and `'*'`
498
+ when the request comes from a backend credential acting as the organization
499
+ itself. Absent identity writes the empty string rather than leaving the previous
500
+ value on a pooled connection, so a policy reading it denies rather than
501
+ inheriting. Your own policies are welcome to read it; they simply cannot be the
502
+ thing that decides what it contains.
503
+
504
+ If your schema mapped `app.current_user_id` through `sessionSettings`, that entry
505
+ is now dropped at authoring time and at runtime, and the engine's own value
506
+ applies instead. Map a different name if your policies need a value you control.
507
+
508
+ The new error code covers the one arrangement where that boundary cannot hold. A
509
+ plane served from its retained log carries the organization and the project on
510
+ every row but not the owner, so a rule that names a person has nothing to act on
511
+ there. Rather than fold the rows and return a plausible answer, such a read is
512
+ declined whole — a member reading a colleague's private records would otherwise
513
+ be indistinguishable from a member reading their own. Reads made by a credential
514
+ acting for the organization are unaffected, as is every plane served from its
515
+ tables.
516
+
517
+ All of this now has a page. The seam has been in the SDK since 0.32.0 and under
518
+ this name since 0.33.0, documented both times only in a release note — which is
519
+ a poor place to keep a feature rather than announce one:
520
+ a reader with row-level-security policies had no way to discover that Ablo sets
521
+ an identity context at all, let alone that they could point their own policies
522
+ at it. **Session Settings** states what the engine sets before every write, which
523
+ identities a mapping may name, which settings are reserved and why, and what
524
+ happens on a plane served from its log. Read it with `npx ablo docs
525
+ session-settings`, or on the site.
526
+
527
+ `causedByTaskId` is removed from `MutationOptions` — and so from the per-verb params that
528
+ compose it, `ModelCreateParams`, `ModelUpdateParams`, and `ModelDeleteParams` — together with
529
+ the seven `turn_*` error codes that existed to police it. It also leaves `CommitContext`, where
530
+ the server carried it from the wire to the stored row. The field was never usable for its stated
531
+ purpose: setting it was the one reliable way to have a write refused.
532
+
533
+ It was the surviving half of an agent-turn protocol whose other half was never built. The idea
534
+ had been that an agent would open a turn, write under it, and close it, so that an auditor could
535
+ later ask what an agent did _because_ a person asked it to — a prompt at the root of a tree of
536
+ changes. What actually shipped was only the check. Every commit that carried a `causedByTaskId`
537
+ was validated against a task record, and nothing in the system has ever opened one, so the record
538
+ was never there. A caller who supplied the field had the entire batch rejected with
539
+ `turn_validation_failed`; a caller who left it null passed straight through. The prudent thing to
540
+ do with a documented, public option was to never touch it, and the field spent its life as
541
+ plumbing threaded through the wire protocol, the settlement envelope, the stored row, and the
542
+ replication consumer, carrying null on every path.
543
+
544
+ Nothing is lost by its removal, because write attribution never depended on it. A delta already
545
+ records who made the change and on whose authority — the actor, the `onBehalfOf` principal behind
546
+ a delegated write, the capability that authorized it, and the claim the write was made under. Those
547
+ answer "who did this and by what right" without asking the caller to maintain a lifecycle the
548
+ engine never opened on their behalf. If your code passed `causedByTaskId`, delete the argument;
549
+ if it branched on `turn_validation_failed`, that branch was unreachable and can go with it.
550
+
551
+ The removed codes are `turn_validation_failed`, `turn_open_failed`, `turn_close_failed`,
552
+ `turn_not_found`, `turn_foreign_agent`, `parent_turn_not_found`, and `parent_turn_foreign_agent`.
553
+ `ERROR_CONTRACT_VERSION` moves to `2026-07-19`, as it does whenever a code leaves the registry.
554
+ The stored-row schema loses its provenance slice with them: `deltaProvenanceSchema` and the
555
+ `DeltaProvenance` type are gone, and `syncDeltaRowSchema` is now the core and attribution slices
556
+ composed, which is what it had always described in practice. On the wire the field was optional
557
+ and nullable, so a client that still sends it is accepted and ignored, and a client reading deltas
558
+ sees one fewer always-null key.
559
+
560
+ The storage goes with it. The `caused_by_task_id` column leaves `sync_deltas`, `agent_actions_log`,
561
+ and `source_write_intents`, and the two tables that were never written — `agent_tasks` and
562
+ `agent_task_prompts` — are dropped along with the `agent_surface` enum that typed them. This looked
563
+ at first like a separate versioned migration, because the audit chain hashes the lineage id into
564
+ every row's signature and rewriting those signatures is not something a release should do quietly.
565
+ It turned out not to need rewriting. Every row was hashed with a null in that position, so the
566
+ canonical byte layout the stored hashes were computed against is preserved exactly by passing
567
+ `NULL::text` where the column used to be read — which is what the migration does, in the append
568
+ trigger and in the rechain function, before the column is dropped. The signature is untouched and
569
+ every historical `row_hash` still verifies. The `packages/audit-chain` reproduction freezes the same
570
+ position, so the two implementations cannot drift apart on it.
571
+
572
+ Eight names that were renamed in an earlier release, and have shipped since as
573
+ aliases beside the names that replaced them, are removed.
574
+
575
+ | Removed | Use |
576
+ | ----------------------- | ------------------------ |
577
+ | `CommitOutboxRecord` | `PendingWrite` |
578
+ | `CommitOutboxStore` | `DurableWriteStore` |
579
+ | `MeshParticipantStatus` | `ParticipantStatus` |
580
+ | `planeSchema` | `residencySchema` |
581
+ | `SchemaPlane` | `ModelResidency` |
582
+ | `DEFAULT_PLANE` | `DEFAULT_RESIDENCY` |
583
+ | `DELTA_RESIDENCY` | `DELTA_PHYSICAL_STORAGE` |
584
+ | `InferModel` | `Model` |
585
+
586
+ Each is a one-for-one substitution — the alias and its replacement were the same
587
+ type or the same value, so nothing about your program's behavior changes with
588
+ the name. `InferModel` is the one worth a sentence: `Model<typeof schema,
589
+ 'tasks'>` reads as the domain rather than the machinery, which is why it became
590
+ the published spelling, and the old name is gone from `@abloatai/ablo/schema`
591
+ and from the `Ablo.Schema` namespace both. The type it named still exists
592
+ underneath, because `Model` is defined in terms of it rather than as a second
593
+ name for the same idea.
594
+
595
+ An alias earns one release of overlap. That is what the surface snapshot in
596
+ this repository records and enforces: a published name may leave only from a
597
+ release that shipped it marked deprecated, which is the release in which a
598
+ reader could have found out. These eight were marked in 0.34.1, so this is the
599
+ release that may drop them, and dropping them is what keeps the rule a rule
600
+ rather than a preamble on a list that only grows.
601
+
602
+ `DELTA_RESIDENCY` is the one whose replacement is not merely a rename.
603
+ Residency and classification are separate axes — where a column physically
604
+ lives, and how sensitive its contents are — and one constant named as though
605
+ they were the same question. `DELTA_PHYSICAL_STORAGE` answers the first;
606
+ `DELTA_DATA_CLASSIFICATION`, which has been exported alongside it, answers the
607
+ second. If you were reading `DELTA_RESIDENCY` to decide handling rather than
608
+ placement, the classification constant is the one you wanted.
609
+
610
+ `ablo docs` reads the documentation for the version you installed, and the
611
+ public docs routes now serve the whole corpus instead of two thirds of it.
612
+
613
+ A documentation URL always describes the newest release. A package in
614
+ `node_modules` is frozen at whatever version was published. Those two facts
615
+ drift apart the moment a project pins a dependency, and the drift is invisible
616
+ from both ends: the docs are correct, the code is correct, and the agent reading
617
+ one to write the other produces a call that does not exist. `get`, `getAll`, and
618
+ `getCount` became `retrieve` and `list` in 0.35.0, so an assistant working in a
619
+ project still on 0.34 reads the current page, writes `retrieve`, and watches it
620
+ fail against the package sitting beside it.
621
+
622
+ The documentation already travelled in the npm tarball; nothing read it there.
623
+ `ablo docs` lists every page with what it covers, and `ablo docs <page>` prints
624
+ one as markdown — from the files shipped alongside the code they describe, so
625
+ they cannot disagree with it, and with no network at all, which is the condition
626
+ most agent work now runs under. `ablo docs --json` gives the same list to a
627
+ program. A new `@abloatai/ablo/docs` entry point exposes the catalog for
628
+ anything that wants to build its own reader.
629
+
630
+ The same catalog now answers `/api/docs/*`. That surface had been maintained by
631
+ hand and had fallen twenty-one pages behind: `coordination`, the page explaining
632
+ how claims work, returned a 404 while the site published it, and so did
633
+ `sessions`, `webhooks`, `migration`, `deployment`, and every example but two.
634
+ Nothing failed when the list went stale, which is why it stayed stale. Pages are
635
+ now discovered rather than listed, so one is reachable the moment it is written,
636
+ and only what ships in the package is reachable at all.
637
+
638
+ One address changed meaning. `/api/docs/agents` returns the Agents guide, the
639
+ page the docs index has always linked under that name; the install playbook that
640
+ briefly answered there is at `/api/docs/AGENTS.md`, where it also answered
641
+ before.
642
+
643
+ `GET /v1/logs` answers two questions now — what changed, and who is working on
644
+ what — through one envelope and one cursor.
645
+
646
+ ```ts
647
+ import { feedEventSchema, parseFeedCursor } from '@abloatai/ablo/wire';
648
+
649
+ const page = await fetch(`${baseUrl}/v1/logs?after=${cursor}`, {
650
+ headers: { authorization: `Bearer ${apiKey}` },
651
+ }).then((r) => r.json());
652
+
653
+ for (const event of page.data) {
654
+ const entry = feedEventSchema.parse(event);
655
+ if (entry.object === 'log_event') applyChange(entry);
656
+ }
657
+ cursor = page.next_cursor;
658
+ ```
659
+
660
+ An entry is discriminated on `object`, so a reader that knows only the arm it
661
+ came for parses the whole page and skips the rest — which is what lets a second
662
+ arm be added to a feed that callers are already following. The two arms stay
663
+ separately sequenced on purpose. A claim is a lease rather than a settled fact,
664
+ and allocating its positions from the delta sequence would put ephemeral leases
665
+ into the log that clients materialise rows from and that WAL-echo promotion and
666
+ compaction operate over, where a burst of claim churn would be indistinguishable
667
+ from committed change. They share a reading, not a sequence.
668
+
669
+ That is why the cursor carries a position for each, encoded `"<log>.<claims>"`.
670
+ A bare `"<log>"` still parses and resumes at claim position zero, which is the
671
+ right answer for every cursor issued before this existed: a caller who never
672
+ asked for claim events has no claim position to preserve. `parseFeedCursor`,
673
+ `formatFeedCursor`, and `feedCursorAdvanced` are exported from
674
+ `@abloatai/ablo/wire` alongside `feedEventSchema`, `logListResponseSchema`, and
675
+ `claimEventSchema`, so a program that builds its own reader states the grammar
676
+ once. `claimRecordSchema` and `heldClaimStatusSchema` join them on
677
+ `@abloatai/ablo/coordination` — the one claim record, and the peer-visible
678
+ projection of it.
679
+
680
+ `GET /v1/claims` gains the filters the audit log already had: `actorId`,
681
+ `actorKind`, `onBehalfOfId`, and `capabilityId`, beside the existing `model` and
682
+ `id`. The names are audit's names deliberately. Asking what an agent is doing
683
+ and asking what it did should not require two vocabularies for one idea, and now
684
+ the tense is the only thing that differs.
685
+
686
+ The claim arm has no producer yet. Claim transitions are broadcast and recorded
687
+ nowhere, so there is no sequence to read a position from, and the route emits
688
+ only `log_event`s today — with no request parameter for selecting arms, because
689
+ a knob that cannot be honoured is how a contract comes to describe a server that
690
+ does not exist. What ships here is the definition that producer will fill: the
691
+ union parses today's pages unchanged, and the cursor already carries the second
692
+ position, so a follower written now keeps working when the arm arrives.
693
+
694
+ ### The cursor fix
695
+
696
+ A malformed `after` is now an error rather than a position. The previous reader
697
+ was `parseInt(raw, 10)` with a `NaN` falling back to zero, so a truncated or
698
+ garbled cursor resumed from the beginning of the log and replayed it in full —
699
+ while looking exactly like a working follow, with the damage scaling to how long
700
+ the log had been running. Such a request is now declined with `invalid_request`,
701
+ naming the `after` parameter and how to recover. `ablo logs` carried the mirror
702
+ of the same bug: it read the cursor with `Number(...)`, which is harmless while a
703
+ cursor is a bare delta id and silently wrong the moment it has two parts —
704
+ `Number('42.10')` is `42.1`, which re-serialises as `'42.1'` and resumes eight
705
+ claim positions late. The cursor is now a string end to end, passed back exactly
706
+ as it was issued.
707
+
708
+ `legacyCompatibleCommitReceiptSchema` is removed from `@abloatai/ablo/wire`. The WebSocket client
709
+ now parses a commit acknowledgement with `commitReceiptSchema`, the canonical one, directly.
710
+
711
+ It was a compatibility decoder rather than a contract: a `z.preprocess` step that supplied fields
712
+ older servers omitted and coerced their string `lastSyncId` into a number before handing the result
713
+ to the canonical schema, which did the real work. It was careful in one respect worth recording —
714
+ it never invented a source correlation, so an old or malformed queued receipt still failed closed
715
+ rather than being promoted to something the server had not said.
716
+
717
+ The servers it decoded for predate the commit contract, and the hosted engine has not spoken that
718
+ shape in some time. Keeping the wrapper meant every receipt on the socket path went through a
719
+ normalizer for a case that no longer arrives, and it left two spellings of "a receipt" on a
720
+ published surface whose entire purpose is to have one.
721
+
722
+ This matters only if you run a self-hosted engine older than the commit contract: its
723
+ acknowledgements will now fail to parse, and the write is reported as `commit_no_result` — pending
724
+ and safe to retry — rather than being silently normalized. Upgrade the engine. If you imported the
725
+ schema to parse receipts yourself, `commitReceiptSchema` is the replacement and always was the one
726
+ doing the parsing.
727
+
728
+ ### Patch Changes
729
+
730
+ `ablo login`, `ablo mode`, and `ablo status` now say what the key in your hand
731
+ can do, and `status` labels the plane your credential reaches as `acts on`
732
+ rather than `env`.
733
+
734
+ Logging in provisions a pair — a secret sandbox key and a restricted production
735
+ one — so that a stolen CLI config cannot deploy to production. That protection
736
+ was deliberate and it was also silent. `login` confirmed the sandbox and moved
737
+ on, `mode production` printed a checkmark, and `status` showed the production
738
+ key as twelve characters of prefix. Nothing said the key could not push, so the
739
+ first notice was a 403 in the middle of a deploy, at the moment it cost the
740
+ most to learn.
741
+
742
+ Each of those three commands now names the capability at the point it hands you
743
+ the credential: the key rows in `status` carry what each key does beside how
744
+ long it lasts, the line under `push` says which secret key deploys, and
745
+ `ablo status --json` reports `effectiveKey.kind` so a pipeline can check before
746
+ it pushes instead of after it fails. The wording has one definition site, so the
747
+ command that stores a key, the command that switches to it, and the command
748
+ that reports it cannot describe it three different ways.
749
+
750
+ The renamed `status` line is the same fix in smaller form. `mode` and `env`
751
+ printed as peers and were not: one is the environment you selected, the other is
752
+ the one your credential actually reaches, and when they diverged neither word
753
+ said which was which. `acts on` states the fact. The `--json` output is
754
+ unchanged apart from the added `kind`.
755
+
3
756
  ## 0.34.1
4
757
 
5
758
  ### Patch Changes
@@ -16,7 +769,7 @@ A new page, **Operating on Your Database**, sets out the safety model for workin
16
769
 
17
770
  A long-running actor has a stale-context problem the per-commit read gate never reaches. The `reads` guard is a premise for the commit in hand: you declare what you looked at, the server checks it at commit, and the premise is gone. That fits an actor that reads and writes in one breath, not one that reads a row, works for minutes — an LLM call, a fetch, a human's turn — and only then writes. By the time it commits, the premise it would have declared is already old, and there was no commit in between on which to hear that the ground had shifted. This release adds `track`, the durable half of the same idea. `ablo.<model>.track({ id })` registers a read-dependency that persists on the server; the next time you commit anything, a change that landed on the tracked row since you registered rides back on the receipt's `notifications` — the same `StaleNotification` an `onStale: 'notify'` premise hands you, arriving on the write you were going to make anyway. You can also register one as part of a write, `track: [{ group: 'deck:abc' }]` alongside the batch, the standing-subscription companion to the single-commit `reads`. A track is idempotent — registering the same target again refreshes the one subscription rather than stacking duplicates — it re-baselines after it fires so a given change notifies once, and it never notifies you of your own writes, since the signal is about what others did. Delivery is on your next commit's receipt; a track does not yet push out of band between commits, so it sharpens the write-time freshness check rather than replacing a live subscription.
18
771
 
19
- The model-level presence verb is renamed from `watch` to `join`. It read like a data subscription but delivered presence — who else is on a set of rows and what they hold — so it now says what it does. `ablo.<model>.join(ids, { ttl })` opens the participant handle, with `.peers`, the scoped claim stream, and `await using` disposal unchanged; the handle's `status` was already `'joined'` and the layer beneath always called itself join, so the verb now matches the thing it returns. `onChange` remains the way to hear a row's *values* change, and `track` is the durable read-dependency for actors — three distinct jobs that the one overloaded `watch` used to blur. The React hook follows: `useWatch` becomes `useJoin`, the `WatchOptions` / `UseWatchOptions` / `UseWatchReturn` types become `JoinOptions` / `UseJoinOptions` / `UseJoinReturn`, and the error code `model_watch_not_configured` is now `model_join_not_configured`. There is no compatibility alias — rename the call sites and the type imports. The migration guide carries the mechanical diff.
772
+ The model-level presence verb is renamed from `watch` to `join`. It read like a data subscription but delivered presence — who else is on a set of rows and what they hold — so it now says what it does. `ablo.<model>.join(ids, { ttl })` opens the participant handle, with `.peers`, the scoped claim stream, and `await using` disposal unchanged; the handle's `status` was already `'joined'` and the layer beneath always called itself join, so the verb now matches the thing it returns. `onChange` remains the way to hear a row's _values_ change, and `track` is the durable read-dependency for actors — three distinct jobs that the one overloaded `watch` used to blur. The React hook follows: `useWatch` becomes `useJoin`, the `WatchOptions` / `UseWatchOptions` / `UseWatchReturn` types become `JoinOptions` / `UseJoinOptions` / `UseJoinReturn`, and the error code `model_watch_not_configured` is now `model_join_not_configured`. There is no compatibility alias — rename the call sites and the type imports. The migration guide carries the mechanical diff.
20
773
 
21
774
  ### Patch Changes
22
775
 
@@ -30,9 +783,9 @@ The declarative seam introduced in 0.32.0 for carrying tenant identity into your
30
783
 
31
784
  ```ts
32
785
  defineSchema({
33
- models: { document: { /* … */ } },
786
+ models: { document: {/* … */} },
34
787
  sessionSettings: { 'app.current_org': 'orgId' },
35
- })
788
+ });
36
789
  ```
37
790
 
38
791
  The setting name is the key, so a setting takes exactly one source and a duplicate is unrepresentable rather than something to validate away. If you adopted `tenantContext` in 0.32.0, rename it to `sessionSettings` and turn each `{ guc: 'app.current_org', from: 'orgId' }` into `'app.current_org': 'orgId'`; the exported names followed the rename — `TenantContextMapping` and `TenantContextSource` became `SessionSettings` and `SessionSettingSource`, and `RESERVED_TENANT_CONTEXT_GUCS` became `RESERVED_SESSION_SETTINGS`. The meaning is unchanged: Ablo fills only settings it resolves from your authenticated identity, never from client-supplied data, so a mapping can forward the tenant Ablo already trusts but can never widen a writer's scope, and settings the engine reserves for itself — `row_security`, the timeouts — are still refused at definition time. Schemas pushed before the rename keep parsing.
@@ -59,9 +812,9 @@ The other half of holding one key is trusting that Ablo's writer stays inside th
59
812
 
60
813
  ```ts
61
814
  defineSchema({
62
- models: { document: { /* … */ } },
815
+ models: { document: {/* … */} },
63
816
  tenantContext: [{ guc: 'app.current_org', from: 'orgId' }],
64
- })
817
+ });
65
818
  ```
66
819
 
67
820
  The `from` side is a closed set — `orgId`, `projectId`, `environment`, and the other identifiers Ablo establishes for the write from your credential. It is deliberately not free-form: a mapping can only pass through a value Ablo has already authenticated, never let a caller name its own tenant, so it can narrow what the writer sees but never widen it. Settings Ablo reserves for itself are refused at definition time. Your policies stay the sole authority on what the writer may touch; this only gives them the context they were written to read.