@abloatai/transaction 0.37.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 (723) hide show
  1. package/CONVENTIONS.md +83 -0
  2. package/README.md +48 -0
  3. package/dist/ablo.d.ts +90 -0
  4. package/dist/ablo.d.ts.map +1 -0
  5. package/dist/ablo.js +34 -0
  6. package/dist/ablo.js.map +1 -0
  7. package/dist/ai-sdk/coordinatedTool.d.ts +123 -0
  8. package/dist/ai-sdk/coordinatedTool.d.ts.map +1 -0
  9. package/dist/ai-sdk/coordinatedTool.js +135 -0
  10. package/dist/ai-sdk/coordinatedTool.js.map +1 -0
  11. package/dist/ai-sdk/index.d.ts +2 -0
  12. package/dist/ai-sdk/index.d.ts.map +1 -0
  13. package/dist/ai-sdk/index.js +2 -0
  14. package/dist/ai-sdk/index.js.map +1 -0
  15. package/dist/auth/apiKey.d.ts +154 -0
  16. package/dist/auth/apiKey.d.ts.map +1 -0
  17. package/dist/auth/apiKey.js +408 -0
  18. package/dist/auth/apiKey.js.map +1 -0
  19. package/dist/auth/bootstrapScope.d.ts +16 -0
  20. package/dist/auth/bootstrapScope.d.ts.map +1 -0
  21. package/dist/auth/bootstrapScope.js +2 -0
  22. package/dist/auth/bootstrapScope.js.map +1 -0
  23. package/dist/auth/browserCredentialSafety.d.ts +7 -0
  24. package/dist/auth/browserCredentialSafety.d.ts.map +1 -0
  25. package/dist/auth/browserCredentialSafety.js +29 -0
  26. package/dist/auth/browserCredentialSafety.js.map +1 -0
  27. package/dist/auth/capability.d.ts +241 -0
  28. package/dist/auth/capability.d.ts.map +1 -0
  29. package/dist/auth/capability.js +253 -0
  30. package/dist/auth/capability.js.map +1 -0
  31. package/dist/auth/capabilityLifecycle.d.ts +47 -0
  32. package/dist/auth/capabilityLifecycle.d.ts.map +1 -0
  33. package/dist/auth/capabilityLifecycle.js +108 -0
  34. package/dist/auth/capabilityLifecycle.js.map +1 -0
  35. package/dist/auth/credentialEndpoint.d.ts +10 -0
  36. package/dist/auth/credentialEndpoint.d.ts.map +1 -0
  37. package/dist/auth/credentialEndpoint.js +58 -0
  38. package/dist/auth/credentialEndpoint.js.map +1 -0
  39. package/dist/auth/credentialEndpointProtocol.d.ts +24 -0
  40. package/dist/auth/credentialEndpointProtocol.d.ts.map +1 -0
  41. package/dist/auth/credentialEndpointProtocol.js +34 -0
  42. package/dist/auth/credentialEndpointProtocol.js.map +1 -0
  43. package/dist/auth/credentialKind.d.ts +4 -0
  44. package/dist/auth/credentialKind.d.ts.map +1 -0
  45. package/dist/auth/credentialKind.js +15 -0
  46. package/dist/auth/credentialKind.js.map +1 -0
  47. package/dist/auth/credentialPolicy.d.ts +141 -0
  48. package/dist/auth/credentialPolicy.d.ts.map +1 -0
  49. package/dist/auth/credentialPolicy.js +113 -0
  50. package/dist/auth/credentialPolicy.js.map +1 -0
  51. package/dist/auth/credentialResult.d.ts +7 -0
  52. package/dist/auth/credentialResult.d.ts.map +1 -0
  53. package/dist/auth/credentialResult.js +9 -0
  54. package/dist/auth/credentialResult.js.map +1 -0
  55. package/dist/auth/credentialSource.d.ts +31 -0
  56. package/dist/auth/credentialSource.d.ts.map +1 -0
  57. package/dist/auth/credentialSource.js +56 -0
  58. package/dist/auth/credentialSource.js.map +1 -0
  59. package/dist/auth/hostedEndpoints.d.ts +22 -0
  60. package/dist/auth/hostedEndpoints.d.ts.map +1 -0
  61. package/dist/auth/hostedEndpoints.js +22 -0
  62. package/dist/auth/hostedEndpoints.js.map +1 -0
  63. package/dist/auth/identity.d.ts +62 -0
  64. package/dist/auth/identity.d.ts.map +1 -0
  65. package/dist/auth/identity.js +224 -0
  66. package/dist/auth/identity.js.map +1 -0
  67. package/dist/auth/index.d.ts +189 -0
  68. package/dist/auth/index.d.ts.map +1 -0
  69. package/dist/auth/index.js +312 -0
  70. package/dist/auth/index.js.map +1 -0
  71. package/dist/auth/schemas.d.ts +41 -0
  72. package/dist/auth/schemas.d.ts.map +1 -0
  73. package/dist/auth/schemas.js +69 -0
  74. package/dist/auth/schemas.js.map +1 -0
  75. package/dist/auth/sessionMint.d.ts +29 -0
  76. package/dist/auth/sessionMint.d.ts.map +1 -0
  77. package/dist/auth/sessionMint.js +92 -0
  78. package/dist/auth/sessionMint.js.map +1 -0
  79. package/dist/auth/token.d.ts +4 -0
  80. package/dist/auth/token.d.ts.map +1 -0
  81. package/dist/auth/token.js +4 -0
  82. package/dist/auth/token.js.map +1 -0
  83. package/dist/batching/index.d.ts +56 -0
  84. package/dist/batching/index.d.ts.map +1 -0
  85. package/dist/batching/index.js +148 -0
  86. package/dist/batching/index.js.map +1 -0
  87. package/dist/coordination/awaitClaimGrant.d.ts +57 -0
  88. package/dist/coordination/awaitClaimGrant.d.ts.map +1 -0
  89. package/dist/coordination/awaitClaimGrant.js +138 -0
  90. package/dist/coordination/awaitClaimGrant.js.map +1 -0
  91. package/dist/coordination/claimHeartbeatLoop.d.ts +85 -0
  92. package/dist/coordination/claimHeartbeatLoop.d.ts.map +1 -0
  93. package/dist/coordination/claimHeartbeatLoop.js +109 -0
  94. package/dist/coordination/claimHeartbeatLoop.js.map +1 -0
  95. package/dist/coordination/claimMeta.d.ts +50 -0
  96. package/dist/coordination/claimMeta.d.ts.map +1 -0
  97. package/dist/coordination/claimMeta.js +53 -0
  98. package/dist/coordination/claimMeta.js.map +1 -0
  99. package/dist/coordination/events.d.ts +75 -0
  100. package/dist/coordination/events.d.ts.map +1 -0
  101. package/dist/coordination/events.js +8 -0
  102. package/dist/coordination/events.js.map +1 -0
  103. package/dist/coordination/index.d.ts +20 -0
  104. package/dist/coordination/index.d.ts.map +1 -0
  105. package/dist/coordination/index.js +46 -0
  106. package/dist/coordination/index.js.map +1 -0
  107. package/dist/coordination/locator.d.ts +106 -0
  108. package/dist/coordination/locator.d.ts.map +1 -0
  109. package/dist/coordination/locator.js +110 -0
  110. package/dist/coordination/locator.js.map +1 -0
  111. package/dist/coordination/schema.d.ts +1332 -0
  112. package/dist/coordination/schema.d.ts.map +1 -0
  113. package/dist/coordination/schema.js +1137 -0
  114. package/dist/coordination/schema.js.map +1 -0
  115. package/dist/coordination/targetConflict.d.ts +3 -0
  116. package/dist/coordination/targetConflict.d.ts.map +1 -0
  117. package/dist/coordination/targetConflict.js +74 -0
  118. package/dist/coordination/targetConflict.js.map +1 -0
  119. package/dist/coordination/trace.d.ts +79 -0
  120. package/dist/coordination/trace.d.ts.map +1 -0
  121. package/dist/coordination/trace.js +139 -0
  122. package/dist/coordination/trace.js.map +1 -0
  123. package/dist/docs/catalog.d.ts +73 -0
  124. package/dist/docs/catalog.d.ts.map +1 -0
  125. package/dist/docs/catalog.js +231 -0
  126. package/dist/docs/catalog.js.map +1 -0
  127. package/dist/docs/index.d.ts +11 -0
  128. package/dist/docs/index.d.ts.map +1 -0
  129. package/dist/docs/index.js +11 -0
  130. package/dist/docs/index.js.map +1 -0
  131. package/dist/durableWrites.d.ts +63 -0
  132. package/dist/durableWrites.d.ts.map +1 -0
  133. package/dist/durableWrites.js +72 -0
  134. package/dist/durableWrites.js.map +1 -0
  135. package/dist/environment.d.ts +106 -0
  136. package/dist/environment.d.ts.map +1 -0
  137. package/dist/environment.js +109 -0
  138. package/dist/environment.js.map +1 -0
  139. package/dist/errorCodes.d.ts +411 -0
  140. package/dist/errorCodes.d.ts.map +1 -0
  141. package/dist/errorCodes.js +500 -0
  142. package/dist/errorCodes.js.map +1 -0
  143. package/dist/errors.d.ts +429 -0
  144. package/dist/errors.d.ts.map +1 -0
  145. package/dist/errors.js +687 -0
  146. package/dist/errors.js.map +1 -0
  147. package/dist/footprint.d.ts +112 -0
  148. package/dist/footprint.d.ts.map +1 -0
  149. package/dist/footprint.js +0 -0
  150. package/dist/footprint.js.map +1 -0
  151. package/dist/headlessClient.d.ts +10 -0
  152. package/dist/headlessClient.d.ts.map +1 -0
  153. package/dist/headlessClient.js +114 -0
  154. package/dist/headlessClient.js.map +1 -0
  155. package/dist/index.d.ts +21 -0
  156. package/dist/index.d.ts.map +1 -0
  157. package/dist/index.js +21 -0
  158. package/dist/index.js.map +1 -0
  159. package/dist/keys/index.d.ts +88 -0
  160. package/dist/keys/index.d.ts.map +1 -0
  161. package/dist/keys/index.js +208 -0
  162. package/dist/keys/index.js.map +1 -0
  163. package/dist/log/syncDeltaRow.d.ts +159 -0
  164. package/dist/log/syncDeltaRow.d.ts.map +1 -0
  165. package/dist/log/syncDeltaRow.js +96 -0
  166. package/dist/log/syncDeltaRow.js.map +1 -0
  167. package/dist/logger.d.ts +17 -0
  168. package/dist/logger.d.ts.map +1 -0
  169. package/dist/logger.js +8 -0
  170. package/dist/logger.js.map +1 -0
  171. package/dist/observability.d.ts +54 -0
  172. package/dist/observability.d.ts.map +1 -0
  173. package/dist/observability.js +20 -0
  174. package/dist/observability.js.map +1 -0
  175. package/dist/policy/types.d.ts +218 -0
  176. package/dist/policy/types.d.ts.map +1 -0
  177. package/dist/policy/types.js +127 -0
  178. package/dist/policy/types.js.map +1 -0
  179. package/dist/resources/functionalUpdate.d.ts +80 -0
  180. package/dist/resources/functionalUpdate.d.ts.map +1 -0
  181. package/dist/resources/functionalUpdate.js +88 -0
  182. package/dist/resources/functionalUpdate.js.map +1 -0
  183. package/dist/resources/httpResources.d.ts +449 -0
  184. package/dist/resources/httpResources.d.ts.map +1 -0
  185. package/dist/resources/httpResources.js +8 -0
  186. package/dist/resources/httpResources.js.map +1 -0
  187. package/dist/resources/modelOperations.d.ts +380 -0
  188. package/dist/resources/modelOperations.d.ts.map +1 -0
  189. package/dist/resources/modelOperations.js +13 -0
  190. package/dist/resources/modelOperations.js.map +1 -0
  191. package/dist/resources/mutationOptions.d.ts +67 -0
  192. package/dist/resources/mutationOptions.d.ts.map +1 -0
  193. package/dist/resources/mutationOptions.js +10 -0
  194. package/dist/resources/mutationOptions.js.map +1 -0
  195. package/dist/resources/where.d.ts +102 -0
  196. package/dist/resources/where.d.ts.map +1 -0
  197. package/dist/resources/where.js +116 -0
  198. package/dist/resources/where.js.map +1 -0
  199. package/dist/resources/writeOptionsSchema.d.ts +48 -0
  200. package/dist/resources/writeOptionsSchema.d.ts.map +1 -0
  201. package/dist/resources/writeOptionsSchema.js +74 -0
  202. package/dist/resources/writeOptionsSchema.js.map +1 -0
  203. package/dist/schema/coordination.d.ts +113 -0
  204. package/dist/schema/coordination.d.ts.map +1 -0
  205. package/dist/schema/coordination.js +134 -0
  206. package/dist/schema/coordination.js.map +1 -0
  207. package/dist/schema/ddl.d.ts +98 -0
  208. package/dist/schema/ddl.d.ts.map +1 -0
  209. package/dist/schema/ddl.js +492 -0
  210. package/dist/schema/ddl.js.map +1 -0
  211. package/dist/schema/ddlLock.d.ts +36 -0
  212. package/dist/schema/ddlLock.d.ts.map +1 -0
  213. package/dist/schema/ddlLock.js +47 -0
  214. package/dist/schema/ddlLock.js.map +1 -0
  215. package/dist/schema/diff.d.ts +226 -0
  216. package/dist/schema/diff.d.ts.map +1 -0
  217. package/dist/schema/diff.js +290 -0
  218. package/dist/schema/diff.js.map +1 -0
  219. package/dist/schema/field.d.ts +121 -0
  220. package/dist/schema/field.d.ts.map +1 -0
  221. package/dist/schema/field.js +266 -0
  222. package/dist/schema/field.js.map +1 -0
  223. package/dist/schema/fieldRef.d.ts +58 -0
  224. package/dist/schema/fieldRef.d.ts.map +1 -0
  225. package/dist/schema/fieldRef.js +26 -0
  226. package/dist/schema/fieldRef.js.map +1 -0
  227. package/dist/schema/generate.d.ts +20 -0
  228. package/dist/schema/generate.d.ts.map +1 -0
  229. package/dist/schema/generate.js +87 -0
  230. package/dist/schema/generate.js.map +1 -0
  231. package/dist/schema/index.d.ts +43 -0
  232. package/dist/schema/index.d.ts.map +1 -0
  233. package/dist/schema/index.js +81 -0
  234. package/dist/schema/index.js.map +1 -0
  235. package/dist/schema/loadStrategy.d.ts +46 -0
  236. package/dist/schema/loadStrategy.d.ts.map +1 -0
  237. package/dist/schema/loadStrategy.js +47 -0
  238. package/dist/schema/loadStrategy.js.map +1 -0
  239. package/dist/schema/model.d.ts +380 -0
  240. package/dist/schema/model.d.ts.map +1 -0
  241. package/dist/schema/model.js +124 -0
  242. package/dist/schema/model.js.map +1 -0
  243. package/dist/schema/openapi.d.ts +59 -0
  244. package/dist/schema/openapi.d.ts.map +1 -0
  245. package/dist/schema/openapi.js +508 -0
  246. package/dist/schema/openapi.js.map +1 -0
  247. package/dist/schema/queries.d.ts +202 -0
  248. package/dist/schema/queries.d.ts.map +1 -0
  249. package/dist/schema/queries.js +144 -0
  250. package/dist/schema/queries.js.map +1 -0
  251. package/dist/schema/relation.d.ts +205 -0
  252. package/dist/schema/relation.d.ts.map +1 -0
  253. package/dist/schema/relation.js +105 -0
  254. package/dist/schema/relation.js.map +1 -0
  255. package/dist/schema/residency.d.ts +30 -0
  256. package/dist/schema/residency.d.ts.map +1 -0
  257. package/dist/schema/residency.js +26 -0
  258. package/dist/schema/residency.js.map +1 -0
  259. package/dist/schema/roles.d.ts +250 -0
  260. package/dist/schema/roles.d.ts.map +1 -0
  261. package/dist/schema/roles.js +231 -0
  262. package/dist/schema/roles.js.map +1 -0
  263. package/dist/schema/schema.d.ts +352 -0
  264. package/dist/schema/schema.d.ts.map +1 -0
  265. package/dist/schema/schema.js +326 -0
  266. package/dist/schema/schema.js.map +1 -0
  267. package/dist/schema/select.d.ts +41 -0
  268. package/dist/schema/select.d.ts.map +1 -0
  269. package/dist/schema/select.js +91 -0
  270. package/dist/schema/select.js.map +1 -0
  271. package/dist/schema/serialize.d.ts +116 -0
  272. package/dist/schema/serialize.d.ts.map +1 -0
  273. package/dist/schema/serialize.js +278 -0
  274. package/dist/schema/serialize.js.map +1 -0
  275. package/dist/schema/sugar.d.ts +110 -0
  276. package/dist/schema/sugar.d.ts.map +1 -0
  277. package/dist/schema/sugar.js +84 -0
  278. package/dist/schema/sugar.js.map +1 -0
  279. package/dist/schema/tenancy.d.ts +140 -0
  280. package/dist/schema/tenancy.d.ts.map +1 -0
  281. package/dist/schema/tenancy.js +191 -0
  282. package/dist/schema/tenancy.js.map +1 -0
  283. package/dist/server/adapter.d.ts +174 -0
  284. package/dist/server/adapter.d.ts.map +1 -0
  285. package/dist/server/adapter.js +19 -0
  286. package/dist/server/adapter.js.map +1 -0
  287. package/dist/server/commit.d.ts +108 -0
  288. package/dist/server/commit.d.ts.map +1 -0
  289. package/dist/server/commit.js +2 -0
  290. package/dist/server/commit.js.map +1 -0
  291. package/dist/server/index.d.ts +15 -0
  292. package/dist/server/index.d.ts.map +1 -0
  293. package/dist/server/index.js +3 -0
  294. package/dist/server/index.js.map +1 -0
  295. package/dist/server/readConfig.d.ts +81 -0
  296. package/dist/server/readConfig.d.ts.map +1 -0
  297. package/dist/server/readConfig.js +9 -0
  298. package/dist/server/readConfig.js.map +1 -0
  299. package/dist/server/storageMode.d.ts +24 -0
  300. package/dist/server/storageMode.d.ts.map +1 -0
  301. package/dist/server/storageMode.js +18 -0
  302. package/dist/server/storageMode.js.map +1 -0
  303. package/dist/source/adapter.d.ts +84 -0
  304. package/dist/source/adapter.d.ts.map +1 -0
  305. package/dist/source/adapter.js +25 -0
  306. package/dist/source/adapter.js.map +1 -0
  307. package/dist/source/adapters/drizzle.d.ts +49 -0
  308. package/dist/source/adapters/drizzle.d.ts.map +1 -0
  309. package/dist/source/adapters/drizzle.js +220 -0
  310. package/dist/source/adapters/drizzle.js.map +1 -0
  311. package/dist/source/adapters/kysely.d.ts +43 -0
  312. package/dist/source/adapters/kysely.d.ts.map +1 -0
  313. package/dist/source/adapters/kysely.js +206 -0
  314. package/dist/source/adapters/kysely.js.map +1 -0
  315. package/dist/source/adapters/kyselyMutationCore.d.ts +77 -0
  316. package/dist/source/adapters/kyselyMutationCore.d.ts.map +1 -0
  317. package/dist/source/adapters/kyselyMutationCore.js +126 -0
  318. package/dist/source/adapters/kyselyMutationCore.js.map +1 -0
  319. package/dist/source/adapters/memory.d.ts +14 -0
  320. package/dist/source/adapters/memory.d.ts.map +1 -0
  321. package/dist/source/adapters/memory.js +131 -0
  322. package/dist/source/adapters/memory.js.map +1 -0
  323. package/dist/source/adapters/prisma.d.ts +64 -0
  324. package/dist/source/adapters/prisma.d.ts.map +1 -0
  325. package/dist/source/adapters/prisma.js +203 -0
  326. package/dist/source/adapters/prisma.js.map +1 -0
  327. package/dist/source/conformance.d.ts +38 -0
  328. package/dist/source/conformance.d.ts.map +1 -0
  329. package/dist/source/conformance.js +216 -0
  330. package/dist/source/conformance.js.map +1 -0
  331. package/dist/source/connector.d.ts +96 -0
  332. package/dist/source/connector.d.ts.map +1 -0
  333. package/dist/source/connector.js +267 -0
  334. package/dist/source/connector.js.map +1 -0
  335. package/dist/source/connectorProtocol.d.ts +155 -0
  336. package/dist/source/connectorProtocol.d.ts.map +1 -0
  337. package/dist/source/connectorProtocol.js +164 -0
  338. package/dist/source/connectorProtocol.js.map +1 -0
  339. package/dist/source/contract.d.ts +196 -0
  340. package/dist/source/contract.d.ts.map +1 -0
  341. package/dist/source/contract.js +165 -0
  342. package/dist/source/contract.js.map +1 -0
  343. package/dist/source/drizzle.d.ts +2 -0
  344. package/dist/source/drizzle.d.ts.map +1 -0
  345. package/dist/source/drizzle.js +2 -0
  346. package/dist/source/drizzle.js.map +1 -0
  347. package/dist/source/factory.d.ts +93 -0
  348. package/dist/source/factory.d.ts.map +1 -0
  349. package/dist/source/factory.js +287 -0
  350. package/dist/source/factory.js.map +1 -0
  351. package/dist/source/idempotency.d.ts +62 -0
  352. package/dist/source/idempotency.d.ts.map +1 -0
  353. package/dist/source/idempotency.js +145 -0
  354. package/dist/source/idempotency.js.map +1 -0
  355. package/dist/source/index.d.ts +24 -0
  356. package/dist/source/index.d.ts.map +1 -0
  357. package/dist/source/index.js +29 -0
  358. package/dist/source/index.js.map +1 -0
  359. package/dist/source/kysely.d.ts +3 -0
  360. package/dist/source/kysely.d.ts.map +1 -0
  361. package/dist/source/kysely.js +3 -0
  362. package/dist/source/kysely.js.map +1 -0
  363. package/dist/source/migrations.d.ts +22 -0
  364. package/dist/source/migrations.d.ts.map +1 -0
  365. package/dist/source/migrations.js +104 -0
  366. package/dist/source/migrations.js.map +1 -0
  367. package/dist/source/next.d.ts +33 -0
  368. package/dist/source/next.d.ts.map +1 -0
  369. package/dist/source/next.js +26 -0
  370. package/dist/source/next.js.map +1 -0
  371. package/dist/source/pushQueue.d.ts +135 -0
  372. package/dist/source/pushQueue.d.ts.map +1 -0
  373. package/dist/source/pushQueue.js +257 -0
  374. package/dist/source/pushQueue.js.map +1 -0
  375. package/dist/source/signing.d.ts +93 -0
  376. package/dist/source/signing.d.ts.map +1 -0
  377. package/dist/source/signing.js +163 -0
  378. package/dist/source/signing.js.map +1 -0
  379. package/dist/source/types.d.ts +402 -0
  380. package/dist/source/types.d.ts.map +1 -0
  381. package/dist/source/types.js +60 -0
  382. package/dist/source/types.js.map +1 -0
  383. package/dist/syncLog/contract.d.ts +21 -0
  384. package/dist/syncLog/contract.d.ts.map +1 -0
  385. package/dist/syncLog/contract.js +20 -0
  386. package/dist/syncLog/contract.js.map +1 -0
  387. package/dist/syncLog/index.d.ts +2 -0
  388. package/dist/syncLog/index.d.ts.map +1 -0
  389. package/dist/syncLog/index.js +2 -0
  390. package/dist/syncLog/index.js.map +1 -0
  391. package/dist/testing/fixtures/httpResponses.d.ts +74 -0
  392. package/dist/testing/fixtures/httpResponses.d.ts.map +1 -0
  393. package/dist/testing/fixtures/httpResponses.js +102 -0
  394. package/dist/testing/fixtures/httpResponses.js.map +1 -0
  395. package/dist/transactionLayer.d.ts +110 -0
  396. package/dist/transactionLayer.d.ts.map +1 -0
  397. package/dist/transactionLayer.js +25 -0
  398. package/dist/transactionLayer.js.map +1 -0
  399. package/dist/transactions/settlement/commitEnvelope.d.ts +144 -0
  400. package/dist/transactions/settlement/commitEnvelope.d.ts.map +1 -0
  401. package/dist/transactions/settlement/commitEnvelope.js +162 -0
  402. package/dist/transactions/settlement/commitEnvelope.js.map +1 -0
  403. package/dist/transactions/settlement/httpCommitEnvelope.d.ts +54 -0
  404. package/dist/transactions/settlement/httpCommitEnvelope.d.ts.map +1 -0
  405. package/dist/transactions/settlement/httpCommitEnvelope.js +208 -0
  406. package/dist/transactions/settlement/httpCommitEnvelope.js.map +1 -0
  407. package/dist/transactions/settlement/idempotencyKey.d.ts +11 -0
  408. package/dist/transactions/settlement/idempotencyKey.d.ts.map +1 -0
  409. package/dist/transactions/settlement/idempotencyKey.js +10 -0
  410. package/dist/transactions/settlement/idempotencyKey.js.map +1 -0
  411. package/dist/transactions/settlement/pendingWrite.d.ts +113 -0
  412. package/dist/transactions/settlement/pendingWrite.d.ts.map +1 -0
  413. package/dist/transactions/settlement/pendingWrite.js +21 -0
  414. package/dist/transactions/settlement/pendingWrite.js.map +1 -0
  415. package/dist/transport/commitFrames.d.ts +91 -0
  416. package/dist/transport/commitFrames.d.ts.map +1 -0
  417. package/dist/transport/commitFrames.js +135 -0
  418. package/dist/transport/commitFrames.js.map +1 -0
  419. package/dist/transport/connectionManager.d.ts +216 -0
  420. package/dist/transport/connectionManager.d.ts.map +1 -0
  421. package/dist/transport/connectionManager.js +674 -0
  422. package/dist/transport/connectionManager.js.map +1 -0
  423. package/dist/transport/credentialLifecycle.d.ts +178 -0
  424. package/dist/transport/credentialLifecycle.d.ts.map +1 -0
  425. package/dist/transport/credentialLifecycle.js +324 -0
  426. package/dist/transport/credentialLifecycle.js.map +1 -0
  427. package/dist/transport/heartbeat.d.ts +66 -0
  428. package/dist/transport/heartbeat.d.ts.map +1 -0
  429. package/dist/transport/heartbeat.js +94 -0
  430. package/dist/transport/heartbeat.js.map +1 -0
  431. package/dist/transport/httpClient.d.ts +143 -0
  432. package/dist/transport/httpClient.d.ts.map +1 -0
  433. package/dist/transport/httpClient.js +150 -0
  434. package/dist/transport/httpClient.js.map +1 -0
  435. package/dist/transport/httpFeed.d.ts +4 -0
  436. package/dist/transport/httpFeed.d.ts.map +1 -0
  437. package/dist/transport/httpFeed.js +94 -0
  438. package/dist/transport/httpFeed.js.map +1 -0
  439. package/dist/transport/httpOptions.d.ts +34 -0
  440. package/dist/transport/httpOptions.d.ts.map +1 -0
  441. package/dist/transport/httpOptions.js +13 -0
  442. package/dist/transport/httpOptions.js.map +1 -0
  443. package/dist/transport/httpTransport.d.ts +68 -0
  444. package/dist/transport/httpTransport.d.ts.map +1 -0
  445. package/dist/transport/httpTransport.js +1433 -0
  446. package/dist/transport/httpTransport.js.map +1 -0
  447. package/dist/transport/networkProbe.d.ts +85 -0
  448. package/dist/transport/networkProbe.d.ts.map +1 -0
  449. package/dist/transport/networkProbe.js +208 -0
  450. package/dist/transport/networkProbe.js.map +1 -0
  451. package/dist/transport/wsFrameHandlers.d.ts +129 -0
  452. package/dist/transport/wsFrameHandlers.d.ts.map +1 -0
  453. package/dist/transport/wsFrameHandlers.js +429 -0
  454. package/dist/transport/wsFrameHandlers.js.map +1 -0
  455. package/dist/transport/wsTransport.d.ts +575 -0
  456. package/dist/transport/wsTransport.d.ts.map +1 -0
  457. package/dist/transport/wsTransport.js +1024 -0
  458. package/dist/transport/wsTransport.js.map +1 -0
  459. package/dist/types/assertExact.d.ts +18 -0
  460. package/dist/types/assertExact.d.ts.map +1 -0
  461. package/dist/types/assertExact.js +2 -0
  462. package/dist/types/assertExact.js.map +1 -0
  463. package/dist/types/global.d.ts +108 -0
  464. package/dist/types/global.d.ts.map +1 -0
  465. package/dist/types/global.js +41 -0
  466. package/dist/types/global.js.map +1 -0
  467. package/dist/types/index.d.ts +206 -0
  468. package/dist/types/index.d.ts.map +1 -0
  469. package/dist/types/index.js +57 -0
  470. package/dist/types/index.js.map +1 -0
  471. package/dist/types/modelData.d.ts +11 -0
  472. package/dist/types/modelData.d.ts.map +1 -0
  473. package/dist/types/modelData.js +10 -0
  474. package/dist/types/modelData.js.map +1 -0
  475. package/dist/types/participant.d.ts +21 -0
  476. package/dist/types/participant.d.ts.map +1 -0
  477. package/dist/types/participant.js +11 -0
  478. package/dist/types/participant.js.map +1 -0
  479. package/dist/types/streams.d.ts +545 -0
  480. package/dist/types/streams.d.ts.map +1 -0
  481. package/dist/types/streams.js +12 -0
  482. package/dist/types/streams.js.map +1 -0
  483. package/dist/utils/asyncIterator.d.ts +35 -0
  484. package/dist/utils/asyncIterator.d.ts.map +1 -0
  485. package/dist/utils/asyncIterator.js +136 -0
  486. package/dist/utils/asyncIterator.js.map +1 -0
  487. package/dist/utils/duration.d.ts +51 -0
  488. package/dist/utils/duration.d.ts.map +1 -0
  489. package/dist/utils/duration.js +78 -0
  490. package/dist/utils/duration.js.map +1 -0
  491. package/dist/utils/json.d.ts +58 -0
  492. package/dist/utils/json.d.ts.map +1 -0
  493. package/dist/utils/json.js +277 -0
  494. package/dist/utils/json.js.map +1 -0
  495. package/dist/webhooks/events.d.ts +44 -0
  496. package/dist/webhooks/events.d.ts.map +1 -0
  497. package/dist/webhooks/events.js +43 -0
  498. package/dist/webhooks/events.js.map +1 -0
  499. package/dist/webhooks/index.d.ts +9 -0
  500. package/dist/webhooks/index.d.ts.map +1 -0
  501. package/dist/webhooks/index.js +9 -0
  502. package/dist/webhooks/index.js.map +1 -0
  503. package/dist/wire/accountResponses.d.ts +463 -0
  504. package/dist/wire/accountResponses.d.ts.map +1 -0
  505. package/dist/wire/accountResponses.js +294 -0
  506. package/dist/wire/accountResponses.js.map +1 -0
  507. package/dist/wire/auth.d.ts +57 -0
  508. package/dist/wire/auth.d.ts.map +1 -0
  509. package/dist/wire/auth.js +71 -0
  510. package/dist/wire/auth.js.map +1 -0
  511. package/dist/wire/bootstrapReason.d.ts +10 -0
  512. package/dist/wire/bootstrapReason.d.ts.map +1 -0
  513. package/dist/wire/bootstrapReason.js +9 -0
  514. package/dist/wire/bootstrapReason.js.map +1 -0
  515. package/dist/wire/claimEvent.d.ts +70 -0
  516. package/dist/wire/claimEvent.d.ts.map +1 -0
  517. package/dist/wire/claimEvent.js +74 -0
  518. package/dist/wire/claimEvent.js.map +1 -0
  519. package/dist/wire/claims.d.ts +475 -0
  520. package/dist/wire/claims.d.ts.map +1 -0
  521. package/dist/wire/claims.js +326 -0
  522. package/dist/wire/claims.js.map +1 -0
  523. package/dist/wire/commit.d.ts +604 -0
  524. package/dist/wire/commit.d.ts.map +1 -0
  525. package/dist/wire/commit.js +322 -0
  526. package/dist/wire/commit.js.map +1 -0
  527. package/dist/wire/delta.d.ts +251 -0
  528. package/dist/wire/delta.d.ts.map +1 -0
  529. package/dist/wire/delta.js +148 -0
  530. package/dist/wire/delta.js.map +1 -0
  531. package/dist/wire/errorEnvelope.d.ts +73 -0
  532. package/dist/wire/errorEnvelope.d.ts.map +1 -0
  533. package/dist/wire/errorEnvelope.js +124 -0
  534. package/dist/wire/errorEnvelope.js.map +1 -0
  535. package/dist/wire/feedCursor.d.ts +61 -0
  536. package/dist/wire/feedCursor.d.ts.map +1 -0
  537. package/dist/wire/feedCursor.js +83 -0
  538. package/dist/wire/feedCursor.js.map +1 -0
  539. package/dist/wire/feedEvent.d.ts +264 -0
  540. package/dist/wire/feedEvent.d.ts.map +1 -0
  541. package/dist/wire/feedEvent.js +66 -0
  542. package/dist/wire/feedEvent.js.map +1 -0
  543. package/dist/wire/frames.d.ts +195 -0
  544. package/dist/wire/frames.d.ts.map +1 -0
  545. package/dist/wire/frames.js +51 -0
  546. package/dist/wire/frames.js.map +1 -0
  547. package/dist/wire/inboundFrames.d.ts +490 -0
  548. package/dist/wire/inboundFrames.d.ts.map +1 -0
  549. package/dist/wire/inboundFrames.js +117 -0
  550. package/dist/wire/inboundFrames.js.map +1 -0
  551. package/dist/wire/index.d.ts +55 -0
  552. package/dist/wire/index.d.ts.map +1 -0
  553. package/dist/wire/index.js +84 -0
  554. package/dist/wire/index.js.map +1 -0
  555. package/dist/wire/listEnvelope.d.ts +38 -0
  556. package/dist/wire/listEnvelope.d.ts.map +1 -0
  557. package/dist/wire/listEnvelope.js +43 -0
  558. package/dist/wire/listEnvelope.js.map +1 -0
  559. package/dist/wire/modelMutations.d.ts +32 -0
  560. package/dist/wire/modelMutations.d.ts.map +1 -0
  561. package/dist/wire/modelMutations.js +53 -0
  562. package/dist/wire/modelMutations.js.map +1 -0
  563. package/dist/wire/modelResponses.d.ts +79 -0
  564. package/dist/wire/modelResponses.d.ts.map +1 -0
  565. package/dist/wire/modelResponses.js +44 -0
  566. package/dist/wire/modelResponses.js.map +1 -0
  567. package/dist/wire/modelShape.d.ts +79 -0
  568. package/dist/wire/modelShape.d.ts.map +1 -0
  569. package/dist/wire/modelShape.js +75 -0
  570. package/dist/wire/modelShape.js.map +1 -0
  571. package/dist/wire/protocol.d.ts +39 -0
  572. package/dist/wire/protocol.d.ts.map +1 -0
  573. package/dist/wire/protocol.js +39 -0
  574. package/dist/wire/protocol.js.map +1 -0
  575. package/dist/wire/protocolVersion.d.ts +74 -0
  576. package/dist/wire/protocolVersion.d.ts.map +1 -0
  577. package/dist/wire/protocolVersion.js +84 -0
  578. package/dist/wire/protocolVersion.js.map +1 -0
  579. package/package.json +187 -0
  580. package/src/ablo.ts +139 -0
  581. package/src/ai-sdk/coordinatedTool.ts +214 -0
  582. package/src/ai-sdk/index.ts +7 -0
  583. package/src/auth/apiKey.ts +540 -0
  584. package/src/auth/bootstrapScope.ts +15 -0
  585. package/src/auth/browserCredentialSafety.ts +48 -0
  586. package/src/auth/capability.ts +326 -0
  587. package/src/auth/capabilityLifecycle.ts +174 -0
  588. package/src/auth/credentialEndpoint.ts +79 -0
  589. package/src/auth/credentialEndpointProtocol.ts +51 -0
  590. package/src/auth/credentialKind.ts +20 -0
  591. package/src/auth/credentialPolicy.ts +244 -0
  592. package/src/auth/credentialResult.ts +23 -0
  593. package/src/auth/credentialSource.ts +99 -0
  594. package/src/auth/hostedEndpoints.ts +24 -0
  595. package/src/auth/identity.ts +330 -0
  596. package/src/auth/index.ts +592 -0
  597. package/src/auth/schemas.ts +94 -0
  598. package/src/auth/sessionMint.ts +126 -0
  599. package/src/auth/token.ts +4 -0
  600. package/src/batching/index.ts +200 -0
  601. package/src/coordination/awaitClaimGrant.ts +243 -0
  602. package/src/coordination/claimHeartbeatLoop.ts +165 -0
  603. package/src/coordination/claimMeta.ts +56 -0
  604. package/src/coordination/events.ts +86 -0
  605. package/src/coordination/index.ts +181 -0
  606. package/src/coordination/locator.ts +200 -0
  607. package/src/coordination/schema.ts +1346 -0
  608. package/src/coordination/targetConflict.ts +85 -0
  609. package/src/coordination/trace.ts +173 -0
  610. package/src/docs/catalog.ts +274 -0
  611. package/src/docs/index.ts +18 -0
  612. package/src/durableWrites.ts +134 -0
  613. package/src/environment.ts +132 -0
  614. package/src/errorCodes.ts +1591 -0
  615. package/src/errors.ts +918 -0
  616. package/src/footprint.ts +0 -0
  617. package/src/headlessClient.ts +161 -0
  618. package/src/index.ts +52 -0
  619. package/src/keys/index.ts +254 -0
  620. package/src/log/syncDeltaRow.ts +119 -0
  621. package/src/logger.ts +22 -0
  622. package/src/observability.ts +85 -0
  623. package/src/policy/types.ts +292 -0
  624. package/src/resources/functionalUpdate.ts +151 -0
  625. package/src/resources/httpResources.ts +520 -0
  626. package/src/resources/modelOperations.ts +444 -0
  627. package/src/resources/mutationOptions.ts +66 -0
  628. package/src/resources/where.ts +160 -0
  629. package/src/resources/writeOptionsSchema.ts +91 -0
  630. package/src/schema/coordination.ts +161 -0
  631. package/src/schema/ddl.ts +593 -0
  632. package/src/schema/ddlLock.ts +53 -0
  633. package/src/schema/diff.ts +489 -0
  634. package/src/schema/field.ts +321 -0
  635. package/src/schema/fieldRef.ts +88 -0
  636. package/src/schema/generate.ts +95 -0
  637. package/src/schema/index.ts +278 -0
  638. package/src/schema/loadStrategy.ts +52 -0
  639. package/src/schema/model.ts +476 -0
  640. package/src/schema/openapi.ts +658 -0
  641. package/src/schema/queries.ts +273 -0
  642. package/src/schema/relation.ts +291 -0
  643. package/src/schema/residency.ts +30 -0
  644. package/src/schema/roles.ts +325 -0
  645. package/src/schema/schema.ts +825 -0
  646. package/src/schema/select.ts +115 -0
  647. package/src/schema/serialize.ts +373 -0
  648. package/src/schema/sugar.ts +194 -0
  649. package/src/schema/tenancy.ts +221 -0
  650. package/src/server/adapter.ts +203 -0
  651. package/src/server/commit.ts +109 -0
  652. package/src/server/index.ts +26 -0
  653. package/src/server/readConfig.ts +82 -0
  654. package/src/server/storageMode.ts +20 -0
  655. package/src/source/adapter.ts +85 -0
  656. package/src/source/adapters/drizzle.ts +291 -0
  657. package/src/source/adapters/kysely.ts +346 -0
  658. package/src/source/adapters/kyselyMutationCore.ts +220 -0
  659. package/src/source/adapters/memory.ts +154 -0
  660. package/src/source/adapters/prisma.ts +282 -0
  661. package/src/source/conformance.ts +287 -0
  662. package/src/source/connector.ts +394 -0
  663. package/src/source/connectorProtocol.ts +189 -0
  664. package/src/source/contract.ts +221 -0
  665. package/src/source/drizzle.ts +1 -0
  666. package/src/source/factory.ts +444 -0
  667. package/src/source/idempotency.ts +189 -0
  668. package/src/source/index.ts +182 -0
  669. package/src/source/kysely.ts +2 -0
  670. package/src/source/migrations.ts +109 -0
  671. package/src/source/next.ts +38 -0
  672. package/src/source/pushQueue.ts +368 -0
  673. package/src/source/signing.ts +279 -0
  674. package/src/source/types.ts +502 -0
  675. package/src/syncLog/contract.ts +32 -0
  676. package/src/syncLog/index.ts +1 -0
  677. package/src/testing/fixtures/httpResponses.ts +155 -0
  678. package/src/transactionLayer.ts +122 -0
  679. package/src/transactions/settlement/commitEnvelope.ts +192 -0
  680. package/src/transactions/settlement/httpCommitEnvelope.ts +250 -0
  681. package/src/transactions/settlement/idempotencyKey.ts +11 -0
  682. package/src/transactions/settlement/pendingWrite.ts +24 -0
  683. package/src/transport/commitFrames.ts +225 -0
  684. package/src/transport/connectionManager.ts +856 -0
  685. package/src/transport/credentialLifecycle.ts +389 -0
  686. package/src/transport/heartbeat.ts +118 -0
  687. package/src/transport/httpClient.ts +354 -0
  688. package/src/transport/httpFeed.ts +111 -0
  689. package/src/transport/httpOptions.ts +41 -0
  690. package/src/transport/httpTransport.ts +1976 -0
  691. package/src/transport/networkProbe.ts +249 -0
  692. package/src/transport/wsFrameHandlers.ts +619 -0
  693. package/src/transport/wsTransport.ts +1491 -0
  694. package/src/types/assertExact.ts +17 -0
  695. package/src/types/global.ts +123 -0
  696. package/src/types/index.ts +270 -0
  697. package/src/types/modelData.ts +11 -0
  698. package/src/types/participant.ts +22 -0
  699. package/src/types/streams.ts +702 -0
  700. package/src/utils/asyncIterator.ts +141 -0
  701. package/src/utils/duration.ts +88 -0
  702. package/src/utils/json.ts +281 -0
  703. package/src/webhooks/events.ts +96 -0
  704. package/src/webhooks/index.ts +12 -0
  705. package/src/wire/accountResponses.ts +328 -0
  706. package/src/wire/auth.ts +86 -0
  707. package/src/wire/bootstrapReason.ts +10 -0
  708. package/src/wire/claimEvent.ts +85 -0
  709. package/src/wire/claims.ts +370 -0
  710. package/src/wire/commit.ts +379 -0
  711. package/src/wire/delta.ts +173 -0
  712. package/src/wire/errorEnvelope.ts +133 -0
  713. package/src/wire/feedCursor.ts +91 -0
  714. package/src/wire/feedEvent.ts +74 -0
  715. package/src/wire/frames.ts +77 -0
  716. package/src/wire/inboundFrames.ts +182 -0
  717. package/src/wire/index.ts +328 -0
  718. package/src/wire/listEnvelope.ts +51 -0
  719. package/src/wire/modelMutations.ts +55 -0
  720. package/src/wire/modelResponses.ts +48 -0
  721. package/src/wire/modelShape.ts +83 -0
  722. package/src/wire/protocol.ts +39 -0
  723. package/src/wire/protocolVersion.ts +97 -0
@@ -0,0 +1,1346 @@
1
+ import { z } from 'zod';
2
+ import { syncGroupInputSchema } from '../schema/roles.js';
3
+ export { syncGroupInputSchema } from '../schema/roles.js';
4
+ import { isFieldRef, type FieldRef } from '../schema/fieldRef.js';
5
+ import type { ParticipantKind } from '../types/participant.js';
6
+ import type { AssertExact } from '../types/assertExact.js';
7
+
8
+ /**
9
+ * The wire schemas for coordination — the shapes that keep agents and people
10
+ * from overwriting each other on a shared row. Coordination works in three
11
+ * layers, from outermost to innermost:
12
+ *
13
+ * 1. Presence (observation): who is working where. It reports, never blocks.
14
+ * 2. Claims (pessimistic leases): `claim_begin` / `claim_abandon` grant one
15
+ * participant exclusive intent on a target while others wait.
16
+ * 3. Stale-context (optimistic): a `readAt` watermark plus an `onStale` write
17
+ * guard that catches a lost update when the row moved after you read it.
18
+ *
19
+ * These Zod schemas are the single definition of each shape. Both the client
20
+ * SDK and the server derive their TypeScript types from them with `z.infer`
21
+ * rather than re-declaring the shapes, and the server validates inbound frames
22
+ * against them at runtime.
23
+ */
24
+
25
+ // ─────────────────────────────────────────────────────────────────────────
26
+ // Shared primitives
27
+ // ─────────────────────────────────────────────────────────────────────────
28
+
29
+ /**
30
+ * An app-defined claimable part name — a cell (`'B2'`), a section id, a
31
+ * block — made explicit with {@link part}. The schema's own field names need
32
+ * no marker; a name that is NOT a field does, so looseness is a visible
33
+ * decision at the call site rather than a silent absorber of typos.
34
+ *
35
+ * A small object rather than a branded string on purpose: a brand makes a
36
+ * concrete schema's claim params mutually unassignable with the erased
37
+ * `SchemaRecord` view, and the react context boundary erases and restores
38
+ * exactly that way. The object member stays pairwise comparable, so no
39
+ * boundary needs a cast through `unknown`.
40
+ */
41
+ export interface ClaimPart {
42
+ readonly part: string;
43
+ }
44
+
45
+ /**
46
+ * Name an app-defined part of a row for a claim target: `part('B2')` for a
47
+ * cell, `part('sec_intro')` for a section. The conflict rule compares part
48
+ * names as opaque case-insensitive strings, so any name is legal on the
49
+ * wire — this marker exists purely so the type surface stays definite about
50
+ * the model's own fields.
51
+ */
52
+ export function part(name: string): ClaimPart {
53
+ return { part: name };
54
+ }
55
+
56
+ /**
57
+ * The wire spelling of a part name, from whichever spelling the caller used.
58
+ *
59
+ * Three, because they are three different promises. A {@link FieldRef} —
60
+ * `schema.fields.tasks.status` — is a field the schema declares, so a name that
61
+ * does not exist never compiles. `part('B2')` is a name the schema does not
62
+ * know and says so. A bare string is neither, and survives only because the
63
+ * erased `SchemaRecord` view and untyped callers still need it.
64
+ *
65
+ * All three become the same string here: the wire has always carried names, and
66
+ * what differs is how much was known before the crossing.
67
+ */
68
+ export function partName(value: string | ClaimPart | FieldRef): string {
69
+ if (typeof value === 'string') return value;
70
+ return isFieldRef(value) ? value.field : value.part;
71
+ }
72
+
73
+ /**
74
+ * One claimable part name.
75
+ *
76
+ * Names compare as opaque strings, so any name is legal — except one that is
77
+ * plainly several. A caller who needed to claim two parts and had only `field`
78
+ * to say it in packed them into one delimited string, and because
79
+ * `blocks:b_1,b_2` and `blocks:b_1` are different names, both writers were
80
+ * granted a lease on `b_1` and one of their updates was lost with nothing
81
+ * raised. `fields` exists to say that, and refusing the packed spelling is what
82
+ * makes the mistake visible at the moment it is made rather than as a missing
83
+ * update later.
84
+ *
85
+ * Deliberately narrow: only the comma, because that is what a caller reaches
86
+ * for to join a list. A part name is otherwise free.
87
+ */
88
+ const partNameSchema = z.string().refine((name) => !name.includes(','), {
89
+ message:
90
+ 'A part name cannot contain a comma. Claim several parts with `fields: [a, b]` — two names in one `field` compare as a single unrelated name, so both writers would be granted the same part.',
91
+ });
92
+
93
+ export const participantKindSchema = z.enum(['user', 'agent', 'system']);
94
+ // The actor union is declared once, in types/participant.ts — the participant
95
+ // IS the actor (user | agent | system), so that file owns the name. The pin
96
+ // below fails to compile if this schema and the canonical union ever drift.
97
+ export type { ParticipantKind } from '../types/participant.js';
98
+ const _participantKindContract: AssertExact<
99
+ z.infer<typeof participantKindSchema>,
100
+ ParticipantKind
101
+ > = true;
102
+ void _participantKindContract;
103
+
104
+ /**
105
+ * Parses a participant kind from an inbound frame, tolerating an older wire
106
+ * dialect. Some presence and claim frames label a non-agent participant
107
+ * `'human'`, while the rest of the surface uses `'user'` for the same
108
+ * participant. This normalizes `'human'` to `'user'` on read so every consumer
109
+ * switches on one vocabulary. Producers emit the canonical
110
+ * {@link participantKindSchema} values, and the output union is never widened.
111
+ */
112
+ export const wireParticipantKindSchema = z.preprocess(
113
+ (value) => (value === 'human' ? 'user' : value),
114
+ participantKindSchema,
115
+ );
116
+
117
+ /**
118
+ * Resolves a peer's kind from an inbound presence or claim frame. It prefers
119
+ * the server-stamped `participantKind` (normalized through
120
+ * {@link wireParticipantKindSchema}). A frame from an older server that omits
121
+ * that field falls back to the `isAgent` boolean, which can tell 'agent' from
122
+ * 'user' but can never report 'system'.
123
+ */
124
+ export function participantKindFromWire(
125
+ wireKind: unknown,
126
+ isAgent: boolean | undefined,
127
+ ): ParticipantKind {
128
+ const parsed = wireParticipantKindSchema.safeParse(wireKind);
129
+ if (parsed.success) return parsed.data;
130
+ return isAgent ? 'agent' : 'user';
131
+ }
132
+
133
+ /**
134
+ * Reads the peer-visible description a claim or presence frame carries in its
135
+ * opaque `meta.description`. This is the single place that unpacks that field.
136
+ * A caller that has an explicit `description` should prefer it
137
+ * (`explicit ?? fromMeta`).
138
+ *
139
+ * The parameter is `unknown` because that is the honest requirement: this reads
140
+ * one optional string off a value it does not own. Demanding the wire's open
141
+ * record instead forced every caller holding a claim's *declared* `meta` — the
142
+ * shape registered on `Register`'s `ClaimMeta` slot — through a conversion to
143
+ * ask a question that never needed one.
144
+ */
145
+ export function descriptionFromMeta(meta: unknown): string | undefined {
146
+ if (typeof meta !== 'object' || meta === null) return undefined;
147
+ if (!('description' in meta)) return undefined;
148
+ const { description } = meta;
149
+ return typeof description === 'string' ? description : undefined;
150
+ }
151
+
152
+ /** The default a claim carries when its holder describes no work. */
153
+ export const DEFAULT_CLAIM_DESCRIPTION = 'editing';
154
+
155
+ /**
156
+ * Resolves the peer-visible description of a claim from the places a caller may
157
+ * have put it, falling back to a plain default.
158
+ *
159
+ * `reason` was this field's name before it was renamed, and the rename shipped
160
+ * without leaving anything behind — so the wire kept accepting both spellings
161
+ * while the SDK type quietly offered only one, and two branches "fixed" the
162
+ * gap in opposite directions without either being contradicted by a compiler.
163
+ * The precedence is declared once, here, and both the client and the server
164
+ * read it from this function rather than each spelling out the same `??` chain.
165
+ *
166
+ * A caller whose default differs — a claim taken around a `create` describes
167
+ * itself as `'creating'` — passes that word as `fallback`; the precedence above
168
+ * it stays this function's.
169
+ */
170
+ export function claimDescription(
171
+ source: {
172
+ description?: string | null;
173
+ reason?: string | null;
174
+ /** Wire-shaped or declared — see {@link descriptionFromMeta}. */
175
+ meta?: unknown;
176
+ },
177
+ fallback: string = DEFAULT_CLAIM_DESCRIPTION,
178
+ ): string {
179
+ return (
180
+ source.description ??
181
+ descriptionFromMeta(source.meta) ??
182
+ source.reason ??
183
+ fallback
184
+ );
185
+ }
186
+
187
+ /**
188
+ * What a coordination event points at — the locator shared by all three
189
+ * layers. It names an entity, optionally narrowed to a field or set of fields,
190
+ * and carries opaque application metadata.
191
+ */
192
+ export const targetRefSchema = z.object({
193
+ entityType: z.string(),
194
+ entityId: z.string(),
195
+ field: partNameSchema.optional(),
196
+ /**
197
+ * Several named parts of one row, claimed together — three sections of a
198
+ * document, two cells of a table.
199
+ *
200
+ * This exists because there was no way to say it. A caller who needed it
201
+ * packed the set into `field` as one delimited string, and the conflict rule
202
+ * compares `field` for equality: `blocks:b_1` and `blocks:b_1,b_2` read as
203
+ * unrelated targets, so both writers were granted a lease on `b_1` and one
204
+ * of their updates was lost with nothing raised. A set compares as a set —
205
+ * overlapping sets conflict, disjoint sets do not.
206
+ *
207
+ * `field` remains for the single-field case and is read as a set of one, so
208
+ * a claim naming `field` and a claim naming `fields` still compare correctly
209
+ * against each other.
210
+ */
211
+ fields: z.array(partNameSchema).readonly().optional(),
212
+ meta: z.record(z.string(), z.unknown()).optional(),
213
+ });
214
+ export type TargetRef = z.infer<typeof targetRefSchema>;
215
+
216
+ /**
217
+ * The same locator in the spelling the wait line and the claim handle use —
218
+ * `{ type, id }` for the entity, the sub-entity half unchanged. It is a
219
+ * projection of {@link targetRefSchema} rather than a second declaration, so a
220
+ * member added to the locator reaches the wait line without anyone editing it;
221
+ * a hand-written copy here is how `fields` came to be missing from queue frames.
222
+ */
223
+ const streamTargetSchema = targetRefSchema
224
+ .omit({ entityType: true, entityId: true })
225
+ .extend({ type: z.string(), id: z.string() });
226
+
227
+ // ─────────────────────────────────────────────────────────────────────────
228
+ // Layer 3 — optimistic stale-context (the write guard)
229
+ // ─────────────────────────────────────────────────────────────────────────
230
+
231
+ /**
232
+ * How the server treats a write whose snapshot watermark (`readAt`) is older
233
+ * than the target row's latest change. There are three dispositions:
234
+ * • `notify` — hold the write and return a {@link StaleNotification}
235
+ * carrying the current value, so the actor (agent or human)
236
+ * can resolve it.
237
+ * • `reject` — throw `AbloStaleContextError`, the default when `readAt`
238
+ * is present.
239
+ * • `overwrite` — apply the write blindly, last write wins, with no signal.
240
+ */
241
+ export const onStaleModeSchema = z.enum(['reject', 'overwrite', 'notify']);
242
+ export type OnStaleMode = z.infer<typeof onStaleModeSchema>;
243
+
244
+ /**
245
+ * The optimistic guard carried on a commit operation. `readAt` is the
246
+ * snapshot watermark from `context.capture` (null/absent ⇒ unguarded write).
247
+ * `bypass` is the explicit, recorded override of a *foreign* pessimistic
248
+ * claim — see the claim layer below.
249
+ */
250
+ export const writeGuardSchema = z.object({
251
+ readAt: z.number().nullish(),
252
+ onStale: onStaleModeSchema.nullish(),
253
+ bypass: z.boolean().optional(),
254
+ });
255
+ export type WriteGuard = z.infer<typeof writeGuardSchema>;
256
+
257
+ /**
258
+ * The advisory returned to a committer whose write hit a stale-context
259
+ * conflict under `onStale: 'notify'` — it reports that the value the committer
260
+ * reasoned against changed while they were away. Rather than throwing, the
261
+ * server hands back the conflicting field's current value as data so the
262
+ * actor — an agent or a human — can reconcile and re-commit. A claim is the
263
+ * prospective form of the same idea (coordinate before acting); this
264
+ * notification is the in-flight form (here is what changed, you resolve). It
265
+ * rides on the commit acknowledgement alongside `lastSyncId`; an empty or
266
+ * absent array means nothing the committer depended on moved.
267
+ *
268
+ * Only `onStale: 'notify'` produces this. The conflicting operation was held,
269
+ * not written, and the actor reconciles against `currentValues` and
270
+ * re-commits. `reject` throws instead, and `overwrite` proceeds silently —
271
+ * neither notifies.
272
+ */
273
+ export const staleNotificationSchema = z.object({
274
+ /** Names this object's type; every returned object carries such a tag. */
275
+ object: z.literal('stale_notification').optional(),
276
+ /** Model name of the conflicting row. */
277
+ model: z.string(),
278
+ /** Row id. */
279
+ id: z.string(),
280
+ /** The watermark the committer reasoned against (its `readAt`). */
281
+ readAt: z.number(),
282
+ /**
283
+ * Newest delta id on the row — the committer's new watermark. Re-capture
284
+ * context at/after this id to reconcile.
285
+ */
286
+ observedSyncId: z.number(),
287
+ /**
288
+ * Fields whose concurrent change collided with this write (intersection of
289
+ * the committer's written columns and a newer delta's `changed_fields`).
290
+ * Empty ⇒ a whole-entity change (CREATE/DELETE/legacy delta).
291
+ */
292
+ conflictingFields: z.array(z.string()),
293
+ /**
294
+ * The live values of `conflictingFields` after the conflict — the piece a
295
+ * plain stale error omits. It lets the actor reconcile without a follow-up read.
296
+ */
297
+ currentValues: z.record(z.string(), z.unknown()),
298
+ /** Who wrote the conflicting delta. */
299
+ writtenBy: z.object({
300
+ kind: participantKindSchema,
301
+ id: z.string(),
302
+ }),
303
+ /**
304
+ * Set when this notification is for a GROUP premise (e.g. `report:abc`,
305
+ * `section:s1`) rather than a single row — "something in the group you read
306
+ * changed." For a group notification `conflictingFields`/`currentValues` are
307
+ * empty (the change could span many rows); re-read the group at
308
+ * `observedSyncId` to reconcile. Absent ⇒ a row-scoped notification.
309
+ */
310
+ group: z.string().optional(),
311
+ });
312
+ export type StaleNotification = z.infer<typeof staleNotificationSchema>;
313
+
314
+ /**
315
+ * One entry in a commit's batch premise — a read it was based on, so the
316
+ * server can ask "did anything I looked at change?" — broader than the
317
+ * write-target check, which only validates the rows being written. The server
318
+ * re-runs stale detection against each entry at its `readAt`; a moved premise
319
+ * fires the entry's `onStale` disposition (default `reject`) across the whole
320
+ * batch (`notify` holds every write and notifies, `reject` aborts, `overwrite`
321
+ * proceeds silently). An entry comes at one of two granularities:
322
+ *
323
+ * • Row — `{ model, id, readAt, fields? }`: did this specific row, or these
324
+ * specific fields, change?
325
+ * • Group — `{ group, readAt }`: did anything in this sync group change?
326
+ * `group` is a sync-group key such as `report:abc` or `section:s1`, the
327
+ * same unit a participant watches and claims.
328
+ *
329
+ * See `packages/ablo/docs/concurrency-convention.md` (§4) for the
330
+ * governing convention and the receive → reconcile loop.
331
+ */
332
+ const readRowDependencySchema = z.object({
333
+ model: z.string(),
334
+ id: z.string(),
335
+ readAt: z.number(),
336
+ fields: z.array(z.string()).readonly().optional(),
337
+ onStale: onStaleModeSchema.optional(),
338
+ });
339
+
340
+ const readGroupDependencySchema = z.object({
341
+ group: z.string(),
342
+ readAt: z.number(),
343
+ onStale: onStaleModeSchema.optional(),
344
+ });
345
+
346
+ export const readDependencySchema = z.union([
347
+ readRowDependencySchema,
348
+ readGroupDependencySchema,
349
+ ]);
350
+ export type ReadDependency = z.infer<typeof readDependencySchema>;
351
+
352
+ /**
353
+ * A durable premise — what a participant is watching so that a later
354
+ * change to it opens a {@link StaleNotification}. Where a {@link ReadDependency}
355
+ * is checked once at commit and discarded, a `TrackDependency` is kept and
356
+ * re-checked against every future delta. The row form watches one object; the
357
+ * group form watches a whole sync group ("anything in `report:abc`"). See
358
+ * `packages/ablo/docs/groups.md` for how it drives change propagation.
359
+ *
360
+ * It is a PROJECTION of the ephemeral premise, not a second declaration of the
361
+ * same reference: a track names its target exactly as a read does, and the
362
+ * three ways it differs are stated here as omissions the compiler holds.
363
+ *
364
+ * • no `onStale` — a track always notifies; that is what tracking is;
365
+ * • no `fields` — a track fires at row grain, because the server keeps one
366
+ * row per tracked target and reports that the target moved, not which
367
+ * column did (`track_dependencies` has no field axis to store one in);
368
+ * • `readAt` optional — it defaults to the watermark of the commit that
369
+ * registered the track, so a caller with nothing to say about when it last
370
+ * looked gets "from here on".
371
+ *
372
+ * A field added to the premise reaches both halves; a field the durable half
373
+ * genuinely cannot carry has to be omitted here on purpose, in one line, rather
374
+ * than by being quietly left out of a copy.
375
+ */
376
+ const trackReadAtSchema = { readAt: z.number().optional() } as const;
377
+
378
+ export const trackDependencySchema = z.union([
379
+ readRowDependencySchema
380
+ .omit({ onStale: true, fields: true, readAt: true })
381
+ .extend(trackReadAtSchema),
382
+ readGroupDependencySchema
383
+ .omit({ onStale: true, readAt: true })
384
+ .extend(trackReadAtSchema),
385
+ ]);
386
+ export type TrackDependency = z.infer<typeof trackDependencySchema>;
387
+
388
+ // ─────────────────────────────────────────────────────────────────────────
389
+ // Layer 2 — pessimistic claims and leases
390
+ // ─────────────────────────────────────────────────────────────────────────
391
+
392
+ /**
393
+ * The lifecycle of a claim. When absent on the wire it means `'active'` (an
394
+ * additive back-compat default). The server stamps `'active'` on `claim_begin`
395
+ * and emits one terminal frame — `committed`, `canceled`, or `expired` — as the
396
+ * claim ends, so contenders learn how it resolved, not merely that it vanished.
397
+ */
398
+ export const wireClaimStatusSchema = z.enum([
399
+ 'active',
400
+ 'committed',
401
+ 'expired',
402
+ 'canceled',
403
+ ]);
404
+ export type WireClaimStatus = z.infer<typeof wireClaimStatusSchema>;
405
+
406
+ /**
407
+ * Every lifecycle state of a claim, as a caller sees it.
408
+ *
409
+ * `active` is the current holder — the lock itself. `queued` is waiting in line
410
+ * behind the holder and carries an advisory `position`. The rest are terminal
411
+ * and drop the claim from the synced set.
412
+ *
413
+ * Distinct from {@link wireClaimStatusSchema}, which never carries `queued`
414
+ * because the wire frame for a waiter is a different message. This is the one a
415
+ * published contract describes, so it is a schema rather than a bare TS union —
416
+ * a union cannot be derived into the API reference.
417
+ */
418
+ export const publicClaimStatusSchema = z.enum([
419
+ 'active',
420
+ 'queued',
421
+ 'committed',
422
+ 'expired',
423
+ 'canceled',
424
+ ]);
425
+ export type PublicClaimStatus = z.infer<typeof publicClaimStatusSchema>;
426
+
427
+ /**
428
+ * @deprecated Renamed to {@link wireClaimStatusSchema} — this is the wire enum,
429
+ * which never carries `'queued'`; the five-state public status lives in
430
+ * types/streams. Removed in 0.36.0.
431
+ */
432
+ export const claimStatusSchema = wireClaimStatusSchema;
433
+ /** @deprecated Renamed to {@link WireClaimStatus}. Removed in 0.36.0. */
434
+ export type ClaimStatus = WireClaimStatus;
435
+
436
+ /**
437
+ * Server-owned grant stamps — minted once when a claim is first granted and
438
+ * preserved verbatim across a re-announce of the same `claimId`, so neither a
439
+ * reconnect nor a client-supplied value can move them (unlike `declaredAt`,
440
+ * which the client sends afresh each announce). Both optional: a frame without
441
+ * them stays valid, and the feature each backs simply does not engage.
442
+ */
443
+ const grantStampFields = {
444
+ /**
445
+ * The monotonic fencing token minted for this grant (Option B). Strictly
446
+ * increasing per entity across successive grants, so a write that carries it
447
+ * is rejected at commit if a later holder already advanced the entity's
448
+ * high-water. A token-less write is simply not fence-checked.
449
+ */
450
+ fenceToken: z.number().optional(),
451
+ /**
452
+ * Lease origin (epoch ms): when THIS holding was acquired. The cumulative-
453
+ * hold ceiling measures a holder's fair share from here — and because it
454
+ * survives a re-announce, a reconnect cannot rewind the clock.
455
+ */
456
+ acquiredAt: z.number().optional(),
457
+ } as const;
458
+
459
+
460
+
461
+ /**
462
+ * A holder as the participant blocked behind them sees it: who has the row,
463
+ * what they said they are doing, and until when.
464
+ *
465
+ * This is the smaller half of a claim, so it is declared first and the full
466
+ * claim extends it — the waiter's view cannot omit a member the holder's view
467
+ * has, because there is nowhere for it to be omitted. Listing what to keep was
468
+ * the bug: this named `field` and not `path`, `range`, or `fields`, so a waiter
469
+ * could see that a row was held but never which part of it, and every locator
470
+ * member added later would have been missing here too.
471
+ */
472
+ export const wireClaimSummarySchema = targetRefSchema.extend({
473
+ claimId: z.string(),
474
+ /**
475
+ * Peer-visible description of the work being done (`'rewriting the risk
476
+ * section to match Q3'`). The server stamps a default when a frame carries
477
+ * none.
478
+ */
479
+ description: z.string().optional(),
480
+ /** Server-stamped declaration time (epoch ms). */
481
+ declaredAt: z.number(),
482
+ /** Server-computed TTL deadline (epoch ms). Readers treat as advisory. */
483
+ expiresAt: z.number(),
484
+ /**
485
+ * On whose authority the holder acts, and under which grant — stamped by the
486
+ * server off the connection's credential, never accepted from the frame. The
487
+ * same three fields the delta this claim produces will record, so "who is
488
+ * doing this" and "who did this" answer in one vocabulary.
489
+ *
490
+ * On the summary rather than the full claim because this is precisely what a
491
+ * blocked waiter needs: yielding to a colleague and queuing behind another
492
+ * customer's agent are different decisions.
493
+ *
494
+ * All three optional and additive — an older server omits them, which is a
495
+ * different fact from a holder that has no delegator.
496
+ */
497
+ onBehalfOfId: z.string().nullish(),
498
+ onBehalfOfKind: wireParticipantKindSchema.nullish(),
499
+ capabilityId: z.string().nullish(),
500
+ });
501
+ export type WireClaimSummary = z.infer<typeof wireClaimSummarySchema>;
502
+
503
+ /**
504
+ * The full claim as its holder's own frames carry it — the waiter's view plus
505
+ * the lifecycle and grant stamps that belong to the holding itself.
506
+ */
507
+ const wireClaimBaseSchema = wireClaimSummarySchema.extend({
508
+ status: wireClaimStatusSchema.optional(),
509
+ ...grantStampFields,
510
+ });
511
+
512
+ /** Why a claim ended in a non-success terminal state. */
513
+ export const claimErrorSchema = z.object({
514
+ code: z.string(),
515
+ message: z.string().optional(),
516
+ /** Participant already holding the target (conflict rejections). */
517
+ heldBy: z.string().optional(),
518
+ heldByClaimId: z.string().optional(),
519
+ heldByExpiresAt: z.number().optional(),
520
+ /** Rich holder context for conflict rejections. Additive: older frames omit it. */
521
+ heldByClaim: wireClaimSummarySchema.optional(),
522
+ /** Optional conflict-policy explanation. Additive: older frames omit it. */
523
+ policyReason: z.string().optional(),
524
+ });
525
+ export type ClaimError = z.infer<typeof claimErrorSchema>;
526
+
527
+ /**
528
+ * Why a claim ended without its holder releasing it, or was refused.
529
+ *
530
+ * A closed set, because the two refusals are different guarantees and a reader
531
+ * has to be able to tell them apart: `conflict` means someone holds the row
532
+ * right now and you may queue behind them, while `coordination_unavailable`
533
+ * means the coordinator could not answer, so nothing is known about the row.
534
+ * `expired` and `preempted` are the two ways a lease you held ends.
535
+ *
536
+ * The rejection frame's `reason` stays a plain string on the wire — it is
537
+ * frozen, and an older server may send a word not listed here. This is the
538
+ * reader's side of it: a value that parses becomes the typed reason, and one
539
+ * that does not is simply absent rather than smuggled through as prose.
540
+ * {@link claimExpiredSchema} and {@link claimLostSchema} already spelled their
541
+ * reasons as enums; this brings the refusals into line.
542
+ */
543
+ export const claimEventReasonSchema = z.enum([
544
+ 'conflict',
545
+ 'coordination_unavailable',
546
+ 'capability_denied',
547
+ 'invalid_target',
548
+ 'expired',
549
+ 'preempted',
550
+ ]);
551
+ export type ClaimEventReason = z.infer<typeof claimEventReasonSchema>;
552
+
553
+ /**
554
+ * A declared, pending-mutation claim — the unit broadcast inside a presence
555
+ * frame's `activeClaims`. The client supplies the descriptive `targetRef`
556
+ * fields, a `description` of the work, and a chosen `claimId`; the server stamps
557
+ * `declaredAt` and `expiresAt` and may set `status` and `error`. Those last
558
+ * two are optional, so one shape serves both the server, which sets them, and
559
+ * the leaner SDK view, which reads a claim without them.
560
+ */
561
+ export const wireClaimSchema = wireClaimBaseSchema.extend({
562
+ error: claimErrorSchema.optional(),
563
+ });
564
+ export type WireClaim = z.infer<typeof wireClaimSchema>;
565
+
566
+ export const claimRejectionSchema = z.object({
567
+ claimId: z.string(),
568
+ /**
569
+ * Why the claim was refused, as one of {@link claimEventReasonSchema}'s
570
+ * words — so a caller can branch on it. `conflict` means someone holds the
571
+ * row right now and you may queue behind them; `coordination_unavailable`
572
+ * means the coordinator could not answer, so nothing is known about it.
573
+ * Those are different decisions, and a free string made them one.
574
+ *
575
+ * The wire spelling is frozen and an older server may send a word not listed
576
+ * there, so this reads the way {@link wireParticipantKindSchema} reads its
577
+ * dialect: a value that parses becomes the typed reason, and one that does
578
+ * not is absent rather than smuggled through as prose. Prose has its own
579
+ * field — `policyReason` below — which is why nothing is lost by refusing to
580
+ * carry it here.
581
+ *
582
+ * The registry code a caller finally sees on `AbloClaimedError`
583
+ * (`claim_conflict`) stays a separate vocabulary, mapped at the throw. Two
584
+ * small vocabularies with one mapping point beat one vocabulary and a
585
+ * projection of it that has to be maintained as the registry grows.
586
+ */
587
+ reason: z.preprocess(
588
+ (value) => (claimEventReasonSchema.safeParse(value).success ? value : undefined),
589
+ claimEventReasonSchema.optional(),
590
+ ),
591
+ target: targetRefSchema.optional(),
592
+ heldBy: z.string().optional(),
593
+ /**
594
+ * Whether the holder blocking this claim is a person, an agent, or the
595
+ * system. The server already derives this from the holder's id when it
596
+ * builds the conflict; carrying it means a caller can decide how to respond
597
+ * — yield to a person, queue behind an agent — without parsing an opaque id
598
+ * for a prefix and guessing. Additive: an older server omits it.
599
+ */
600
+ heldByKind: wireParticipantKindSchema.optional(),
601
+ heldByClaimId: z.string().optional(),
602
+ heldByExpiresAt: z.number().optional(),
603
+ heldByClaim: wireClaimSummarySchema.optional(),
604
+ policyReason: z.string().optional(),
605
+ });
606
+ export type ClaimRejection = z.infer<typeof claimRejectionSchema>;
607
+
608
+ /**
609
+ * The point-to-point notification sent to a holder whose lease ended without
610
+ * a successful commit. This remains a wire-shaped target because it arrives
611
+ * directly from the WebSocket; the schema is the single validation boundary
612
+ * before the event reaches public `claims.onLost` listeners.
613
+ */
614
+ export const claimLostSchema = z.object({
615
+ claimId: z.string(),
616
+ reason: z.enum(['expired', 'preempted']),
617
+ target: targetRefSchema,
618
+ });
619
+ export type ClaimLost = z.infer<typeof claimLostSchema>;
620
+
621
+ /**
622
+ * The lease is ours without waiting — the target was free when the claim
623
+ * arrived. `fenceToken` is present whenever the coordinator minted one, and a
624
+ * write carries it back so a lapsed lease cannot apply late.
625
+ */
626
+ export const claimAcquiredSchema = z.object({
627
+ claimId: z.string(),
628
+ fenceToken: z.number().optional(),
629
+ target: targetRefSchema,
630
+ });
631
+ export type ClaimAcquired = z.infer<typeof claimAcquiredSchema>;
632
+
633
+ /**
634
+ * A queued claim reached the head of the line and the lease is now ours. The
635
+ * shape matches {@link claimAcquiredSchema} exactly — the two frames differ
636
+ * only in whether the caller waited — but they stay separate declarations
637
+ * because they are separate wire contracts, and collapsing them would let a
638
+ * change to one silently redefine the other.
639
+ */
640
+ export const claimGrantedSchema = z.object({
641
+ claimId: z.string(),
642
+ fenceToken: z.number().optional(),
643
+ target: targetRefSchema,
644
+ });
645
+ export type ClaimGranted = z.infer<typeof claimGrantedSchema>;
646
+
647
+ /**
648
+ * Our claim is waiting in line behind a live holder — the same conflict
649
+ * {@link claimRejectionSchema} reports, delivered as a wait rather than a
650
+ * refusal, plus the caller's `position`.
651
+ *
652
+ * `reason` is the one member that does not carry over as required. A refusal
653
+ * states why it refused; a wait has only ever named the conflict through
654
+ * `heldBy`/`heldByClaim`, and no server has ever stamped `reason` on this
655
+ * frame. Requiring it here — inherited silently by extending the rejection
656
+ * schema — made the parse boundary reject every genuine `claim_queued` as
657
+ * malformed the moment frame validation went in. Optional is what the wire
658
+ * actually is, and it stays derived from the rejection field so the two cannot
659
+ * describe the value differently.
660
+ *
661
+ * `position` is advisory: a privileged reorder can move it up, so a caller
662
+ * that asserts monotonic position will fail in production. Only the arrival of
663
+ * a grant is authoritative.
664
+ */
665
+ export const claimQueuedSchema = claimRejectionSchema.extend({
666
+ position: z.number(),
667
+ reason: claimRejectionSchema.shape.reason.optional(),
668
+ });
669
+ export type ClaimQueued = z.infer<typeof claimQueuedSchema>;
670
+
671
+ /**
672
+ * One entry in a wait-line snapshot.
673
+ *
674
+ * NOTE — this is the third spelling of a target locator on the wire: here it is
675
+ * `{ type, id }`, the HTTP claim DTO uses `{ model, id }`
676
+ * ({@link modelTargetSchema}), and the claim frames use
677
+ * `{ entityType, entityId }` ({@link targetRefSchema}). The schema describes
678
+ * what the server sends today rather than what it should send; unifying the
679
+ * three is a coordinated protocol change scheduled behind the protocol version.
680
+ * Until it happens, the translation between the spellings lives in one place —
681
+ * `wireTarget`, `modelTarget` and `streamTarget` in ./locator.ts — so no hop
682
+ * gets to invent a fourth.
683
+ */
684
+ export const claimQueueEntrySchema = z.object({
685
+ object: z.literal('claim'),
686
+ id: z.string(),
687
+ status: z.literal('queued'),
688
+ target: streamTargetSchema,
689
+ /**
690
+ * Peer-visible description of the work. A claim may be declared without one,
691
+ * and the public `Claim` promises the field is always there — so the default
692
+ * lives here, applied as the frame is decoded, rather than being restated by
693
+ * each reader.
694
+ */
695
+ description: z.string().default('editing'),
696
+ heldBy: z.string().optional(),
697
+ participantKind: wireParticipantKindSchema.optional(),
698
+ position: z.number(),
699
+ expiresAt: z.number(),
700
+ });
701
+ export type ClaimQueueEntry = z.infer<typeof claimQueueEntrySchema>;
702
+
703
+ /**
704
+ * The whole wait line for one row, rebroadcast to that row's peers on every
705
+ * queue mutation. This is what backs the reactive
706
+ * `ablo.<model>.claim.queue({ id })` read, which is why it carries the full
707
+ * line rather than a delta against it.
708
+ */
709
+ export const claimQueueSchema = z.object({
710
+ target: streamTargetSchema.pick({ type: true, id: true }),
711
+ queue: z.array(claimQueueEntrySchema),
712
+ });
713
+ export type ClaimQueue = z.infer<typeof claimQueueSchema>;
714
+
715
+ /**
716
+ * A held claim's TTL lapsed server-side. The claim is already inactive by the
717
+ * time this arrives, so a consumer either re-claims with a fresh credential or
718
+ * accepts the drop; there is nothing to release.
719
+ */
720
+ export const claimExpiredSchema = z.object({
721
+ claimId: z.string(),
722
+ });
723
+ export type ClaimExpired = z.infer<typeof claimExpiredSchema>;
724
+
725
+
726
+ /**
727
+ * What a {@link ModelClaim} points at — the target locator as SDK callers see
728
+ * it, keyed by `model` and `id` rather than the wire schema's `entityType` and
729
+ * `entityId`. This is the public `ModelTarget` shape.
730
+ */
731
+ export const modelTargetSchema = z
732
+ .object({
733
+ model: z.string(),
734
+ id: z.string(),
735
+ field: z.string().optional(),
736
+ /** Several named parts at once — see {@link targetRefSchema}. */
737
+ fields: z.array(z.string()).readonly().optional(),
738
+ meta: z.record(z.string(), z.unknown()).optional(),
739
+ })
740
+ .readonly();
741
+ export type ModelTarget = z.infer<typeof modelTargetSchema>;
742
+
743
+ /**
744
+ * The two states a claim can be observed in while it still exists.
745
+ *
746
+ * Derived from {@link publicClaimStatusSchema} rather than spelled again: the
747
+ * other three are terminal and drop the claim from the observable set, so a
748
+ * listing or a peer's view can only ever see these. Extracting them by name
749
+ * means a state added to the public vocabulary is a deliberate decision about
750
+ * whether it is observable, not a silent omission.
751
+ */
752
+ export const heldClaimStatusSchema = publicClaimStatusSchema.extract([
753
+ 'active',
754
+ 'queued',
755
+ ]);
756
+ export type HeldClaimStatus = z.infer<typeof heldClaimStatusSchema>;
757
+
758
+ /**
759
+ * ONE CLAIM — everything true about a lease at a moment: what it points at, who
760
+ * holds it, what they said they are doing, where it stands, and until when.
761
+ *
762
+ * Every caller-facing surface that answers a question about a claim is a
763
+ * PROJECTION of this record, never a second object: {@link modelClaimSchema} is
764
+ * what a peer may see, and `claimStateSchema` (`wire/claims.ts`) is what a
765
+ * caller polls about a claim of its own. Each is pinned to this record, so a
766
+ * field added here either reaches the people it was declared for or fails to
767
+ * compile.
768
+ *
769
+ * Before this, the peer-visible shape was a standalone `z.object` deriving from
770
+ * nothing, and the polling shape was a third. That is why it took reading four
771
+ * files to answer whether a heartbeat's progress reaches an asker — the
772
+ * declaring surface and the observing surface were kept in step by hand, and
773
+ * three of their shared fields had already drifted on how strictly they parse.
774
+ *
775
+ * What this record deliberately does NOT unify is the socket family
776
+ * ({@link wireClaimSchema} and its base). Those carry the same claim under the
777
+ * `entityType`/`entityId` locator rather than `model`/`id`, and they already
778
+ * derive from one another; collapsing the two locator spellings is a wire
779
+ * rename, not a projection.
780
+ */
781
+ export const claimRecordSchema = z.object({
782
+ /** The claim's identity. Spelled `claimId` where a message names a claim it
783
+ * is not itself, and `id` where the claim is the resource. */
784
+ id: z.string(),
785
+ /** Who holds it. */
786
+ actor: z.string(),
787
+ /** Parsed through {@link wireParticipantKindSchema}, so a legacy `'human'`
788
+ * frame normalizes to `'user'`. */
789
+ participantKind: wireParticipantKindSchema,
790
+ /**
791
+ * On whose authority the holder acts — the same three fields, with the same
792
+ * meanings, that `deltaAttributionSchema` records on every delta the claim
793
+ * goes on to produce, and sourced the same way: off the credential the
794
+ * connection authenticated with, never from the caller.
795
+ *
796
+ * A claim that carries only `actor` can say who is doing something and not
797
+ * who asked for it. "What is agent a7f3 doing" is a debugging question;
798
+ * "what is running on behalf of this customer right now" is an operations
799
+ * question, and until these are here it is answerable only in hindsight,
800
+ * against the audit log, after the fact.
801
+ *
802
+ * Null rather than absent when there is genuinely no delegator or no grant —
803
+ * a person acting directly is their own principal, and a session holds no
804
+ * capability.
805
+ */
806
+ onBehalfOfId: z.string().nullable(),
807
+ onBehalfOfKind: wireParticipantKindSchema.nullable(),
808
+ capabilityId: z.string().nullable(),
809
+ /**
810
+ * What the holder said they are doing (`'rewriting the risk section'`).
811
+ *
812
+ * A caller may declare it as `description`, as the older `reason`, or inside
813
+ * `meta`; {@link claimDescription} resolves those to this one field with a
814
+ * declared precedence, so only one of them is ever a shape.
815
+ */
816
+ description: z.string(),
817
+ /** Holding the row, or waiting in line for it. */
818
+ status: heldClaimStatusSchema,
819
+ /**
820
+ * Place in the wait line. Advisory: a privileged caller can reorder the
821
+ * queue, so a position can go UP between reads — only `status` is
822
+ * authoritative.
823
+ */
824
+ position: z.number().int().nonnegative(),
825
+ /**
826
+ * When the lease lapses without a heartbeat, in epoch milliseconds — the same
827
+ * encoding as the WebSocket {@link WireClaim}, so one timestamp
828
+ * representation spans the wire, the SDK, HTTP, and errors. There is no ISO
829
+ * string anywhere.
830
+ */
831
+ expiresAt: z.number().int(),
832
+ /** The grant's fencing token, minted at acquisition. Present on a claim that
833
+ * is held, never on one that is queued. */
834
+ fenceToken: z.number().int(),
835
+ /** The row, and which part of it. */
836
+ target: modelTargetSchema,
837
+ /**
838
+ * The claim's metadata as an OPEN record — including what the coordinator
839
+ * writes there rather than the holder. A heartbeat's `details` lands here as
840
+ * `progress` (last beat wins), so an asker reads
841
+ * `claim.state({ id })?.meta.progress`. It is presence, not a checkpoint: it
842
+ * dies with the lease.
843
+ *
844
+ * This is the same bag the wire carries; what is new is that it has a home at
845
+ * the CLAIM level. Both projections used to file it under `target` alone, and
846
+ * `target.meta` is typed as the shape the program registered for its own claim
847
+ * metadata — so a server-written key was unreadable there by construction:
848
+ * `target.meta.progress` does not typecheck for any program that declared a
849
+ * shape, and the value was arriving in a slot whose type forbids it.
850
+ *
851
+ * Two views of one field, each typed for its reader: `target.meta` stays the
852
+ * holder's declared shape, and this stays open, because a peer reading someone
853
+ * else's claim has no grounds to assume the writer's declaration.
854
+ */
855
+ meta: z.record(z.string(), z.unknown()).optional(),
856
+ });
857
+ export type ClaimRecord = z.infer<typeof claimRecordSchema>;
858
+
859
+ /**
860
+ * A claim as SDK callers and the HTTP claim routes see it
861
+ * (`ablo.<model>.claim.state`, `GET /v1/claims`) — the resolved, peer-readable
862
+ * view of one active or queued claim. The client's `ModelClaim` type derives
863
+ * from this shape.
864
+ *
865
+ * Everything but the deprecated `field` is projected from
866
+ * {@link claimRecordSchema}. Four members are optional here and required on the
867
+ * record, and the split is the same in each case: the record says what a claim
868
+ * IS, while a peer's view of one may legitimately have been built without them
869
+ * — a queued claim has no `fenceToken`, a held one has no `position`, an older
870
+ * producer sends no `status`, and a claim may be declared with no description.
871
+ */
872
+ export const modelClaimSchema = claimRecordSchema
873
+ .partial({
874
+ description: true,
875
+ status: true,
876
+ position: true,
877
+ fenceToken: true,
878
+ // Additive: a server that predates the delegation trio omits all three,
879
+ // which is a different fact from a claim that has no delegator (null).
880
+ onBehalfOfId: true,
881
+ onBehalfOfKind: true,
882
+ capabilityId: true,
883
+ })
884
+ .extend({
885
+ /**
886
+ * @deprecated Read `target.field` instead, and `target.fields` for a claim
887
+ * on several parts of the row. Removed in 0.36.0.
888
+ *
889
+ * This says the same thing as `target.field` and nothing keeps the two
890
+ * agreeing, so a producer that sets one and not the other publishes a
891
+ * claim that contradicts itself. It also cannot express a field set at
892
+ * all, which is the reason `target.fields` exists.
893
+ */
894
+ field: z.string().optional(),
895
+ })
896
+ .readonly();
897
+ export type ModelClaim = z.infer<typeof modelClaimSchema>;
898
+
899
+ /**
900
+ * The peer-visible view covers the record. A field added to a claim is either
901
+ * projected to the people it was declared for, or deliberately dropped by an
902
+ * `.omit` here — never missing because nobody remembered the second object.
903
+ */
904
+ const _modelClaimCoversRecord: AssertExact<
905
+ Exclude<keyof ModelClaim, 'field'>,
906
+ keyof ClaimRecord
907
+ > = true;
908
+ void _modelClaimCoversRecord;
909
+
910
+ /**
911
+ * The `claim_begin` payload a client sends. It carries the descriptive target
912
+ * and a `description` of the work, an optional duration hint, and the opt-in
913
+ * fair-queue flag. The server stamps the lifecycle and timestamp fields, so they
914
+ * are not part of this inbound shape — this is exactly what the server validates
915
+ * on ingest.
916
+ */
917
+ export const claimBeginPayloadSchema = targetRefSchema.extend({
918
+ claimId: z.string(),
919
+ /** Peer-visible description of the work. The server stamps `'editing'` when a
920
+ * frame carries none. */
921
+ description: z.string().optional(),
922
+ /** Hint for `expiresAt`; the server caps it. */
923
+ estimatedMs: z.number().optional(),
924
+ /**
925
+ * Opt into the fair wait queue. When the target is already held, the server
926
+ * enqueues this claim in FIFO order and replies `claim_queued`, then
927
+ * `claim_granted` later, instead of `claim_rejected`. A client that sets this
928
+ * must be ready to handle the grant.
929
+ */
930
+ queue: z.boolean().optional(),
931
+ });
932
+ export type ClaimBeginPayload = z.infer<typeof claimBeginPayloadSchema>;
933
+
934
+ /**
935
+ * The `claim_abandon` payload a client sends. `entityType` and `entityId` let
936
+ * the server dequeue a claim that is still waiting (not yet held) from the FIFO
937
+ * line; abandoning a claim that is already held needs only `claimId`.
938
+ */
939
+ export const claimAbandonPayloadSchema = z.object({
940
+ claimId: z.string(),
941
+ entityType: z.string().optional(),
942
+ entityId: z.string().optional(),
943
+ });
944
+ export type ClaimAbandonPayload = z.infer<typeof claimAbandonPayloadSchema>;
945
+
946
+ /**
947
+ * The `claim_reorder` payload a client sends. A privileged participant, such as
948
+ * a supervisor over its sub-agents, re-ranks the FIFO wait queue for an entity:
949
+ * `order` lists waiters by `heldBy` and `claimId` in the desired priority, and
950
+ * any waiter not listed keeps its relative order behind those that are. The
951
+ * server gates who may call this and drops an unauthorized sender. Where
952
+ * `claim_abandon` acts on the caller's own entry, a reorder acts on other
953
+ * participants' queue positions — which is why it is gated.
954
+ */
955
+ export const claimReorderPayloadSchema = z.object({
956
+ entityType: z.string(),
957
+ entityId: z.string(),
958
+ order: z.array(z.object({ heldBy: z.string(), claimId: z.string() })),
959
+ });
960
+ export type ClaimReorderPayload = z.infer<typeof claimReorderPayloadSchema>;
961
+
962
+ // ─────────────────────────────────────────────────────────────────────────
963
+ // Heartbeat — the async / long-running-work surface of a claim.
964
+ //
965
+ // A claim's TTL is crash cleanup, not a work-duration estimate. Work that
966
+ // outlives it — an agent run, a background worker's job — keeps its lease by
967
+ // BEATING: request `claim_heartbeat`, reply `claim_heartbeat_ack`. One field
968
+ // set serves every shape; the single and batched payloads are both derived
969
+ // from it, and the WebSocket frame and HTTP routes are two encodings of the
970
+ // same messages. Everything long-running-work-related on the wire lives in
971
+ // this block.
972
+ // ─────────────────────────────────────────────────────────────────────────
973
+
974
+ /**
975
+ * The one field set behind every heartbeat message. The single-claim payload
976
+ * refines it; the batched payload picks from it — there is deliberately no
977
+ * second shape to keep in sync.
978
+ */
979
+ const claimHeartbeatFieldsSchema = z.object({
980
+ claimId: z.string().optional(),
981
+ entityType: z.string().optional(),
982
+ entityId: z.string().optional(),
983
+ /** Requested extension from now; the server clamps it, and an extension
984
+ * never shortens a lease. */
985
+ ttlMs: z.number().positive().optional(),
986
+ /**
987
+ * Lightweight progress the beat carries along ("42/100 pages") — stored
988
+ * as the claim's `meta.progress` (last beat wins) and peer-visible via
989
+ * `claim.state` while the lease is held. This is presence, not a
990
+ * checkpoint: it dies with the lease. Crash-recoverable progress belongs
991
+ * in the data itself — write a row, and every subscriber already sees it.
992
+ */
993
+ details: z.record(z.string(), z.unknown()).optional(),
994
+ });
995
+
996
+ /**
997
+ * The `claim_heartbeat` payload a client sends to extend a lease it holds (or
998
+ * refresh its slot in the wait queue) past the liveness window — the
999
+ * work-duration signal for long-running holders, distinct from the connection
1000
+ * keepalive.
1001
+ *
1002
+ * The claim is identified either way: by `claimId`, or — since a claim is
1003
+ * singular per (actor, entity) — by the full `entityType`/`entityId` target
1004
+ * ("my claim on this row"). At least one of the two must be present. The
1005
+ * target also lets the server resolve without a scan and is required to
1006
+ * refresh a *queued* claim (a waiter is not in the holder set the server
1007
+ * would otherwise search).
1008
+ */
1009
+ export const claimHeartbeatPayloadSchema = claimHeartbeatFieldsSchema.refine(
1010
+ (payload) =>
1011
+ payload.claimId !== undefined ||
1012
+ (payload.entityType !== undefined && payload.entityId !== undefined),
1013
+ {
1014
+ message:
1015
+ 'a heartbeat must identify its claim — pass claimId, or entityType and entityId together',
1016
+ },
1017
+ );
1018
+ export type ClaimHeartbeatPayload = z.infer<typeof claimHeartbeatPayloadSchema>;
1019
+
1020
+ /**
1021
+ * The server's reply to a `claim_heartbeat`. For a socketless worker the
1022
+ * heartbeat reply is the only inbound signal path, so it carries the lease's
1023
+ * fate rather than a bare ok: `held` (extended to `expiresAt`), `queued`
1024
+ * (slot refreshed; `position` is the current place in line), or `lost` (the
1025
+ * lease expired and the queue moved on — the worker should abandon or
1026
+ * re-queue, and any write it still attempts is caught by its `readAt` guard).
1027
+ */
1028
+ export const claimHeartbeatAckPayloadSchema = z.object({
1029
+ claimId: z.string(),
1030
+ status: z.enum(['held', 'queued', 'lost']),
1031
+ expiresAt: z.number().optional(),
1032
+ position: z.number().optional(),
1033
+ /**
1034
+ * How many participants are waiting in line behind a held lease — the
1035
+ * cooperative-yield pressure signal (present on `held`). A worker that can
1036
+ * checkpoint may choose to release early when others wait. Hard
1037
+ * cancellation needs no extra field: a preempted, expired, or revoked
1038
+ * lease answers the next beat with `lost`.
1039
+ */
1040
+ queueDepth: z.number().optional(),
1041
+ });
1042
+ export type ClaimHeartbeatAckPayload = z.infer<typeof claimHeartbeatAckPayloadSchema>;
1043
+
1044
+ /**
1045
+ * The batched heartbeat — one request extends every lease the caller holds
1046
+ * on its plane (the socketless twin of the WebSocket keepalive, which renews
1047
+ * all held leases on every ping). For a worker holding many rows this is one
1048
+ * round trip per cadence instead of one per claim. Queued slots are not
1049
+ * batch-refreshed: a waiter knows its target and beats it directly.
1050
+ */
1051
+ export const claimHeartbeatBatchPayloadSchema = claimHeartbeatFieldsSchema.pick(
1052
+ { ttlMs: true },
1053
+ );
1054
+ export type ClaimHeartbeatBatchPayload = z.infer<
1055
+ typeof claimHeartbeatBatchPayloadSchema
1056
+ >;
1057
+
1058
+ /** Reply to a batched heartbeat: one ack entry per lease that was extended. */
1059
+ export const claimHeartbeatBatchAckPayloadSchema = z.object({
1060
+ results: z.array(claimHeartbeatAckPayloadSchema),
1061
+ });
1062
+ export type ClaimHeartbeatBatchAckPayload = z.infer<
1063
+ typeof claimHeartbeatBatchAckPayloadSchema
1064
+ >;
1065
+
1066
+ // ─────────────────────────────────────────────────────────────────────────
1067
+ // Read interest — what a connection receives
1068
+ //
1069
+ // Two frames set it, and they differ in exactly one way: whether the
1070
+ // interest is leased.
1071
+ //
1072
+ // • `claim` — a PARTICIPANT claim. Adds a scope under a handle, with a
1073
+ // TTL and an optional capability token, and announces the sender into
1074
+ // that scope's roster. This is the frame `ablo.<model>.join(...)` sends;
1075
+ // `release` drops it. Several may be open on one connection at once.
1076
+ // • `update_subscription` — REPLACES the connection's whole read set. No
1077
+ // handle, no lease, no roster entry.
1078
+ //
1079
+ // Both are bounded by the connection credential's grant, and both name their
1080
+ // groups the same way, so both parse their `syncGroups` through the same
1081
+ // element schema. Neither is the row lease — that is `claim_begin`, in the
1082
+ // pessimistic-claims block above, which shares only a word.
1083
+ // ─────────────────────────────────────────────────────────────────────────
1084
+
1085
+ /**
1086
+ * How many scopes one frame may name. A coarse abuse ceiling, not a business
1087
+ * limit: a connection legitimately watches a handful of entities, and a list
1088
+ * this long is an amplification attempt rather than a workload. Declared here,
1089
+ * beside the two frames it bounds, so neither can be given a different answer.
1090
+ */
1091
+ export const MAX_FRAME_SYNC_GROUPS = 200;
1092
+
1093
+ /**
1094
+ * The sync groups a scope-subscription frame names. Each entry is a
1095
+ * {@link syncGroupInputSchema} (`'default'` or a branded `kind:id`), so a
1096
+ * malformed group is rejected on ingest rather than silently indexed — a group
1097
+ * that does not parse matches nothing, and subscribing to nothing quietly is
1098
+ * the failure this element type exists to prevent.
1099
+ *
1100
+ * Strict because this is untrusted client input, and shared because the two
1101
+ * frames below carry the same value: when they disagreed, `claim` accepted a
1102
+ * malformed group that `update_subscription` refused, and the connection
1103
+ * ended up leased to a scope it could never receive.
1104
+ */
1105
+ const frameSyncGroupsSchema = z
1106
+ .array(syncGroupInputSchema)
1107
+ .max(MAX_FRAME_SYNC_GROUPS);
1108
+
1109
+ /**
1110
+ * The `claim` payload a client sends — the frame behind `join`.
1111
+ *
1112
+ * It opens one participant claim: the connection is added to each named
1113
+ * scope's fan-out under `claimId`, announced into its presence roster, and
1114
+ * holds that interest until `release`, the TTL lapses, or the socket closes.
1115
+ * The handle is client-chosen because the client must be able to `release`
1116
+ * the exact claim it opened while others stay open.
1117
+ *
1118
+ * This shape was, for a long time, written three times — built as a literal in
1119
+ * the transport, restated as an interface on the server, and read back through
1120
+ * a cast in the frame handler — which is how the frame came to be the only
1121
+ * coordination message with no runtime check on the way in.
1122
+ */
1123
+ export const participantClaimPayloadSchema = z.object({
1124
+ /** Client-chosen handle. Echoed on `claim_ack`; names the claim to `release`. */
1125
+ claimId: z.string().min(1),
1126
+ syncGroups: frameSyncGroupsSchema,
1127
+ /**
1128
+ * A narrower capability to present for this claim than the connection's own.
1129
+ * Absent means the connection's credential governs it.
1130
+ */
1131
+ capabilityToken: z.string().optional(),
1132
+ /**
1133
+ * Crash cleanup, in seconds. The server caps it at the capability's own TTL;
1134
+ * absent means the claim lives until `release` or disconnect.
1135
+ */
1136
+ ttlSeconds: z.number().optional(),
1137
+ });
1138
+ export type ParticipantClaimPayload = z.infer<
1139
+ typeof participantClaimPayloadSchema
1140
+ >;
1141
+
1142
+ /**
1143
+ * The `release` payload — drop one participant claim by its handle.
1144
+ *
1145
+ * A projection of the claim it releases rather than a second object, so the
1146
+ * handle cannot be spelled one way when opened and another when dropped.
1147
+ * Idempotent by contract: the server accepts an unknown handle silently, so a
1148
+ * client releasing everything at shutdown never has to check what is still open.
1149
+ */
1150
+ export const participantReleasePayloadSchema =
1151
+ participantClaimPayloadSchema.pick({ claimId: true });
1152
+ export type ParticipantReleasePayload = z.infer<
1153
+ typeof participantReleasePayloadSchema
1154
+ >;
1155
+
1156
+ /**
1157
+ * The `update_subscription` payload a client sends. It replaces the
1158
+ * connection's read interest with the complete set of sync groups — the
1159
+ * unleased counterpart to {@link participantClaimPayloadSchema}, with no
1160
+ * handle, no TTL, and no roster entry.
1161
+ */
1162
+ export const updateSubscriptionPayloadSchema = z.object({
1163
+ syncGroups: frameSyncGroupsSchema,
1164
+ });
1165
+ export type UpdateSubscriptionPayload = z.infer<
1166
+ typeof updateSubscriptionPayloadSchema
1167
+ >;
1168
+
1169
+ /**
1170
+ * `subscription_ack` payload (server → client). Echoes the connection's
1171
+ * effective read set after the update (unchanged on rejection — the update is
1172
+ * atomic). `error` is present iff `success` is false (e.g. a scoped key
1173
+ * requesting a group outside its grant). `syncGroups` is lenient
1174
+ * (`z.string()`) here, not branded: it is the server's own echo for display,
1175
+ * not untrusted input, and includes base anchors like `org:<id>`.
1176
+ */
1177
+ export const subscriptionAckPayloadSchema = z.object({
1178
+ success: z.boolean(),
1179
+ syncGroups: z.array(z.string()),
1180
+ error: z.object({ code: z.string(), message: z.string() }).optional(),
1181
+ });
1182
+ export type SubscriptionAckPayload = z.infer<
1183
+ typeof subscriptionAckPayloadSchema
1184
+ >;
1185
+
1186
+ // ─────────────────────────────────────────────────────────────────────────
1187
+ // Commit operation — carries the optimistic write-guard (Layer 3)
1188
+ // ─────────────────────────────────────────────────────────────────────────
1189
+
1190
+ export const commitOperationTypeSchema = z.enum([
1191
+ 'CREATE',
1192
+ 'UPDATE',
1193
+ 'DELETE',
1194
+ 'ARCHIVE',
1195
+ 'UNARCHIVE',
1196
+ ]);
1197
+ export type CommitOperationType = z.infer<typeof commitOperationTypeSchema>;
1198
+
1199
+ /**
1200
+ * A single mutation in a commit batch, as it arrives on the wire. Extends the
1201
+ * optimistic `writeGuard` (`readAt`/`onStale`/`bypass`) — the structural link
1202
+ * that makes "every write is stale-guarded" legible in the type, not just in
1203
+ * prose.
1204
+ */
1205
+ export const commitOperationSchema = writeGuardSchema.extend({
1206
+ type: commitOperationTypeSchema,
1207
+ model: z.string(),
1208
+ id: z.string().nullish(),
1209
+ input: z.record(z.string(), z.unknown()).nullish(),
1210
+ /** Per-op client tx id, echoed on the broadcast delta. */
1211
+ transactionId: z.string().nullish(),
1212
+ /**
1213
+ * The fencing token from the held claim this write belongs to (Option B).
1214
+ * Present only on a write issued under a claim that was granted one; the
1215
+ * server checks it against the entity's persisted high-water and rejects a
1216
+ * stale token. Absent (nullish) on every unclaimed write — those are governed
1217
+ * by version-CAS and the Option A blind-write guard, unchanged.
1218
+ */
1219
+ fenceToken: z.number().nullish(),
1220
+ });
1221
+ export type CommitOperation = z.infer<typeof commitOperationSchema>;
1222
+
1223
+ /**
1224
+ * Any commit operation on the wire — the runtime-validated ingest contract.
1225
+ * Commit operations carry replace (last-write-wins) semantics, guarded by the
1226
+ * optimistic write guard. It is a distinct alias from {@link CommitOperation}
1227
+ * so the server's ingest boundary reads as "any op on the wire", even though
1228
+ * the two shapes are identical.
1229
+ */
1230
+ export type AnyCommitOperation = CommitOperation;
1231
+
1232
+ // ─────────────────────────────────────────────────────────────────────────
1233
+ // Layer 1 — presence (observation only; it never enforces)
1234
+ // ─────────────────────────────────────────────────────────────────────────
1235
+
1236
+ export const presenceKindSchema = z.enum(['enter', 'update', 'leave']);
1237
+ export type PresenceKind = z.infer<typeof presenceKindSchema>;
1238
+
1239
+ /**
1240
+ * What a participant is actively working on (agents fill this in).
1241
+ *
1242
+ * The two backpressure fields are part of the frame, not an extension of it:
1243
+ * an agent worker announces them on every step, and an orchestrator reading
1244
+ * peer activity routes work by them. They are declared here because a reader
1245
+ * that validates this frame would otherwise drop them on the floor — the
1246
+ * server passes both through without interpreting either.
1247
+ */
1248
+ export const presenceActivitySchema = targetRefSchema.extend({
1249
+ action: z.string(),
1250
+ detail: z.string().optional(),
1251
+ /** Backpressure signal in `[0, 1]`: `0` idle, `1` at capacity. */
1252
+ loadFactor: z.number().optional(),
1253
+ /** Gate for new assignments; absent means yes. */
1254
+ acceptingNewWork: z.boolean().optional(),
1255
+ });
1256
+ export type PresenceActivity = z.infer<typeof presenceActivitySchema>;
1257
+
1258
+ /**
1259
+ * Full `presence_update` frame as the server broadcasts it. The activity +
1260
+ * `activeClaims` are the observation surface for the other two layers —
1261
+ * rendered, never acted on as enforcement.
1262
+ *
1263
+ * Open for the same reason {@link presenceUpdatePayloadSchema} is, and it has to
1264
+ * be the same in both directions: whatever vocabulary an application announces
1265
+ * through presence, it reads back off its peers' frames. A reader that parsed
1266
+ * this strictly would validate the frame and quietly discard the part the
1267
+ * application actually came for.
1268
+ */
1269
+ export const presenceUpdateSchema = z.object({
1270
+ kind: presenceKindSchema,
1271
+ /**
1272
+ * Who the frame is about. Required, because every one of the five sites that
1273
+ * builds a presence frame stamps it from the connection's identity — an
1274
+ * anonymous presence frame has never been sent and would say nothing. It was
1275
+ * optional here for as long as nothing parsed the frame, and the hand-written
1276
+ * copy the transport used to carry declared it required; two descriptions of
1277
+ * one frame can disagree indefinitely while neither is ever checked.
1278
+ */
1279
+ userId: z.string(),
1280
+ syncGroups: z.array(z.string()).optional(),
1281
+ timestamp: z.number().optional(),
1282
+ status: z.string(),
1283
+ timezone: z.string().optional(),
1284
+ customStatus: z.string().optional(),
1285
+ activity: presenceActivitySchema.optional(),
1286
+ isAgent: z.boolean().optional(),
1287
+ /**
1288
+ * Server-stamped canonical kind. Additive — older servers omit it and
1289
+ * readers fall back to `isAgent` (see {@link participantKindFromWire}).
1290
+ */
1291
+ participantKind: wireParticipantKindSchema.optional(),
1292
+ activeClaims: z.array(wireClaimSchema).optional(),
1293
+ delegatedFrom: z.string().nullish(),
1294
+ }).catchall(z.unknown());
1295
+ export type PresenceUpdate = z.infer<typeof presenceUpdateSchema>;
1296
+
1297
+ /**
1298
+ * @deprecated Renamed to {@link presenceUpdateSchema}. Removed in 0.36.0.
1299
+ *
1300
+ * `Frame` was the only such suffix in this vocabulary: every other frame the
1301
+ * server sends is named plainly — {@link claimLostSchema},
1302
+ * {@link claimAcquiredSchema}, {@link claimRejectionSchema} — and the client's
1303
+ * half carries `Payload`. One name did not follow the rule the other fifteen do.
1304
+ */
1305
+ export const presenceUpdateFrameSchema = presenceUpdateSchema;
1306
+ /** @deprecated Renamed to {@link PresenceUpdate}. Removed in 0.36.0. */
1307
+ export type PresenceUpdateFrame = PresenceUpdate;
1308
+
1309
+ /**
1310
+ * The `presence_update` payload a client SENDS — deliberately much smaller
1311
+ * than the frame the server broadcasts back.
1312
+ *
1313
+ * Everything that identifies or situates the participant is stamped by the
1314
+ * server and cannot be declared here: `userId`, `participantKind`, `isAgent`,
1315
+ * `syncGroups`, `timestamp`, `kind`, and `delegatedFrom` all come from the
1316
+ * connection's own identity. A client that sends them is not believed — an
1317
+ * older SDK once hardcoded `isAgent: true` on every announce, and because the
1318
+ * payload was spread into the broadcast unfiltered, every human session
1319
+ * rendered to its peers as an agent. Parsing an inbound payload through this
1320
+ * schema and broadcasting the *result* is what makes that structurally
1321
+ * impossible rather than a rule the broadcast has to remember.
1322
+ *
1323
+ * `status` is a plain string, matching the outbound frame: the three canonical
1324
+ * values are conventions the presence UI understands, not a closed set the
1325
+ * protocol enforces.
1326
+ *
1327
+ * The payload is deliberately OPEN — `catchall` keeps keys this schema does not
1328
+ * name. Presence is the one frame an application extends: an agent mesh
1329
+ * announces its own coordination vocabulary through it and reads it back off
1330
+ * peer frames, without the protocol having to learn each app's words. So the
1331
+ * fields named here are validated and typed, and anything else rides along
1332
+ * untouched. Openness is not the same as trust: the server-stamped identity
1333
+ * fields are applied AFTER this payload is spread into the broadcast, so a
1334
+ * client that sends its own `userId` or `isAgent` is overwritten either way.
1335
+ */
1336
+ export const presenceUpdatePayloadSchema = z
1337
+ .object({
1338
+ status: z.string().optional(),
1339
+ activity: presenceActivitySchema.optional(),
1340
+ /** The sender's own open claims, which replace what the server holds. */
1341
+ activeClaims: z.array(wireClaimSchema).optional(),
1342
+ timezone: z.string().optional(),
1343
+ customStatus: z.string().optional(),
1344
+ })
1345
+ .catchall(z.unknown());
1346
+ export type PresenceUpdatePayload = z.infer<typeof presenceUpdatePayloadSchema>;