@abloatai/ablo 0.35.0 → 0.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/CHANGELOG.md +713 -629
  2. package/README.md +56 -519
  3. package/bin/ablo.cjs +39 -0
  4. package/dist/BaseSyncedStore.d.ts +24 -4
  5. package/dist/BaseSyncedStore.js +53 -37
  6. package/dist/Database.d.ts +8 -20
  7. package/dist/Database.js +61 -59
  8. package/dist/InstanceCache.d.ts +6 -2
  9. package/dist/InstanceCache.js +18 -16
  10. package/dist/Model.d.ts +17 -7
  11. package/dist/Model.js +17 -7
  12. package/dist/ModelRegistry.d.ts +4 -0
  13. package/dist/ModelRegistry.js +17 -15
  14. package/dist/NetworkMonitor.d.ts +3 -1
  15. package/dist/NetworkMonitor.js +7 -5
  16. package/dist/SyncClient.d.ts +5 -15
  17. package/dist/SyncClient.js +60 -57
  18. package/dist/client/Ablo.js +30 -19
  19. package/dist/client/createInternalComponents.d.ts +4 -0
  20. package/dist/client/createInternalComponents.js +8 -2
  21. package/dist/client/createModelProxy.d.ts +21 -1
  22. package/dist/client/createModelProxy.js +123 -57
  23. package/dist/client/humans.d.ts +30 -9
  24. package/dist/client/humans.js +45 -19
  25. package/dist/client/reactiveEngine.d.ts +16 -11
  26. package/dist/client/reactiveEngine.js +113 -335
  27. package/dist/client/storeCluster.d.ts +47 -0
  28. package/dist/client/storeCluster.js +118 -0
  29. package/dist/client/storeLifecycle.d.ts +61 -0
  30. package/dist/client/storeLifecycle.js +231 -0
  31. package/dist/context.d.ts +13 -0
  32. package/dist/context.js +23 -0
  33. package/dist/core/index.d.ts +1 -1
  34. package/dist/docs/catalog.js +6 -3
  35. package/dist/index.d.ts +4 -2
  36. package/dist/index.js +4 -2
  37. package/dist/query/client.d.ts +3 -0
  38. package/dist/query/client.js +6 -5
  39. package/dist/react/AbloProvider.d.ts +13 -1
  40. package/dist/react/AbloProvider.js +5 -2
  41. package/dist/react/context.d.ts +2 -2
  42. package/dist/react/createAbloReact.d.ts +56 -0
  43. package/dist/react/createAbloReact.js +51 -0
  44. package/dist/react/index.d.ts +1 -0
  45. package/dist/react/index.js +3 -0
  46. package/dist/react/useAblo.d.ts +9 -2
  47. package/dist/react/useAblo.js +25 -7
  48. package/dist/schema/coordination.js +5 -1
  49. package/dist/schema/index.d.ts +1 -0
  50. package/dist/schema/index.js +4 -0
  51. package/dist/schema/select.js +3 -0
  52. package/dist/schema/serialize.js +3 -0
  53. package/dist/source/adapter.d.ts +7 -5
  54. package/dist/source/adapter.js +7 -5
  55. package/dist/source/index.d.ts +1 -1
  56. package/dist/source/index.js +1 -1
  57. package/dist/{core/storeContract.d.ts → storeContract.d.ts} +5 -5
  58. package/dist/{core → stores}/DatabaseManager.d.ts +3 -1
  59. package/dist/{core → stores}/DatabaseManager.js +13 -12
  60. package/dist/{core → stores}/StoreManager.d.ts +5 -3
  61. package/dist/{core → stores}/StoreManager.js +24 -22
  62. package/dist/stores/SyncActionStore.d.ts +3 -1
  63. package/dist/stores/SyncActionStore.js +9 -7
  64. package/dist/sync/BootstrapFetcher.d.ts +4 -0
  65. package/dist/sync/BootstrapFetcher.js +28 -26
  66. package/dist/sync/OnDemandLoader.d.ts +3 -0
  67. package/dist/sync/OnDemandLoader.js +1 -0
  68. package/dist/sync/bootstrapApply.d.ts +3 -0
  69. package/dist/sync/bootstrapApply.js +2 -2
  70. package/dist/sync/deltaPipeline.d.ts +13 -12
  71. package/dist/sync/deltaPipeline.js +21 -4
  72. package/dist/sync/groupChange.d.ts +3 -0
  73. package/dist/sync/groupChange.js +16 -14
  74. package/dist/sync/participants.d.ts +19 -2
  75. package/dist/sync/participants.js +3 -1
  76. package/dist/sync/schemas.d.ts +2 -1
  77. package/dist/sync/schemas.js +3 -3
  78. package/dist/syncLog/contract.d.ts +20 -0
  79. package/dist/syncLog/contract.js +19 -0
  80. package/dist/syncLog/index.d.ts +1 -0
  81. package/dist/syncLog/index.js +1 -0
  82. package/dist/transaction/auth/capability.d.ts +35 -0
  83. package/dist/transaction/auth/capability.js +25 -0
  84. package/dist/transaction/coordination/awaitClaimGrant.d.ts +7 -0
  85. package/dist/transaction/coordination/awaitClaimGrant.js +12 -0
  86. package/dist/transaction/coordination/claimHeartbeatLoop.d.ts +34 -0
  87. package/dist/transaction/coordination/claimHeartbeatLoop.js +20 -0
  88. package/dist/transaction/coordination/index.d.ts +4 -4
  89. package/dist/transaction/coordination/index.js +4 -3
  90. package/dist/transaction/coordination/locator.d.ts +23 -2
  91. package/dist/transaction/coordination/locator.js +22 -2
  92. package/dist/transaction/coordination/schema.d.ts +125 -62
  93. package/dist/transaction/coordination/schema.js +228 -64
  94. package/dist/transaction/coordination/targetConflict.js +32 -28
  95. package/dist/transaction/errorCodes.d.ts +2 -2
  96. package/dist/transaction/errorCodes.js +13 -9
  97. package/dist/transaction/plugin.d.ts +95 -2
  98. package/dist/transaction/plugin.js +21 -2
  99. package/dist/transaction/resources/httpResources.d.ts +57 -2
  100. package/dist/transaction/resources/modelOperations.d.ts +148 -40
  101. package/dist/transaction/resources/where.d.ts +16 -0
  102. package/dist/transaction/resources/where.js +45 -0
  103. package/dist/transaction/schema/field.d.ts +12 -18
  104. package/dist/transaction/schema/fieldRef.d.ts +38 -0
  105. package/dist/transaction/schema/fieldRef.js +11 -0
  106. package/dist/transaction/schema/openapi.d.ts +16 -15
  107. package/dist/transaction/schema/openapi.js +186 -25
  108. package/dist/transaction/schema/relation.d.ts +7 -2
  109. package/dist/transaction/schema/schema.d.ts +27 -0
  110. package/dist/transaction/schema/schema.js +20 -0
  111. package/dist/transaction/transactions/settlement/commitEnvelope.d.ts +1 -1
  112. package/dist/transaction/transactions/settlement/pendingWrite.d.ts +1 -1
  113. package/dist/transaction/transport/httpClient.d.ts +9 -1
  114. package/dist/transaction/transport/httpClient.js +1 -0
  115. package/dist/transaction/transport/httpTransport.js +156 -44
  116. package/dist/transaction/transport/wsTransport.d.ts +2 -4
  117. package/dist/transaction/transport/wsTransport.js +16 -10
  118. package/dist/transaction/types/streams.d.ts +10 -0
  119. package/dist/transaction/utils/duration.d.ts +25 -0
  120. package/dist/transaction/utils/duration.js +32 -0
  121. package/dist/transaction/wire/accountResponses.d.ts +69 -0
  122. package/dist/transaction/wire/accountResponses.js +36 -1
  123. package/dist/transaction/wire/auth.d.ts +9 -2
  124. package/dist/transaction/wire/auth.js +7 -1
  125. package/dist/transaction/wire/claims.d.ts +164 -97
  126. package/dist/transaction/wire/claims.js +126 -28
  127. package/dist/transaction/wire/commit.d.ts +1 -1
  128. package/dist/transaction/wire/feedEvent.d.ts +27 -0
  129. package/dist/transaction/wire/feedEvent.js +27 -1
  130. package/dist/transaction/wire/frames.d.ts +2 -2
  131. package/dist/transaction/wire/inboundFrames.d.ts +12 -2
  132. package/dist/transaction/wire/index.d.ts +10 -6
  133. package/dist/transaction/wire/index.js +12 -3
  134. package/dist/transaction/wire/modelMutations.d.ts +31 -0
  135. package/dist/transaction/wire/modelMutations.js +52 -0
  136. package/dist/transaction/wire/modelShape.d.ts +78 -0
  137. package/dist/transaction/wire/modelShape.js +74 -0
  138. package/dist/transactions/mutations/MutationQueue.d.ts +6 -0
  139. package/dist/transactions/mutations/MutationQueue.js +55 -45
  140. package/dist/transactions/mutations/commitPayload.d.ts +3 -2
  141. package/dist/transactions/mutations/commitPayload.js +5 -5
  142. package/dist/transactions/mutations/deltaConfirmation.d.ts +4 -0
  143. package/dist/transactions/mutations/deltaConfirmation.js +10 -8
  144. package/dist/transactions/mutations/replayValidation.d.ts +2 -1
  145. package/dist/transactions/mutations/replayValidation.js +3 -2
  146. package/dist/{core → views}/QueryView.d.ts +1 -1
  147. package/dist/{core → views}/QueryView.js +1 -1
  148. package/dist/{core → views}/ViewRegistry.d.ts +1 -1
  149. package/dist/{core/queryUtils.d.ts → views/incrementalView.d.ts} +6 -6
  150. package/dist/{core/queryUtils.js → views/incrementalView.js} +6 -6
  151. package/docs/agents.md +1 -1
  152. package/docs/api-keys.md +6 -6
  153. package/docs/api.md +5 -43
  154. package/docs/audit.md +4 -3
  155. package/docs/cli.md +11 -11
  156. package/docs/client-behavior.md +4 -4
  157. package/docs/concurrency-convention.md +28 -42
  158. package/docs/coordination.md +235 -83
  159. package/docs/data-sources.md +4 -4
  160. package/docs/debugging.md +34 -12
  161. package/docs/deployment.md +8 -8
  162. package/docs/examples/scoped-agent.md +3 -3
  163. package/docs/groups.md +57 -3
  164. package/docs/guarantees.md +37 -10
  165. package/docs/how-it-works.md +29 -5
  166. package/docs/idempotency.md +6 -6
  167. package/docs/identity.md +22 -23
  168. package/docs/index.md +8 -8
  169. package/docs/integration-guide.md +14 -3
  170. package/docs/mcp.md +7 -7
  171. package/docs/migration.md +34 -15
  172. package/docs/projects.md +1 -1
  173. package/docs/react.md +19 -8
  174. package/docs/sessions.md +1 -1
  175. package/docs/webhooks.md +9 -9
  176. package/llms.txt +4 -4
  177. package/package.json +13 -20
  178. package/dist/cli.cjs +0 -288600
  179. package/dist/testing/fixtures/bootstrap.d.ts +0 -49
  180. package/dist/testing/fixtures/bootstrap.js +0 -59
  181. package/dist/testing/fixtures/deltas.d.ts +0 -83
  182. package/dist/testing/fixtures/deltas.js +0 -136
  183. package/dist/testing/fixtures/httpResponses.d.ts +0 -70
  184. package/dist/testing/fixtures/httpResponses.js +0 -90
  185. package/dist/testing/fixtures/models.d.ts +0 -83
  186. package/dist/testing/fixtures/models.js +0 -272
  187. package/dist/testing/helpers/reactWrapper.d.ts +0 -69
  188. package/dist/testing/helpers/reactWrapper.js +0 -67
  189. package/dist/testing/helpers/syncEngineHarness.d.ts +0 -54
  190. package/dist/testing/helpers/syncEngineHarness.js +0 -73
  191. package/dist/testing/helpers/wait.d.ts +0 -30
  192. package/dist/testing/helpers/wait.js +0 -49
  193. package/dist/testing/index.d.ts +0 -23
  194. package/dist/testing/index.js +0 -33
  195. package/dist/testing/mocks/FakeDatabase.d.ts +0 -18
  196. package/dist/testing/mocks/FakeDatabase.js +0 -10
  197. package/dist/testing/mocks/MockMutationExecutor.d.ts +0 -87
  198. package/dist/testing/mocks/MockMutationExecutor.js +0 -186
  199. package/dist/testing/mocks/MockNetworkMonitor.d.ts +0 -20
  200. package/dist/testing/mocks/MockNetworkMonitor.js +0 -46
  201. package/dist/testing/mocks/MockSyncContext.d.ts +0 -51
  202. package/dist/testing/mocks/MockSyncContext.js +0 -72
  203. package/dist/testing/mocks/MockSyncStore.d.ts +0 -88
  204. package/dist/testing/mocks/MockSyncStore.js +0 -171
  205. package/dist/testing/mocks/MockWebSocket.d.ts +0 -71
  206. package/dist/testing/mocks/MockWebSocket.js +0 -118
  207. package/docs/interaction-model.md +0 -99
  208. /package/dist/{core → query}/QueryProcessor.d.ts +0 -0
  209. /package/dist/{core → query}/QueryProcessor.js +0 -0
  210. /package/dist/{core/storeContract.js → storeContract.js} +0 -0
  211. /package/dist/{core → stores}/openIDBWithTimeout.d.ts +0 -0
  212. /package/dist/{core → stores}/openIDBWithTimeout.js +0 -0
  213. /package/dist/{source → transaction}/footprint.d.ts +0 -0
  214. /package/dist/{source → transaction}/footprint.js +0 -0
  215. /package/dist/{core → views}/ViewRegistry.js +0 -0
@@ -53,5 +53,11 @@ export const ephemeralKeyRequestSchema = z.object({
53
53
  * `auth/capability.ts` — the grant is one structure, and its request form is a
54
54
  * face of it rather than a separate shape. Re-exported here so the wire barrel
55
55
  * stays the single import path for the boundary.
56
+ *
57
+ * This is the only import wire makes for something larger than a schema leaf,
58
+ * so it carries exactly the shape the barrel forwards and nothing else: the
59
+ * grant's own vocabulary — the verb enum, the `model.verb` spelling, the
60
+ * helpers that derive one from the other — is read from `auth/capability.ts`
61
+ * by the callers that build a grant, not through here.
56
62
  */
57
- export { capabilityRequestSchema, grantedOperationSchema, capabilityOperationSchema, } from '../auth/capability.js';
63
+ export { capabilityRequestSchema, capabilityMintResponseSchema, } from '../auth/capability.js';
@@ -10,6 +10,37 @@
10
10
  * unvalidated and the reference described the shape from memory.
11
11
  */
12
12
  import { z } from 'zod';
13
+ /**
14
+ * How long a lease runs when the caller does not ask for a length.
15
+ *
16
+ * One definition for both ends: the server stamps `expiresAt` with it, and the
17
+ * client's auto-heartbeat derives its cadence from it, so a claim that names no
18
+ * TTL beats comfortably inside the window the server actually granted.
19
+ */
20
+ export declare const DEFAULT_CLAIM_TTL_MS = 60000;
21
+ /**
22
+ * A lease length on the wire, in the SDK's one duration grammar: a
23
+ * unit-suffixed string (`'500ms'`, `'30s'`, `'5m'`, `'24h'`) or a bare number of
24
+ * **seconds** — the same reading `ttlSeconds` has everywhere else in the API.
25
+ *
26
+ * It used to be `z.union([z.string(), z.number()])`, and the units lived only in
27
+ * the route's private parser, which read a bare number as MILLISECONDS. So the
28
+ * SDK computed its heartbeat cadence from one grammar while the server granted a
29
+ * lease under another, and `ttl: 300` meant five minutes to the client and three
30
+ * hundred milliseconds to the server. Publishing the grammar is what makes that
31
+ * class of disagreement impossible: there is now one pattern, emitted into the
32
+ * contract, and one parser behind it.
33
+ */
34
+ export declare const claimTtlSchema: z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>;
35
+ /**
36
+ * A wire TTL as milliseconds — the single reading of the grammar above.
37
+ *
38
+ * The server calls this rather than carrying its own parser. An absent TTL takes
39
+ * {@link DEFAULT_CLAIM_TTL_MS}; anything present has already been validated by
40
+ * {@link claimTtlSchema} at the boundary, so a malformed duration is a 400 at the
41
+ * door rather than a silent one-minute lease that lapses mid-work.
42
+ */
43
+ export declare function claimTtlMs(ttl: z.infer<typeof claimTtlSchema> | null | undefined): number;
13
44
  /** The row a claim points at, when it is not already given by the URL. */
14
45
  export declare const claimTargetSchema: z.ZodObject<{
15
46
  model: z.ZodOptional<z.ZodString>;
@@ -51,7 +82,7 @@ export declare const claimRequestSchema: z.ZodObject<{
51
82
  reason: z.ZodOptional<z.ZodString>;
52
83
  description: z.ZodOptional<z.ZodString>;
53
84
  meta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
54
- ttl: z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>>;
85
+ ttl: z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>>;
55
86
  queue: z.ZodOptional<z.ZodBoolean>;
56
87
  }, z.core.$strip>;
57
88
  export type ClaimRequest = z.infer<typeof claimRequestSchema>;
@@ -63,7 +94,7 @@ export type ClaimRequest = z.infer<typeof claimRequestSchema>;
63
94
  */
64
95
  export declare const claimHeartbeatRequestSchema: z.ZodObject<{
65
96
  claimId: z.ZodOptional<z.ZodString>;
66
- ttl: z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>>;
97
+ ttl: z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>>;
67
98
  details: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
68
99
  }, z.core.$strip>;
69
100
  export type ClaimHeartbeatRequest = z.infer<typeof claimHeartbeatRequestSchema>;
@@ -113,11 +144,11 @@ export type ListQuery = z.infer<typeof listQuerySchema>;
113
144
  * its own read, the same way the socket path takes it from a local snapshot.
114
145
  */
115
146
  export declare const claimStateSchema: z.ZodObject<{
147
+ id: z.ZodString;
116
148
  expiresAt: z.ZodOptional<z.ZodNumber>;
117
149
  fenceToken: z.ZodOptional<z.ZodNumber>;
118
150
  position: z.ZodOptional<z.ZodNumber>;
119
151
  object: z.ZodLiteral<"claim">;
120
- claimId: z.ZodString;
121
152
  status: z.ZodEnum<{
122
153
  active: "active";
123
154
  committed: "committed";
@@ -136,6 +167,9 @@ export type ClaimState = z.infer<typeof claimStateSchema>;
136
167
  export declare const claimAcquiredResponseSchema: z.ZodObject<{
137
168
  id: z.ZodString;
138
169
  object: z.ZodLiteral<"claim">;
170
+ status: z.ZodLiteral<"active">;
171
+ fenceToken: z.ZodOptional<z.ZodNumber>;
172
+ expiresAt: z.ZodOptional<z.ZodNumber>;
139
173
  claim: z.ZodReadonly<z.ZodObject<{
140
174
  id: z.ZodString;
141
175
  actor: z.ZodString;
@@ -183,13 +217,17 @@ export type ClaimAcquiredResponse = z.infer<typeof claimAcquiredResponseSchema>;
183
217
  * put you in line. The grant arrives later on the caller's stream, or through
184
218
  * `GET /v1/claims/{claimId}`.
185
219
  *
186
- * There is deliberately no `object` field here: a queued claim is not yet a
187
- * created resource, and the status code plus `status: 'queued'` already carry
188
- * the distinction.
220
+ * Shaped as a claim resource, exactly like the 201, because that is what it is:
221
+ * a queue entry is a lease in a different state — the reason one `DELETE`
222
+ * serves both, and the reason the poll can move one to the other. It once
223
+ * withheld `object` and `id` on the argument that a queued claim is not yet a
224
+ * created resource, which read as principled and cost a caller the ability to
225
+ * treat the two answers as one type.
189
226
  */
190
227
  export declare const claimQueuedResponseSchema: z.ZodObject<{
228
+ id: z.ZodString;
229
+ object: z.ZodLiteral<"claim">;
191
230
  status: z.ZodLiteral<"queued">;
192
- claimId: z.ZodString;
193
231
  position: z.ZodNumber;
194
232
  heldBy: z.ZodOptional<z.ZodString>;
195
233
  expiresAt: z.ZodOptional<z.ZodNumber>;
@@ -221,14 +259,21 @@ export declare const claimQueuedResponseSchema: z.ZodObject<{
221
259
  }, z.core.$strip>;
222
260
  export type ClaimQueuedResponse = z.infer<typeof claimQueuedResponseSchema>;
223
261
  /**
224
- * Either answer to a claim acquire. The two arms cannot be a discriminated
225
- * union — the 201 keys on `object` and the 202 on `status`, and neither field
226
- * appears in both — so a caller branches on `status === 'queued'`, which is
227
- * what the HTTP transport does.
262
+ * Either answer to a claim acquire one resource, two states, told apart by
263
+ * `status`.
264
+ *
265
+ * A real discriminated union, which it could not be while the arms disagreed
266
+ * about what the handle was called (`id` on the 201, `claimId` on the 202) and
267
+ * only one of them said `object`. `claimRecordSchema.id` states the rule both
268
+ * now follow: `id` where the claim IS the resource, `claimId` where some other
269
+ * object names one.
228
270
  */
229
- export declare const claimAcquireResponseSchema: z.ZodUnion<readonly [z.ZodObject<{
271
+ export declare const claimAcquireResponseSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
230
272
  id: z.ZodString;
231
273
  object: z.ZodLiteral<"claim">;
274
+ status: z.ZodLiteral<"active">;
275
+ fenceToken: z.ZodOptional<z.ZodNumber>;
276
+ expiresAt: z.ZodOptional<z.ZodNumber>;
232
277
  claim: z.ZodReadonly<z.ZodObject<{
233
278
  id: z.ZodString;
234
279
  actor: z.ZodString;
@@ -270,8 +315,9 @@ export declare const claimAcquireResponseSchema: z.ZodUnion<readonly [z.ZodObjec
270
315
  field: z.ZodOptional<z.ZodString>;
271
316
  }, z.core.$strip>>;
272
317
  }, z.core.$strip>, z.ZodObject<{
318
+ id: z.ZodString;
319
+ object: z.ZodLiteral<"claim">;
273
320
  status: z.ZodLiteral<"queued">;
274
- claimId: z.ZodString;
275
321
  position: z.ZodNumber;
276
322
  heldBy: z.ZodOptional<z.ZodString>;
277
323
  expiresAt: z.ZodOptional<z.ZodNumber>;
@@ -300,7 +346,7 @@ export declare const claimAcquireResponseSchema: z.ZodUnion<readonly [z.ZodObjec
300
346
  }>>>>;
301
347
  capabilityId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
302
348
  }, z.core.$strip>>;
303
- }, z.core.$strip>]>;
349
+ }, z.core.$strip>], "status">;
304
350
  export type ClaimAcquireResponse = z.infer<typeof claimAcquireResponseSchema>;
305
351
  /**
306
352
  * `POST /v1/models/{model}/{id}/claim/heartbeat`.
@@ -373,91 +419,112 @@ export type ClaimListQuery = z.infer<typeof claimListQuerySchema>;
373
419
  /**
374
420
  * `GET /v1/claims` — who holds what, and who waits.
375
421
  *
376
- * `queue` is populated only when the request names both a `model` and an `id`;
377
- * a wait line belongs to one row, so an unscoped listing has nothing to report
378
- * and answers with an empty array rather than omitting the field.
422
+ * One list in the shape every other list has, because holders and waiters are
423
+ * the same resource in two states: each entry carries `status`, and a `queued`
424
+ * one carries its `position`. Holders come first, then the wait line in order.
425
+ *
426
+ * It used to answer `{ claims[], queue[] }` — a bespoke envelope on the endpoint
427
+ * a coordination client touches most, which cost a generated client a
428
+ * special-cased model to read the one call it makes constantly.
429
+ *
430
+ * Waiters appear only when the request names both a `model` and an `id`: a wait
431
+ * line belongs to one row, so an unscoped listing has none to report.
379
432
  */
380
433
  export declare const claimListResponseSchema: z.ZodObject<{
381
434
  object: z.ZodLiteral<"list">;
382
- claims: z.ZodReadonly<z.ZodArray<z.ZodReadonly<z.ZodObject<{
383
- id: z.ZodString;
384
- actor: z.ZodString;
385
- participantKind: z.ZodPreprocess<z.ZodEnum<{
386
- user: "user";
387
- agent: "agent";
388
- system: "system";
389
- }>>;
390
- onBehalfOfId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
391
- onBehalfOfKind: z.ZodOptional<z.ZodNullable<z.ZodPreprocess<z.ZodEnum<{
392
- user: "user";
393
- agent: "agent";
394
- system: "system";
395
- }>>>>;
396
- capabilityId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
397
- description: z.ZodOptional<z.ZodString>;
398
- status: z.ZodOptional<z.ZodEnum<{
399
- active: "active";
400
- queued: "queued";
401
- }>>;
402
- position: z.ZodOptional<z.ZodNumber>;
403
- expiresAt: z.ZodNumber;
404
- fenceToken: z.ZodOptional<z.ZodNumber>;
405
- target: z.ZodReadonly<z.ZodObject<{
406
- model: z.ZodString;
407
- id: z.ZodString;
408
- path: z.ZodOptional<z.ZodString>;
409
- range: z.ZodOptional<z.ZodObject<{
410
- startLine: z.ZodNumber;
411
- endLine: z.ZodNumber;
412
- startColumn: z.ZodOptional<z.ZodNumber>;
413
- endColumn: z.ZodOptional<z.ZodNumber>;
414
- }, z.core.$strip>>;
415
- field: z.ZodOptional<z.ZodString>;
416
- fields: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodString>>>;
417
- meta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
418
- }, z.core.$strip>>;
419
- meta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
420
- field: z.ZodOptional<z.ZodString>;
421
- }, z.core.$strip>>>>;
422
- queue: z.ZodReadonly<z.ZodArray<z.ZodReadonly<z.ZodObject<{
423
- id: z.ZodString;
424
- actor: z.ZodString;
425
- participantKind: z.ZodPreprocess<z.ZodEnum<{
426
- user: "user";
427
- agent: "agent";
428
- system: "system";
429
- }>>;
430
- onBehalfOfId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
431
- onBehalfOfKind: z.ZodOptional<z.ZodNullable<z.ZodPreprocess<z.ZodEnum<{
432
- user: "user";
433
- agent: "agent";
434
- system: "system";
435
- }>>>>;
436
- capabilityId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
437
- description: z.ZodOptional<z.ZodString>;
438
- status: z.ZodOptional<z.ZodEnum<{
439
- active: "active";
440
- queued: "queued";
441
- }>>;
442
- position: z.ZodOptional<z.ZodNumber>;
443
- expiresAt: z.ZodNumber;
444
- fenceToken: z.ZodOptional<z.ZodNumber>;
445
- target: z.ZodReadonly<z.ZodObject<{
446
- model: z.ZodString;
447
- id: z.ZodString;
448
- path: z.ZodOptional<z.ZodString>;
449
- range: z.ZodOptional<z.ZodObject<{
450
- startLine: z.ZodNumber;
451
- endLine: z.ZodNumber;
452
- startColumn: z.ZodOptional<z.ZodNumber>;
453
- endColumn: z.ZodOptional<z.ZodNumber>;
454
- }, z.core.$strip>>;
455
- field: z.ZodOptional<z.ZodString>;
456
- fields: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodString>>>;
457
- meta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
458
- }, z.core.$strip>>;
459
- meta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
460
- field: z.ZodOptional<z.ZodString>;
461
- }, z.core.$strip>>>>;
435
+ data: z.ZodReadonly<z.ZodArray<z.ZodType<Readonly<{
436
+ id: string;
437
+ actor: string;
438
+ participantKind: "user" | "agent" | "system";
439
+ expiresAt: number;
440
+ target: Readonly<{
441
+ model: string;
442
+ id: string;
443
+ path?: string | undefined;
444
+ range?: {
445
+ startLine: number;
446
+ endLine: number;
447
+ startColumn?: number | undefined;
448
+ endColumn?: number | undefined;
449
+ } | undefined;
450
+ field?: string | undefined;
451
+ fields?: readonly string[] | undefined;
452
+ meta?: Record<string, unknown> | undefined;
453
+ }>;
454
+ onBehalfOfId?: string | null | undefined;
455
+ onBehalfOfKind?: "user" | "agent" | "system" | null | undefined;
456
+ capabilityId?: string | null | undefined;
457
+ description?: string | undefined;
458
+ status?: "active" | "queued" | undefined;
459
+ position?: number | undefined;
460
+ fenceToken?: number | undefined;
461
+ meta?: Record<string, unknown> | undefined;
462
+ field?: string | undefined;
463
+ }>, unknown, z.core.$ZodTypeInternals<Readonly<{
464
+ id: string;
465
+ actor: string;
466
+ participantKind: "user" | "agent" | "system";
467
+ expiresAt: number;
468
+ target: Readonly<{
469
+ model: string;
470
+ id: string;
471
+ path?: string | undefined;
472
+ range?: {
473
+ startLine: number;
474
+ endLine: number;
475
+ startColumn?: number | undefined;
476
+ endColumn?: number | undefined;
477
+ } | undefined;
478
+ field?: string | undefined;
479
+ fields?: readonly string[] | undefined;
480
+ meta?: Record<string, unknown> | undefined;
481
+ }>;
482
+ onBehalfOfId?: string | null | undefined;
483
+ onBehalfOfKind?: "user" | "agent" | "system" | null | undefined;
484
+ capabilityId?: string | null | undefined;
485
+ description?: string | undefined;
486
+ status?: "active" | "queued" | undefined;
487
+ position?: number | undefined;
488
+ fenceToken?: number | undefined;
489
+ meta?: Record<string, unknown> | undefined;
490
+ field?: string | undefined;
491
+ }>, unknown>>>>;
492
+ has_more: z.ZodBoolean;
493
+ next_cursor: z.ZodNullable<z.ZodString>;
462
494
  }, z.core.$strip>;
463
495
  export type ClaimListResponse = z.infer<typeof claimListResponseSchema>;
496
+ /**
497
+ * `POST /v1/models/{model}/{id}/claim/reorder` — re-rank the wait line.
498
+ *
499
+ * `claimId` rather than `id` here by the rule in {@link claimRecordSchema}: an
500
+ * entry in this list names a claim, it is not one. The server parsed this body
501
+ * by hand — `JSON.parse(raw) as { order?: … }`, a cast onto an inline shape at a
502
+ * wire boundary — until it had a schema to parse against.
503
+ */
504
+ export declare const claimReorderRequestSchema: z.ZodObject<{
505
+ order: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodObject<{
506
+ heldBy: z.ZodString;
507
+ claimId: z.ZodString;
508
+ }, z.core.$strip>>>>;
509
+ }, z.core.$strip>;
510
+ export type ClaimReorderRequest = z.infer<typeof claimReorderRequestSchema>;
511
+ /** The reorder reply. */
512
+ export declare const claimReorderReplySchema: z.ZodObject<{
513
+ object: z.ZodLiteral<"claim_reorder">;
514
+ reordered: z.ZodBoolean;
515
+ }, z.core.$strip>;
516
+ export type ClaimReorderReply = z.infer<typeof claimReorderReplySchema>;
517
+ /**
518
+ * `DELETE /v1/claims/{claimId}` and `DELETE /v1/models/{model}/{id}/claim`.
519
+ *
520
+ * `released` distinguishes "your lease is gone because this call ended it" from
521
+ * "there was nothing of yours to end" — both are success, and a caller
522
+ * retrying a release deserves to know which happened. The two routes answered
523
+ * `{ ok: true }` and `{ ok, released }`: two shapes, neither written down, and
524
+ * `ok` restating the status code.
525
+ */
526
+ export declare const claimReleaseReplySchema: z.ZodObject<{
527
+ object: z.ZodLiteral<"claim_release">;
528
+ released: z.ZodBoolean;
529
+ }, z.core.$strip>;
530
+ export type ClaimReleaseReply = z.infer<typeof claimReleaseReplySchema>;
@@ -10,7 +10,42 @@
10
10
  * unvalidated and the reference described the shape from memory.
11
11
  */
12
12
  import { z } from 'zod';
13
+ import { durationSchema, parseDurationMs } from '../utils/duration.js';
14
+ import { listEnvelopeSchema } from './listEnvelope.js';
13
15
  import { targetRangeSchema, publicClaimStatusSchema, wireParticipantKindSchema, claimRecordSchema, modelClaimSchema, claimHeartbeatAckPayloadSchema, wireClaimSummarySchema, } from '../coordination/schema.js';
16
+ /**
17
+ * How long a lease runs when the caller does not ask for a length.
18
+ *
19
+ * One definition for both ends: the server stamps `expiresAt` with it, and the
20
+ * client's auto-heartbeat derives its cadence from it, so a claim that names no
21
+ * TTL beats comfortably inside the window the server actually granted.
22
+ */
23
+ export const DEFAULT_CLAIM_TTL_MS = 60_000;
24
+ /**
25
+ * A lease length on the wire, in the SDK's one duration grammar: a
26
+ * unit-suffixed string (`'500ms'`, `'30s'`, `'5m'`, `'24h'`) or a bare number of
27
+ * **seconds** — the same reading `ttlSeconds` has everywhere else in the API.
28
+ *
29
+ * It used to be `z.union([z.string(), z.number()])`, and the units lived only in
30
+ * the route's private parser, which read a bare number as MILLISECONDS. So the
31
+ * SDK computed its heartbeat cadence from one grammar while the server granted a
32
+ * lease under another, and `ttl: 300` meant five minutes to the client and three
33
+ * hundred milliseconds to the server. Publishing the grammar is what makes that
34
+ * class of disagreement impossible: there is now one pattern, emitted into the
35
+ * contract, and one parser behind it.
36
+ */
37
+ export const claimTtlSchema = durationSchema;
38
+ /**
39
+ * A wire TTL as milliseconds — the single reading of the grammar above.
40
+ *
41
+ * The server calls this rather than carrying its own parser. An absent TTL takes
42
+ * {@link DEFAULT_CLAIM_TTL_MS}; anything present has already been validated by
43
+ * {@link claimTtlSchema} at the boundary, so a malformed duration is a 400 at the
44
+ * door rather than a silent one-minute lease that lapses mid-work.
45
+ */
46
+ export function claimTtlMs(ttl) {
47
+ return ttl == null ? DEFAULT_CLAIM_TTL_MS : parseDurationMs(ttl);
48
+ }
14
49
  /** The row a claim points at, when it is not already given by the URL. */
15
50
  export const claimTargetSchema = z.object({
16
51
  model: z.string().optional(),
@@ -35,8 +70,8 @@ export const claimRequestSchema = z.object({
35
70
  /** Defaults to `editing`. Shown to other participants holding the row. */
36
71
  description: z.string().optional(),
37
72
  meta: z.record(z.string(), z.unknown()).optional(),
38
- /** Lease length in the same grammar as a heartbeat: `'5m'` or `30000`. */
39
- ttl: z.union([z.string(), z.number()]).nullish(),
73
+ /** Lease length in the same grammar as a heartbeat see {@link claimTtlSchema}. */
74
+ ttl: claimTtlSchema.nullish(),
40
75
  /**
41
76
  * Join the fair wait line when the row is already held, instead of failing.
42
77
  * The response carries `{ status: 'queued', position }` and the grant arrives
@@ -52,7 +87,8 @@ export const claimRequestSchema = z.object({
52
87
  */
53
88
  export const claimHeartbeatRequestSchema = z.object({
54
89
  claimId: z.string().optional(),
55
- ttl: z.union([z.string(), z.number()]).nullish(),
90
+ /** Extends the lease by this much from now — see {@link claimTtlSchema}. */
91
+ ttl: claimTtlSchema.nullish(),
56
92
  /** Progress carried on the beat — becomes the claim's peer-visible
57
93
  * `meta.progress`. Last beat wins, and it goes with the lease. */
58
94
  details: z.record(z.string(), z.unknown()).optional(),
@@ -100,21 +136,27 @@ export const listQuerySchema = z.object({
100
136
  * its own read, the same way the socket path takes it from a local snapshot.
101
137
  */
102
138
  export const claimStateSchema = claimRecordSchema
103
- .pick({ position: true, expiresAt: true, fenceToken: true })
104
- .partial()
139
+ .pick({ id: true, position: true, expiresAt: true, fenceToken: true })
140
+ .partial({ position: true, expiresAt: true, fenceToken: true })
105
141
  .extend({
106
142
  object: z.literal('claim'),
107
- /** The claim named, rather than the claim as a resource — which is why this
108
- * is `claimId` and not the record's `id`. */
109
- claimId: z.string(),
110
143
  /**
111
144
  * Widened from the record's two observable states to all five.
112
145
  *
113
146
  * A poll is the one surface that can arrive after the claim ended: the
114
- * caller holds an id and asks what became of it, so `committed`, `expired`,
115
- * and `canceled` are answers this route must be able to give. A peer's
116
- * listing cannot see them, because a terminal claim has already left the
117
- * set being listed.
147
+ * caller holds an id and asks what became of it, so the terminal statuses
148
+ * are answers this route must be able to give. A peer's listing cannot see
149
+ * them, because a terminal claim has already left the set being listed.
150
+ *
151
+ * `expired` and `canceled` are answered for a few minutes after the fact,
152
+ * long enough for a straggler poll and no longer — the ClaimLog is the
153
+ * record, this is a courtesy. Past that window the claim is genuinely gone
154
+ * and the route answers 404 `claim_not_found`.
155
+ *
156
+ * `committed` is declared and not yet answered: it needs the release path
157
+ * to know that a write settled under the lease, which the commit path does
158
+ * not hand back today. A caller must therefore treat the terminal statuses
159
+ * as a set rather than switching exhaustively on the two it sees.
118
160
  */
119
161
  status: publicClaimStatusSchema,
120
162
  });
@@ -127,6 +169,19 @@ export const claimStateSchema = claimRecordSchema
127
169
  export const claimAcquiredResponseSchema = z.object({
128
170
  id: z.string(),
129
171
  object: z.literal('claim'),
172
+ status: z.literal('active'),
173
+ /**
174
+ * The lease's own fields, in the same place the poll puts them.
175
+ *
176
+ * They are also inside `claim`, which is the peer-visible view and carries
177
+ * far more — actor, target, description, delegation. Mirroring the two that a
178
+ * holder actually acts on is what lets one reader handle both answers: a
179
+ * client that had to look in `claim.fenceToken` here and `fenceToken` on the
180
+ * poll is reading one resource through two shapes, which is the defect this
181
+ * response pair was just unified to remove.
182
+ */
183
+ fenceToken: z.number().int().optional(),
184
+ expiresAt: z.number().int().optional(),
130
185
  claim: modelClaimSchema,
131
186
  });
132
187
  /**
@@ -134,25 +189,33 @@ export const claimAcquiredResponseSchema = z.object({
134
189
  * put you in line. The grant arrives later on the caller's stream, or through
135
190
  * `GET /v1/claims/{claimId}`.
136
191
  *
137
- * There is deliberately no `object` field here: a queued claim is not yet a
138
- * created resource, and the status code plus `status: 'queued'` already carry
139
- * the distinction.
192
+ * Shaped as a claim resource, exactly like the 201, because that is what it is:
193
+ * a queue entry is a lease in a different state — the reason one `DELETE`
194
+ * serves both, and the reason the poll can move one to the other. It once
195
+ * withheld `object` and `id` on the argument that a queued claim is not yet a
196
+ * created resource, which read as principled and cost a caller the ability to
197
+ * treat the two answers as one type.
140
198
  */
141
199
  export const claimQueuedResponseSchema = z.object({
200
+ id: z.string(),
201
+ object: z.literal('claim'),
142
202
  status: z.literal('queued'),
143
- claimId: z.string(),
144
203
  position: z.number().int().nonnegative(),
145
204
  heldBy: z.string().optional(),
146
205
  expiresAt: z.number().int().optional(),
147
206
  heldByClaim: wireClaimSummarySchema.optional(),
148
207
  });
149
208
  /**
150
- * Either answer to a claim acquire. The two arms cannot be a discriminated
151
- * union — the 201 keys on `object` and the 202 on `status`, and neither field
152
- * appears in both — so a caller branches on `status === 'queued'`, which is
153
- * what the HTTP transport does.
209
+ * Either answer to a claim acquire one resource, two states, told apart by
210
+ * `status`.
211
+ *
212
+ * A real discriminated union, which it could not be while the arms disagreed
213
+ * about what the handle was called (`id` on the 201, `claimId` on the 202) and
214
+ * only one of them said `object`. `claimRecordSchema.id` states the rule both
215
+ * now follow: `id` where the claim IS the resource, `claimId` where some other
216
+ * object names one.
154
217
  */
155
- export const claimAcquireResponseSchema = z.union([
218
+ export const claimAcquireResponseSchema = z.discriminatedUnion('status', [
156
219
  claimAcquiredResponseSchema,
157
220
  claimQueuedResponseSchema,
158
221
  ]);
@@ -218,12 +281,47 @@ export const claimListQuerySchema = z.object({
218
281
  /**
219
282
  * `GET /v1/claims` — who holds what, and who waits.
220
283
  *
221
- * `queue` is populated only when the request names both a `model` and an `id`;
222
- * a wait line belongs to one row, so an unscoped listing has nothing to report
223
- * and answers with an empty array rather than omitting the field.
284
+ * One list in the shape every other list has, because holders and waiters are
285
+ * the same resource in two states: each entry carries `status`, and a `queued`
286
+ * one carries its `position`. Holders come first, then the wait line in order.
287
+ *
288
+ * It used to answer `{ claims[], queue[] }` — a bespoke envelope on the endpoint
289
+ * a coordination client touches most, which cost a generated client a
290
+ * special-cased model to read the one call it makes constantly.
291
+ *
292
+ * Waiters appear only when the request names both a `model` and an `id`: a wait
293
+ * line belongs to one row, so an unscoped listing has none to report.
224
294
  */
225
- export const claimListResponseSchema = z.object({
226
- object: z.literal('list'),
227
- claims: z.array(modelClaimSchema).readonly(),
228
- queue: z.array(modelClaimSchema).readonly(),
295
+ export const claimListResponseSchema = listEnvelopeSchema(modelClaimSchema);
296
+ /**
297
+ * `POST /v1/models/{model}/{id}/claim/reorder` — re-rank the wait line.
298
+ *
299
+ * `claimId` rather than `id` here by the rule in {@link claimRecordSchema}: an
300
+ * entry in this list names a claim, it is not one. The server parsed this body
301
+ * by hand — `JSON.parse(raw) as { order?: … }`, a cast onto an inline shape at a
302
+ * wire boundary — until it had a schema to parse against.
303
+ */
304
+ export const claimReorderRequestSchema = z.object({
305
+ order: z
306
+ .array(z.object({ heldBy: z.string(), claimId: z.string() }))
307
+ .readonly()
308
+ .optional(),
309
+ });
310
+ /** The reorder reply. */
311
+ export const claimReorderReplySchema = z.object({
312
+ object: z.literal('claim_reorder'),
313
+ reordered: z.boolean(),
314
+ });
315
+ /**
316
+ * `DELETE /v1/claims/{claimId}` and `DELETE /v1/models/{model}/{id}/claim`.
317
+ *
318
+ * `released` distinguishes "your lease is gone because this call ended it" from
319
+ * "there was nothing of yours to end" — both are success, and a caller
320
+ * retrying a release deserves to know which happened. The two routes answered
321
+ * `{ ok: true }` and `{ ok, released }`: two shapes, neither written down, and
322
+ * `ok` restating the status code.
323
+ */
324
+ export const claimReleaseReplySchema = z.object({
325
+ object: z.literal('claim_release'),
326
+ released: z.boolean(),
229
327
  });
@@ -592,8 +592,8 @@ export declare const commitRequestSchema: z.ZodObject<{
592
592
  }>>;
593
593
  }, z.core.$strip>]>>>>;
594
594
  track: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
595
- model: z.ZodString;
596
595
  id: z.ZodString;
596
+ model: z.ZodString;
597
597
  readAt: z.ZodOptional<z.ZodNumber>;
598
598
  }, z.core.$strip>, z.ZodObject<{
599
599
  group: z.ZodString;
@@ -165,6 +165,33 @@ export declare const logListResponseSchema: z.ZodObject<{
165
165
  next_cursor: z.ZodNullable<z.ZodString>;
166
166
  }, z.core.$strip>;
167
167
  export type LogListResponse = ListEnvelope<FeedEvent>;
168
+ /**
169
+ * The query string on `GET /v1/logs`.
170
+ *
171
+ * Values arrive as strings, so this describes the wire spelling rather than the
172
+ * parsed result — the same reason {@link listQuerySchema} does.
173
+ *
174
+ * Scope is NOT here and never will be: the plane, the organization and the sync
175
+ * groups all come off the credential. A caller cannot widen what it sees by
176
+ * asking, which is what makes this feed safe to hand an agent.
177
+ */
178
+ export declare const logQuerySchema: z.ZodObject<{
179
+ after: z.ZodOptional<z.ZodString>;
180
+ limit: z.ZodOptional<z.ZodString>;
181
+ model: z.ZodOptional<z.ZodString>;
182
+ op: z.ZodOptional<z.ZodEnum<{
183
+ update: "update";
184
+ visible: "visible";
185
+ create: "create";
186
+ delete: "delete";
187
+ archive: "archive";
188
+ revive: "revive";
189
+ group_added: "group_added";
190
+ group_removed: "group_removed";
191
+ }>>;
192
+ since: z.ZodOptional<z.ZodString>;
193
+ }, z.core.$strip>;
194
+ export type LogQuery = z.infer<typeof logQuerySchema>;
168
195
  /**
169
196
  * The claim arm has no producer yet: claim transitions are broadcast today and
170
197
  * recorded nowhere, so there is no sequence to read `seq` from. Until that log