@abloatai/transaction 0.59.2 → 0.61.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 (275) hide show
  1. package/dist/auth/apiKey.d.ts +10 -11
  2. package/dist/auth/apiKey.d.ts.map +1 -1
  3. package/dist/auth/apiKey.js +15 -29
  4. package/dist/auth/apiKey.js.map +1 -1
  5. package/dist/auth/browserCredentialSafety.js +1 -1
  6. package/dist/auth/browserCredentialSafety.js.map +1 -1
  7. package/dist/auth/capability.d.ts +15 -15
  8. package/dist/auth/capabilityLifecycle.d.ts +3 -3
  9. package/dist/auth/capabilityLifecycle.d.ts.map +1 -1
  10. package/dist/auth/capabilityLifecycle.js +2 -1
  11. package/dist/auth/capabilityLifecycle.js.map +1 -1
  12. package/dist/auth/credentialEndpoint.js +1 -1
  13. package/dist/auth/credentialEndpoint.js.map +1 -1
  14. package/dist/auth/credentialResult.d.ts +2 -1
  15. package/dist/auth/credentialResult.d.ts.map +1 -1
  16. package/dist/auth/credentialResult.js.map +1 -1
  17. package/dist/auth/credentialSource.js.map +1 -1
  18. package/dist/auth/identity.js.map +1 -1
  19. package/dist/auth/schemas.d.ts +5 -5
  20. package/dist/auth/sessionIssuanceProtocol.d.ts +8 -8
  21. package/dist/auth/sessionIssuanceProtocol.d.ts.map +1 -1
  22. package/dist/branches.d.ts +38 -38
  23. package/dist/claims/admission.d.ts +4 -0
  24. package/dist/claims/admission.d.ts.map +1 -0
  25. package/dist/claims/admission.js +32 -0
  26. package/dist/claims/admission.js.map +1 -0
  27. package/dist/claims/awaitGrant.d.ts.map +1 -1
  28. package/dist/claims/awaitGrant.js +4 -38
  29. package/dist/claims/awaitGrant.js.map +1 -1
  30. package/dist/claims/conflict.d.ts +55 -0
  31. package/dist/claims/conflict.d.ts.map +1 -0
  32. package/dist/claims/conflict.js +71 -0
  33. package/dist/claims/conflict.js.map +1 -0
  34. package/dist/claims/contract.d.ts +39 -27
  35. package/dist/claims/contract.d.ts.map +1 -1
  36. package/dist/claims/contract.js +2 -0
  37. package/dist/claims/contract.js.map +1 -1
  38. package/dist/claims/eventContract.d.ts +18 -18
  39. package/dist/claims/events.d.ts +1 -1
  40. package/dist/claims/events.d.ts.map +1 -1
  41. package/dist/claims/index.d.ts +2 -0
  42. package/dist/claims/index.d.ts.map +1 -1
  43. package/dist/claims/index.js +1 -0
  44. package/dist/claims/index.js.map +1 -1
  45. package/dist/claims/routes.d.ts +9 -9
  46. package/dist/claims/routes.d.ts.map +1 -1
  47. package/dist/claims/trace.d.ts.map +1 -1
  48. package/dist/claims/trace.js.map +1 -1
  49. package/dist/client/ablo.d.ts +11 -8
  50. package/dist/client/ablo.d.ts.map +1 -1
  51. package/dist/client/ablo.js +5 -6
  52. package/dist/client/ablo.js.map +1 -1
  53. package/dist/client/resources/httpResources.d.ts +1 -169
  54. package/dist/client/resources/httpResources.d.ts.map +1 -1
  55. package/dist/client/resources/httpResources.js +1 -1
  56. package/dist/client/resources/httpResources.js.map +1 -1
  57. package/dist/client/resources/modelOperations.d.ts +6 -15
  58. package/dist/client/resources/modelOperations.d.ts.map +1 -1
  59. package/dist/client/resources/where.d.ts +12 -12
  60. package/dist/client/surface.d.ts +1 -1
  61. package/dist/client/surface.d.ts.map +1 -1
  62. package/dist/client/surface.js +2 -4
  63. package/dist/client/surface.js.map +1 -1
  64. package/dist/commit/confirmation/commitEnvelope.d.ts +7 -7
  65. package/dist/commit/confirmation/httpCommitEnvelope.d.ts +1 -1
  66. package/dist/commit/confirmation/pendingWrite.d.ts +5 -5
  67. package/dist/commit/contract.d.ts +249 -249
  68. package/dist/commit/contract.d.ts.map +1 -1
  69. package/dist/coordination/index.d.ts +2 -2
  70. package/dist/coordination/index.d.ts.map +1 -1
  71. package/dist/coordination/index.js +4 -5
  72. package/dist/coordination/index.js.map +1 -1
  73. package/dist/coordination/schema.d.ts +64 -101
  74. package/dist/coordination/schema.d.ts.map +1 -1
  75. package/dist/coordination/schema.js +14 -69
  76. package/dist/coordination/schema.js.map +1 -1
  77. package/dist/environment.d.ts +3 -3
  78. package/dist/environment.d.ts.map +1 -1
  79. package/dist/environment.js.map +1 -1
  80. package/dist/errorCodes.d.ts +4 -5
  81. package/dist/errorCodes.d.ts.map +1 -1
  82. package/dist/errorCodes.js +15 -9
  83. package/dist/errorCodes.js.map +1 -1
  84. package/dist/errorObservation.d.ts +14 -14
  85. package/dist/errors.d.ts +17 -11
  86. package/dist/errors.d.ts.map +1 -1
  87. package/dist/errors.js +101 -5
  88. package/dist/errors.js.map +1 -1
  89. package/dist/keys/index.d.ts +1 -1
  90. package/dist/keys/index.d.ts.map +1 -1
  91. package/dist/keys/index.js.map +1 -1
  92. package/dist/observation/contract.d.ts +34 -34
  93. package/dist/observation/feedContract.d.ts +86 -86
  94. package/dist/observation/persistence/syncDeltaRow.d.ts +23 -23
  95. package/dist/observation/persistence/syncDeltaRow.d.ts.map +1 -1
  96. package/dist/pricing.d.ts +12 -12
  97. package/dist/schema/audit.js.map +1 -1
  98. package/dist/schema/ddl.js.map +1 -1
  99. package/dist/schema/ddlLock.js.map +1 -1
  100. package/dist/schema/deployment/contracts.d.ts +64 -64
  101. package/dist/schema/deployment/index.js.map +1 -1
  102. package/dist/schema/deployment/reconcile.js.map +1 -1
  103. package/dist/schema/diff.js.map +1 -1
  104. package/dist/schema/field.d.ts +1 -1
  105. package/dist/schema/field.d.ts.map +1 -1
  106. package/dist/schema/loadStrategy.d.ts +2 -2
  107. package/dist/schema/loadStrategy.d.ts.map +1 -1
  108. package/dist/schema/openapi.js.map +1 -1
  109. package/dist/schema/relation.d.ts +3 -3
  110. package/dist/schema/relation.d.ts.map +1 -1
  111. package/dist/schema/residency.d.ts +1 -1
  112. package/dist/schema/roles.d.ts +1 -1
  113. package/dist/schema/roles.d.ts.map +1 -1
  114. package/dist/schema/roles.js.map +1 -1
  115. package/dist/schema/schema.d.ts +1 -1
  116. package/dist/schema/schema.d.ts.map +1 -1
  117. package/dist/schema/serialize.js.map +1 -1
  118. package/dist/schema/sugar.d.ts.map +1 -1
  119. package/dist/schema/tenancy.d.ts +2 -2
  120. package/dist/server/storageMode.d.ts +1 -1
  121. package/dist/sessions/client.d.ts +29 -0
  122. package/dist/sessions/client.d.ts.map +1 -0
  123. package/dist/sessions/client.js +69 -0
  124. package/dist/sessions/client.js.map +1 -0
  125. package/dist/sessions/contract.d.ts +97 -0
  126. package/dist/sessions/contract.d.ts.map +1 -0
  127. package/dist/sessions/contract.js +5 -0
  128. package/dist/sessions/contract.js.map +1 -0
  129. package/dist/{auth/sessionMint.d.ts → sessions/create.d.ts} +4 -4
  130. package/dist/sessions/create.d.ts.map +1 -0
  131. package/dist/{auth/sessionMint.js → sessions/create.js} +11 -10
  132. package/dist/sessions/create.js.map +1 -0
  133. package/dist/sessions/handler.d.ts +23 -0
  134. package/dist/sessions/handler.d.ts.map +1 -0
  135. package/dist/sessions/handler.js +55 -0
  136. package/dist/sessions/handler.js.map +1 -0
  137. package/dist/sessions/index.d.ts +14 -0
  138. package/dist/sessions/index.d.ts.map +1 -0
  139. package/dist/sessions/index.js +14 -0
  140. package/dist/sessions/index.js.map +1 -0
  141. package/dist/{transport/connection/credentialLifecycle.d.ts → sessions/lifecycle.d.ts} +6 -6
  142. package/dist/sessions/lifecycle.d.ts.map +1 -0
  143. package/dist/{transport/connection/credentialLifecycle.js → sessions/lifecycle.js} +5 -5
  144. package/dist/sessions/lifecycle.js.map +1 -0
  145. package/dist/sessions/source.d.ts +24 -0
  146. package/dist/sessions/source.d.ts.map +1 -0
  147. package/dist/sessions/source.js +56 -0
  148. package/dist/sessions/source.js.map +1 -0
  149. package/dist/source/adapters/adapterProfile.d.ts +6 -6
  150. package/dist/source/adapters/conformance.js.map +1 -1
  151. package/dist/source/adapters/contract.d.ts +14 -14
  152. package/dist/source/adapters/kysely.js.map +1 -1
  153. package/dist/source/adapters/memory.js.map +1 -1
  154. package/dist/source/adapters/prisma.js.map +1 -1
  155. package/dist/source/connector/client.d.ts.map +1 -1
  156. package/dist/source/connector/client.js.map +1 -1
  157. package/dist/source/connector/protocol.d.ts.map +1 -1
  158. package/dist/source/delivery/pushQueue.d.ts.map +1 -1
  159. package/dist/source/delivery/pushQueue.js.map +1 -1
  160. package/dist/source/endpoint/signing.d.ts +4 -4
  161. package/dist/source/endpoint/signing.d.ts.map +1 -1
  162. package/dist/source/outbox/contract.d.ts +4 -4
  163. package/dist/testing/fixtures/httpResponses.d.ts +2 -2
  164. package/dist/testing/fixtures/httpResponses.d.ts.map +1 -1
  165. package/dist/testing/fixtures/httpResponses.js.map +1 -1
  166. package/dist/transport/connection/index.d.ts +0 -1
  167. package/dist/transport/connection/index.d.ts.map +1 -1
  168. package/dist/transport/connection/index.js +0 -1
  169. package/dist/transport/connection/index.js.map +1 -1
  170. package/dist/transport/connection/manager.d.ts +1 -1
  171. package/dist/transport/connection/manager.d.ts.map +1 -1
  172. package/dist/transport/connection/manager.js.map +1 -1
  173. package/dist/transport/connection/networkProbe.d.ts +5 -18
  174. package/dist/transport/connection/networkProbe.d.ts.map +1 -1
  175. package/dist/transport/connection/networkProbe.js.map +1 -1
  176. package/dist/transport/http/claimWait.d.ts +9 -0
  177. package/dist/transport/http/claimWait.d.ts.map +1 -0
  178. package/dist/transport/http/claimWait.js +74 -0
  179. package/dist/transport/http/claimWait.js.map +1 -0
  180. package/dist/transport/http/client.d.ts +11 -19
  181. package/dist/transport/http/client.d.ts.map +1 -1
  182. package/dist/transport/http/client.js +15 -18
  183. package/dist/transport/http/client.js.map +1 -1
  184. package/dist/transport/http/contract.d.ts +57 -0
  185. package/dist/transport/http/contract.d.ts.map +1 -0
  186. package/dist/transport/http/contract.js +3 -0
  187. package/dist/transport/http/contract.js.map +1 -0
  188. package/dist/transport/http/index.d.ts +1 -0
  189. package/dist/transport/http/index.d.ts.map +1 -1
  190. package/dist/transport/http/index.js +1 -0
  191. package/dist/transport/http/index.js.map +1 -1
  192. package/dist/transport/http/options.d.ts +4 -1
  193. package/dist/transport/http/options.d.ts.map +1 -1
  194. package/dist/transport/http/transport.d.ts +1 -84
  195. package/dist/transport/http/transport.d.ts.map +1 -1
  196. package/dist/transport/http/transport.js +16 -134
  197. package/dist/transport/http/transport.js.map +1 -1
  198. package/dist/transport/websocket/commitFrames.d.ts +1 -2
  199. package/dist/transport/websocket/commitFrames.d.ts.map +1 -1
  200. package/dist/transport/websocket/commitFrames.js.map +1 -1
  201. package/dist/transport/websocket/frameHandlers.d.ts +0 -14
  202. package/dist/transport/websocket/frameHandlers.d.ts.map +1 -1
  203. package/dist/transport/websocket/frameHandlers.js +4 -71
  204. package/dist/transport/websocket/frameHandlers.js.map +1 -1
  205. package/dist/transport/websocket/heartbeat.d.ts.map +1 -1
  206. package/dist/transport/websocket/heartbeat.js.map +1 -1
  207. package/dist/transport/websocket/session.d.ts.map +1 -1
  208. package/dist/transport/websocket/session.js +137 -64
  209. package/dist/transport/websocket/session.js.map +1 -1
  210. package/dist/transport/websocket/sessionContract.d.ts +4 -15
  211. package/dist/transport/websocket/sessionContract.d.ts.map +1 -1
  212. package/dist/transport/websocket/transport.d.ts +6 -42
  213. package/dist/transport/websocket/transport.d.ts.map +1 -1
  214. package/dist/transport/websocket/transport.js +14 -101
  215. package/dist/transport/websocket/transport.js.map +1 -1
  216. package/dist/wire/accountResponses.d.ts +31 -31
  217. package/dist/wire/auth.d.ts +3 -3
  218. package/dist/wire/bootstrapReason.d.ts +2 -2
  219. package/dist/wire/dataSourceResponses.d.ts +2 -2
  220. package/dist/wire/dataSourceResponses.d.ts.map +1 -1
  221. package/dist/wire/frames.d.ts +8 -8
  222. package/dist/wire/inboundFrames.d.ts +60 -63
  223. package/dist/wire/inboundFrames.d.ts.map +1 -1
  224. package/dist/wire/inboundFrames.js +5 -6
  225. package/dist/wire/inboundFrames.js.map +1 -1
  226. package/dist/wire/listEnvelope.js.map +1 -1
  227. package/dist/wire/modelResponses.d.ts +2 -2
  228. package/dist/wire/modelShape.d.ts +4 -4
  229. package/dist/wire/pricing.d.ts +3 -3
  230. package/dist/wire/protocolVersion.d.ts.map +1 -1
  231. package/package.json +6 -1
  232. package/src/auth/apiKey.ts +25 -44
  233. package/src/auth/browserCredentialSafety.ts +1 -1
  234. package/src/auth/capabilityLifecycle.ts +3 -2
  235. package/src/auth/credentialEndpoint.ts +1 -1
  236. package/src/auth/credentialResult.ts +2 -0
  237. package/src/claims/admission.ts +49 -0
  238. package/src/claims/awaitGrant.ts +3 -52
  239. package/src/claims/conflict.ts +103 -0
  240. package/src/claims/contract.ts +2 -0
  241. package/src/claims/events.ts +1 -2
  242. package/src/claims/index.ts +11 -0
  243. package/src/client/ablo.ts +15 -10
  244. package/src/client/resources/httpResources.ts +2 -174
  245. package/src/client/resources/modelOperations.ts +0 -10
  246. package/src/client/surface.ts +2 -4
  247. package/src/coordination/index.ts +4 -9
  248. package/src/coordination/schema.ts +18 -81
  249. package/src/errorCodes.ts +21 -13
  250. package/src/errors.ts +116 -4
  251. package/src/sessions/client.ts +121 -0
  252. package/src/sessions/contract.ts +116 -0
  253. package/src/{auth/sessionMint.ts → sessions/create.ts} +13 -12
  254. package/src/sessions/handler.ts +96 -0
  255. package/src/sessions/index.ts +13 -0
  256. package/src/{transport/connection/credentialLifecycle.ts → sessions/lifecycle.ts} +5 -5
  257. package/src/sessions/source.ts +94 -0
  258. package/src/transport/connection/index.ts +0 -1
  259. package/src/transport/connection/manager.ts +1 -1
  260. package/src/transport/http/claimWait.ts +108 -0
  261. package/src/transport/http/client.ts +27 -36
  262. package/src/transport/http/contract.ts +71 -0
  263. package/src/transport/http/index.ts +1 -0
  264. package/src/transport/http/options.ts +4 -1
  265. package/src/transport/http/transport.ts +22 -275
  266. package/src/transport/websocket/commitFrames.ts +0 -2
  267. package/src/transport/websocket/frameHandlers.ts +7 -102
  268. package/src/transport/websocket/session.ts +162 -73
  269. package/src/transport/websocket/sessionContract.ts +4 -13
  270. package/src/transport/websocket/transport.ts +15 -137
  271. package/src/wire/inboundFrames.ts +5 -6
  272. package/dist/auth/sessionMint.d.ts.map +0 -1
  273. package/dist/auth/sessionMint.js.map +0 -1
  274. package/dist/transport/connection/credentialLifecycle.d.ts.map +0 -1
  275. package/dist/transport/connection/credentialLifecycle.js.map +0 -1
@@ -28,6 +28,7 @@ import {
28
28
  readProcessEnv,
29
29
  resolveApiKey,
30
30
  resolveApiKeyValue,
31
+ resolveCredentialValue,
31
32
  resolveAuthToken,
32
33
  resolveBaseURL,
33
34
  resolveBootstrapBaseUrl,
@@ -52,7 +53,6 @@ import {
52
53
  DEFAULT_CLAIM_TTL_MS,
53
54
  type ClaimHeartbeatReply,
54
55
  type ClaimListResponse,
55
- type ClaimQueuedResponse,
56
56
  type ClaimRequest,
57
57
  type ClaimState,
58
58
  type ClaimTargetBody,
@@ -68,7 +68,7 @@ import {
68
68
  resolveHeartbeatPlan,
69
69
  startClaimHeartbeatLoop,
70
70
  } from '../../claims/heartbeat.js';
71
- import type { HttpClientConfig } from './options.js';
71
+ import type { HttpTransport, HttpTransportOptions } from './contract.js';
72
72
  import type {
73
73
  ClaimedOptions,
74
74
  CommitCreateOptions,
@@ -86,15 +86,10 @@ import type {
86
86
  HttpTransportList,
87
87
  HttpLogsResource,
88
88
  ModelTarget,
89
- CreateSessionParams,
90
- AbloSession,
91
- SessionResource,
92
89
  } from '../../client/resources/httpResources.js';
93
- import { mintSession } from '../../auth/sessionMint.js';
94
- import {
95
- revokeCapability,
96
- rotateCapability,
97
- } from '../../auth/capabilityLifecycle.js';
90
+ import type { CredentialProviderResult } from '../../auth/credentialResult.js';
91
+ import { credentialToken } from '../../auth/credentialResult.js';
92
+ import { createSessionAccess } from '../../sessions/index.js';
98
93
  import { parseIdentityResolveResponse } from '../../auth/schemas.js';
99
94
  import type { EffectiveAuthority } from '../../auth/capability.js';
100
95
 
@@ -103,7 +98,6 @@ import {
103
98
  heldHeartbeatReply,
104
99
  parseSuccessfulCommitResponse,
105
100
  } from './helpers.js';
106
- import type { SchemaRecord } from '../../schema/schema.js';
107
101
  import type {
108
102
  ClaimLookupParams,
109
103
  ClaimOptions,
@@ -113,7 +107,6 @@ import type {
113
107
  ClaimReorderParams,
114
108
  ModelCreateManyParams,
115
109
  ServerReadOptions,
116
- ResolvedClaimContentionOptions,
117
110
  ClaimQueueView,
118
111
  } from '../../client/resources/modelOperations.js';
119
112
  import {
@@ -124,12 +117,6 @@ import {
124
117
  } from '../../client/resources/modelOperations.js';
125
118
  import type { Duration } from '../../utils/duration.js';
126
119
  import { claimDescription, partName } from '../../coordination/schema.js';
127
- import type {
128
- ClaimAcquired,
129
- ClaimBeginPayload,
130
- ClaimGranted,
131
- ClaimQueued,
132
- } from '../../coordination/schema.js';
133
120
  import type { BatchFence } from '../../claims/locator.js';
134
121
  import {
135
122
  subTarget,
@@ -146,7 +133,6 @@ import type {
146
133
  HeldClaim,
147
134
  HeldLease,
148
135
  } from '../../types/streams.js';
149
- import type { CoordinationObservability } from '../../observability.js';
150
136
  import {
151
137
  assertWriteOptions,
152
138
  assertWriteTarget,
@@ -174,95 +160,14 @@ import {
174
160
  type ExactHttpCommitRequest,
175
161
  } from '../../commit/httpRequest.js';
176
162
  import { resolveDurableWrites } from '../../commit/durableWrites.js';
177
- import { createHttpReadOnChange, type HttpReadOnChange } from './subscription.js';
163
+ import { createHttpReadOnChange } from './subscription.js';
164
+ import { awaitClaimGrantOverHttp } from './claimWait.js';
178
165
  import type { CommitFrameOperation } from '../websocket/commitFrames.js';
179
166
 
180
- /** @internal Private options for the schema-agnostic HTTP protocol transport. */
181
- export type HttpTransportOptions = Omit<HttpClientConfig, 'schema'> & {
182
- readonly bootstrapBaseUrl?: string | undefined;
183
- /** Schema-key to wire-typename mapping used only when minting agent sessions. */
184
- readonly modelTypenames?: Readonly<Record<string, string>> | undefined;
185
- /**
186
- * The observability provider forwarded from `Ablo({ observability })`. The HTTP
187
- * transport emits the same claim and conflict events as the WebSocket transport,
188
- * so a `ClaimLog` works identically for headless server-agent evaluations.
189
- */
190
- readonly observability?: CoordinationObservability;
191
- /**
192
- * Per-request deadline in milliseconds for the stateless HTTP transport.
193
- * Every request this client issues is aborted after this long and surfaces
194
- * as a retryable connection error — without it a black-holed server hangs
195
- * a headless agent forever (browsers never time fetch out on their own).
196
- * Pass `0` to disable the deadline.
197
- *
198
- * @default 30_000
199
- */
200
- readonly timeoutMs?: number;
201
- /** @internal Routes writes through the selected duplex transport. */
202
- readonly dispatchCommit?: ((input: {
203
- readonly clientTxId: string;
204
- readonly operations: readonly CommitFrameOperation[];
205
- readonly reads?: readonly import('../../coordination/schema.js').ReadDependency[] | null;
206
- }) => Promise<CommitReceiptWire>) | undefined;
207
- /** @internal Routes held-claim acquisition through the shared socket. */
208
- readonly dispatchClaim?: ((input: ClaimBeginPayload & {
209
- readonly timeoutMs?: number;
210
- readonly signal?: AbortSignal;
211
- readonly onQueued?: (event: ClaimQueued) => Error | undefined;
212
- }) => Promise<ClaimAcquired | ClaimGranted>) | undefined;
213
- /** @internal Releases a socket-acquired claim on that same session. */
214
- readonly releaseDispatchedClaim?: ((input: {
215
- readonly claimId: string;
216
- readonly entityType: string;
217
- readonly entityId: string;
218
- }) => void) | undefined;
219
- };
220
-
221
167
  /** @internal Default per-request deadline for the private HTTP transport. */
222
168
  export const DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
223
169
  const HTTP_CONFIRMATION_POLL_INTERVAL_MS = 250;
224
- // NOTE: end-user / agent session minting is `ablo.sessions.create(...)` (typed
225
- // against the schema, see Ablo.ts `CreateSessionParams`). There is no separate
226
- // `ephemeralKeys` resource — `sessions` is the one front door for both.
227
-
228
- /** @internal Private protocol surface wrapped by `AbloHttpClient`. */
229
- export interface HttpTransport {
230
- ready(): Promise<void>;
231
- waitForFlush(): Promise<void>;
232
- /** Drains scheduled commits and active requests. */
233
- dispose(): Promise<void>;
234
- purge(): Promise<void>;
235
- /** @internal Live check used only by `context().onChange`. */
236
- readonly onChange: HttpReadOnChange;
237
- readonly commits: CommitResource;
238
- /**
239
- * Claim-ticket operations keyed by `claimId` — the id a queued acquire
240
- * hands back on `AbloClaimedError('claim_queued')`. See
241
- * {@link HttpClaimsResource}.
242
- */
243
- readonly claims: HttpClaimsResource;
244
- readonly logs: HttpLogsResource;
245
- /** Server-confirmed authority of the active bearer, populated by `ready()`. */
246
- readonly identity: EffectiveAuthority | null;
247
- model<T = Record<string, unknown>, Fields = T>(
248
- name: string,
249
- ): HttpTransportModel<T, Fields>;
250
- /**
251
- * Resolve the active bearer credential this client authenticates with — the
252
- * same token its own requests carry in `Authorization`. Returns `null` when
253
- * no credential is configured. Async because the API key may be supplied as
254
- * an async setter. Use it to authenticate a side-band request to the same
255
- * server with the credential this client already holds — no re-mint.
256
- */
257
- getAuthToken(): Promise<string | null>;
258
- /**
259
- * Mint a short-lived scoped session. Minting is a control-plane HTTP call (no
260
- * socket), so it lives on this stateless client too, not only the realtime one.
261
- * `{ user }` mints an `ek_`; `{ agent, can }` mints an `rk_`.
262
- */
263
- readonly sessions: SessionResource<SchemaRecord>;
264
- }
265
-
170
+ const ignoreBestEffortClaimReleaseFailure = (): undefined => undefined;
266
171
  type CommitResponse = CommitReceiptWire;
267
172
 
268
173
  /** @internal Constructed only by the typed HTTP facade. */
@@ -466,6 +371,12 @@ export function createHttpTransport(options: HttpTransportOptions): HttpTranspor
466
371
  return headers;
467
372
  }
468
373
 
374
+ async function activeCredential(): Promise<CredentialProviderResult> {
375
+ return (await resolveCredentialValue(configuredApiKey)) ?? configuredAuthToken ?? null;
376
+ }
377
+
378
+ const access = createSessionAccess(options.session, activeCredential);
379
+
469
380
  function endpoint(path: string): string {
470
381
  const target = new URL(`${apiBaseUrl}${path.startsWith('/') ? path : `/${path}`}`);
471
382
  for (const [key, value] of Object.entries(options.defaultQuery ?? {})) {
@@ -1177,118 +1088,6 @@ export function createHttpTransport(options: HttpTransportOptions): HttpTranspor
1177
1088
  },
1178
1089
  };
1179
1090
 
1180
- // How the stateless client waits its turn. The queued slot is real server
1181
- // state, so one heartbeat per tick does both jobs: it refreshes this
1182
- // waiter's slot and reports the line's answer — `queued` (still waiting) or
1183
- // `held` (granted). The first check comes quickly because most holds are a
1184
- // short claim→write→release; after that the cadence relaxes, with jitter so
1185
- // a fleet of waiters doesn't beat in step.
1186
- const GRANT_POLL_FIRST_MS = 250;
1187
- const GRANT_POLL_INTERVAL_MS = 1_000;
1188
- // An abort cuts the sleep short so the wait ends within a tick of the
1189
- // signal, not at the next scheduled beat.
1190
- const sleep = (ms: number, signal?: AbortSignal): Promise<void> =>
1191
- new Promise((resolve) => {
1192
- const done = (): void => {
1193
- clearTimeout(timer);
1194
- signal?.removeEventListener('abort', done);
1195
- resolve();
1196
- };
1197
- const timer = setTimeout(done, ms);
1198
- signal?.addEventListener('abort', done, { once: true });
1199
- });
1200
-
1201
- async function awaitGrantOverHttp(
1202
- targetLabel: string,
1203
- queued: ClaimQueuedResponse,
1204
- options: ResolvedClaimContentionOptions,
1205
- ): Promise<{ id: string; fenceToken?: number }> {
1206
- // The queued reply is a claim resource in its waiting state, so the
1207
- // handle is its `id` — same rule as the 201 and the poll.
1208
- const claimId = queued.id;
1209
- const { signal } = options;
1210
- // Leave the line before rejecting: an abandoned slot would otherwise sit
1211
- // in the queue until its TTL lapses, stalling every waiter behind it.
1212
- const rejectAndLeave = async (error: AbloClaimedError): Promise<never> => {
1213
- await claims.release({ claimId }).catch(() => {});
1214
- throw error;
1215
- };
1216
-
1217
- emitClaimStatus(options.onStatus, {
1218
- type: 'queued',
1219
- claimId,
1220
- position: queued.position,
1221
- ahead: queued.position + 1,
1222
- });
1223
-
1224
- if (options.maxDepth !== undefined && queued.position >= options.maxDepth) {
1225
- return rejectAndLeave(
1226
- new AbloClaimedError(
1227
- `Claim queue for ${targetLabel} is ${queued.position} deep (max ${options.maxDepth}).`,
1228
- { code: 'queue_too_deep' }
1229
- )
1230
- );
1231
- }
1232
-
1233
- const deadline =
1234
- options.timeoutMs !== undefined ? Date.now() + options.timeoutMs : undefined;
1235
- let delay = GRANT_POLL_FIRST_MS;
1236
- for (;;) {
1237
- if (signal?.aborted) {
1238
- return rejectAndLeave(
1239
- new AbloClaimedError(
1240
- `The wait for the claim on ${targetLabel} was aborted before the grant arrived.`,
1241
- { code: 'claim_wait_aborted' }
1242
- )
1243
- );
1244
- }
1245
- if (deadline !== undefined && Date.now() >= deadline) {
1246
- return rejectAndLeave(
1247
- new AbloClaimedError(
1248
- `Timed out after ${options.timeoutMs}ms waiting for the queue grant on ${targetLabel}.`,
1249
- { code: 'grant_timeout' }
1250
- )
1251
- );
1252
- }
1253
- await sleep(
1254
- deadline !== undefined ? Math.min(delay, Math.max(0, deadline - Date.now())) : delay,
1255
- signal
1256
- );
1257
- if (signal?.aborted) {
1258
- return rejectAndLeave(
1259
- new AbloClaimedError(
1260
- `The wait for the claim on ${targetLabel} was aborted before the grant arrived.`,
1261
- { code: 'claim_wait_aborted' }
1262
- )
1263
- );
1264
- }
1265
- delay = GRANT_POLL_INTERVAL_MS * (0.85 + Math.random() * 0.3);
1266
- // A lease that ended answers the beat with 409 `claim_lost`, which the
1267
- // wire error mapping raises as AbloClaimedError before this reads
1268
- // anything — the wait fails with the loss, as the socket wait does.
1269
- const beat = await claims.heartbeat({ claimId });
1270
- if (beat.status !== 'held') continue;
1271
- // Granted. The heartbeat ack does not carry the fence token — the claim
1272
- // state does, server-stamped at grant.
1273
- const state = await claims.retrieve({ claimId });
1274
- if (state.status !== 'active') {
1275
- return rejectAndLeave(
1276
- new AbloClaimedError(`Claim lost while queued for ${targetLabel}.`, {
1277
- code: 'claim_lost',
1278
- })
1279
- );
1280
- }
1281
- emitClaimStatus(options.onStatus, {
1282
- type: 'granted',
1283
- claimId,
1284
- waited: true,
1285
- });
1286
- return state.fenceToken !== undefined
1287
- ? { id: claimId, fenceToken: state.fenceToken }
1288
- : { id: claimId };
1289
- }
1290
- }
1291
-
1292
1091
  async function applyClaimedPolicy(
1293
1092
  target: Partial<ModelTarget>,
1294
1093
  options?: ClaimedOptions,
@@ -1742,7 +1541,8 @@ export function createHttpTransport(options: HttpTransportOptions): HttpTranspor
1742
1541
  // the manual ticket surface.)
1743
1542
  if (body.status === 'queued') {
1744
1543
  try {
1745
- return await awaitGrantOverHttp(
1544
+ return await awaitClaimGrantOverHttp(
1545
+ claims,
1746
1546
  `${name}/${params.id}`,
1747
1547
  body,
1748
1548
  contention,
@@ -1775,8 +1575,7 @@ export function createHttpTransport(options: HttpTransportOptions): HttpTranspor
1775
1575
  ): Promise<void> => {
1776
1576
  const entityId = isClaimHandle(params) ? params.target.id : params.id;
1777
1577
  if (claimId && options.releaseDispatchedClaim) {
1778
- options.releaseDispatchedClaim({ claimId, entityType: name, entityId });
1779
- return Promise.resolve();
1578
+ return options.releaseDispatchedClaim({ claimId, entityType: name, entityId });
1780
1579
  }
1781
1580
  return requestRaw(claimPath(entityId), {
1782
1581
  method: 'DELETE',
@@ -1904,7 +1703,7 @@ export function createHttpTransport(options: HttpTransportOptions): HttpTranspor
1904
1703
  description: claimDescription(params),
1905
1704
  release,
1906
1705
  revoke: () => {
1907
- void release().catch(() => {});
1706
+ void release().catch(ignoreBestEffortClaimReleaseFailure);
1908
1707
  },
1909
1708
  heartbeat,
1910
1709
  [Symbol.asyncDispose]: release,
@@ -1919,7 +1718,7 @@ export function createHttpTransport(options: HttpTransportOptions): HttpTranspor
1919
1718
  // this credential until the TTL lapses, behind no handle anyone can
1920
1719
  // release, and every other participant is refused for the duration.
1921
1720
  if (data === undefined) {
1922
- await release().catch(() => undefined);
1721
+ await release().catch(ignoreBestEffortClaimReleaseFailure);
1923
1722
  throw new AbloNotFoundError(
1924
1723
  `Cannot claim ${name}/${params.id}: it does not exist (or is outside this credential's scope). ` +
1925
1724
  `To hold a key before its row exists, claim it by id alone: claim('${params.id}') returns a lease without a snapshot.`,
@@ -2026,7 +1825,7 @@ export function createHttpTransport(options: HttpTransportOptions): HttpTranspor
2026
1825
  claim: undefined,
2027
1826
  });
2028
1827
  } finally {
2029
- await releaseClaim({ id }, claimId).catch(() => {});
1828
+ await releaseClaim({ id }, claimId).catch(ignoreBestEffortClaimReleaseFailure);
2030
1829
  }
2031
1830
  };
2032
1831
 
@@ -2222,64 +2021,12 @@ export function createHttpTransport(options: HttpTransportOptions): HttpTranspor
2222
2021
  claims,
2223
2022
  logs,
2224
2023
  model,
2225
- sessions: {
2226
- async create(params: CreateSessionParams<SchemaRecord>): Promise<AbloSession> {
2227
- // Stateless mint: the configured key is the control-plane credential here
2228
- // (no startup `rk_` exchange runs on this client). It reuses the resolved
2229
- // base URL and fetch; the shared `mintSession` handles the two server routes.
2230
- const apiKey = await resolveApiKeyValue(configuredApiKey);
2231
- if (!apiKey) {
2232
- throw new AbloAuthenticationError(
2233
- 'sessions.create requires a secret (sk_) API key — call it from your backend, not the browser.',
2234
- { code: 'apikey_missing' }
2235
- );
2236
- }
2237
- // A transport built without a schema has no way to translate `can`'s
2238
- // schema keys into the type names the server gates on. Minting anyway
2239
- // would spell every override wrong and surface as
2240
- // `capability_scope_denied` on the agent's first write, so refuse here
2241
- // instead of guessing.
2242
- if (!options.modelTypenames) {
2243
- throw new AbloValidationError(
2244
- 'sessions.create needs the schema this client is bound to. Construct it ' +
2245
- "through Ablo({ schema, apiKey, transport: 'http' }) rather than the " +
2246
- 'bare transport.',
2247
- { code: 'invalid_options', param: 'schema' },
2248
- );
2249
- }
2250
- return mintSession(params, {
2251
- apiKey,
2252
- baseUrl: apiBaseUrl,
2253
- modelTypenames: options.modelTypenames,
2254
- ...(options.fetch ? { fetch: options.fetch } : {}),
2255
- });
2256
- },
2257
- async revoke({ id }) {
2258
- const apiKey = await resolveApiKeyValue(configuredApiKey);
2259
- return revokeCapability({
2260
- apiKey: apiKey ?? '',
2261
- baseUrl: apiBaseUrl,
2262
- id,
2263
- ...(options.fetch ? { fetch: options.fetch } : {}),
2264
- });
2265
- },
2266
- async rotate({ id, graceSeconds, ttlSeconds }) {
2267
- const apiKey = await resolveApiKeyValue(configuredApiKey);
2268
- return rotateCapability({
2269
- apiKey: apiKey ?? '',
2270
- baseUrl: apiBaseUrl,
2271
- id,
2272
- ...(graceSeconds !== undefined ? { graceSeconds } : {}),
2273
- ...(ttlSeconds !== undefined ? { ttlSeconds } : {}),
2274
- ...(options.fetch ? { fetch: options.fetch } : {}),
2275
- });
2276
- },
2277
- },
2278
2024
  async getAuthToken(): Promise<string | null> {
2279
2025
  // Mirror `authHeaders()`: a configured API key wins, else the
2280
2026
  // construction-time auth token. Resolve the (possibly async) key setter.
2281
- return (await resolveApiKeyValue(configuredApiKey)) ?? configuredAuthToken ?? null;
2027
+ return credentialToken(await activeCredential());
2282
2028
  },
2029
+ access,
2283
2030
  };
2284
2031
  }
2285
2032
 
@@ -17,7 +17,6 @@ import type {
17
17
  ClaimQueued,
18
18
  ClaimLost,
19
19
  ClaimRejection,
20
- ClaimExpired,
21
20
  } from '../../coordination/schema.js';
22
21
  import type { ClaimEvent, ClaimCounterparty } from '../../claims/events.js';
23
22
  import type { AssertExact } from '../../types/assertExact.js';
@@ -105,7 +104,6 @@ interface ClaimFrameByPhase {
105
104
  queued: ClaimQueued;
106
105
  lost: ClaimLost;
107
106
  rejected: ClaimRejection;
108
- expired: ClaimExpired;
109
107
  }
110
108
 
111
109
  // The phases this map covers and the phases a ClaimEvent can report are the
@@ -45,16 +45,6 @@ export interface PendingCommit {
45
45
  readonly returnReceipt?: boolean;
46
46
  }
47
47
 
48
- /**
49
- * In-flight `claim` request record, keyed by claimId. Resolved when the
50
- * matching `claim_ack` arrives, or rejected on timeout/disconnect.
51
- */
52
- export interface PendingClaim {
53
- resolve: (value: { syncGroups: string[]; ttlSeconds?: number }) => void;
54
- reject: (err: Error) => void;
55
- timeout: ReturnType<typeof setTimeout>;
56
- }
57
-
58
48
  /**
59
49
  * An in-flight `update_subscription` request awaiting its
60
50
  * `subscription_ack`. The wire carries no correlation id, so requests are
@@ -109,8 +99,6 @@ export interface WsSession {
109
99
  observability: SocketObservability;
110
100
  /** In-flight commit acks keyed by clientTxId. */
111
101
  pendingMutations: Map<string, PendingCommit>;
112
- /** In-flight claim acks keyed by claimId. */
113
- pendingClaims: Map<string, PendingClaim>;
114
102
  /** Removes and returns the oldest in-flight `update_subscription` request. */
115
103
  shiftPendingSubscription(): PendingSubscription | undefined;
116
104
  /** Connection options subset the handlers write back (acked sync groups). */
@@ -290,84 +278,6 @@ const handleMutationResult: WsFrameHandler = (session, message) => {
290
278
  }
291
279
  };
292
280
 
293
- /**
294
- * Handles the acknowledgement of a `claim` request. The frame has the shape
295
- * `{ type: 'claim_ack', payload: { claimId, success, syncGroups?,
296
- * ttlSeconds?, error? } }`.
297
- */
298
- const handleClaimAck: WsFrameHandler = (session, message) => {
299
- const p = (message.payload ?? {}) as Record<string, unknown>;
300
- const { claimId, success, syncGroups, ttlSeconds, error } = p;
301
- const pending =
302
- typeof claimId === 'string'
303
- ? session.pendingClaims.get(claimId)
304
- : undefined;
305
- if (!pending) return;
306
- clearTimeout(pending.timeout);
307
- // `pending` exists ⇒ claimId was a string key (the guard above).
308
- session.pendingClaims.delete(claimId as string);
309
- if (success) {
310
- pending.resolve({
311
- syncGroups: Array.isArray(syncGroups) ? syncGroups : [],
312
- ttlSeconds: typeof ttlSeconds === 'number' ? ttlSeconds : undefined,
313
- });
314
- } else {
315
- const err = error as
316
- | {
317
- code?: unknown;
318
- message?: unknown;
319
- request_id?: unknown;
320
- event_id?: unknown;
321
- }
322
- | undefined;
323
- const code =
324
- err?.code && typeof err.code === 'string'
325
- ? err.code
326
- : 'claim_rejected';
327
- const msg =
328
- err?.message && typeof err.message === 'string'
329
- ? err.message
330
- : 'claim rejected by server';
331
- const requestId =
332
- typeof err?.request_id === 'string' ? err.request_id : undefined;
333
- const eventId =
334
- typeof err?.event_id === 'string' ? err.event_id : undefined;
335
- const details = eventId === undefined ? undefined : { event_id: eventId };
336
- // Capability denials get the typed CapabilityError so
337
- // callers can read `.requiredCapability` and attenuate-
338
- // and-retry the claim with a narrower token.
339
- if (
340
- code === 'capability_scope_denied' ||
341
- code === 'capability_invalid'
342
- ) {
343
- const rc = (error as { requiredCapability?: unknown } | undefined)
344
- ?.requiredCapability;
345
- const requiredCapability =
346
- rc != null &&
347
- typeof rc === 'object' &&
348
- typeof (rc as { scope?: unknown }).scope === 'string'
349
- ? (rc as RequiredCapability)
350
- : undefined;
351
- pending.reject(
352
- new CapabilityError(code, msg, requiredCapability, {
353
- ...(requestId !== undefined ? { requestId } : {}),
354
- ...(details !== undefined ? { details } : {}),
355
- }),
356
- );
357
- } else {
358
- // Route through the shared factory so a failed claim_ack is a
359
- // typed AbloError (registry code → right subclass), symmetric
360
- // with the commit `mutation_result` path — never a bare Error.
361
- pending.reject(
362
- errorFromWire(msg, {
363
- code,
364
- requestId,
365
- details,
366
- }),
367
- );
368
- }
369
- }
370
- };
371
281
 
372
282
  /**
373
283
  * Handles the acknowledgement of an `update_subscription` request. The wire
@@ -448,7 +358,6 @@ export const wsFrameHandlers: Record<string, WsFrameHandler> = {
448
358
  bootstrap_response: (session, message) => { session.handleBootstrapResponse(message.payload); },
449
359
  presence_update: (session, message) => { session.handlePresenceUpdate(message); },
450
360
  mutation_result: handleMutationResult,
451
- claim_ack: handleClaimAck,
452
361
  subscription_ack: handleSubscriptionAck,
453
362
  delta: handleDeltaFrame,
454
363
  };
@@ -497,17 +406,6 @@ const validatedFrameHandlers: Record<
497
406
  session.handlePresenceUpdate({ payload });
498
407
  },
499
408
  ),
500
- claim_expired: validating(
501
- WS_INBOUND_FRAMES.claim_expired.payload,
502
- 'claim_expired',
503
- (session, payload) => {
504
- // Server-initiated expiry. The claim is already inactive by the time
505
- // this arrives, so a consumer either re-claims with a fresh credential
506
- // or accepts the drop.
507
- recordClaim(tracePorts(session), 'expired', payload);
508
- session.emit('claim_expired', payload);
509
- },
510
- ),
511
409
  claim_rejected: validating(
512
410
  WS_INBOUND_FRAMES.claim_rejected.payload,
513
411
  'claim_rejected',
@@ -527,6 +425,13 @@ const validatedFrameHandlers: Record<
527
425
  session.emit('claim_acquired', payload);
528
426
  },
529
427
  ),
428
+ claim_abandon_ack: validating(
429
+ WS_INBOUND_FRAMES.claim_abandon_ack.payload,
430
+ 'claim_abandon_ack',
431
+ (session, payload) => {
432
+ session.emit('claim_abandon_ack', payload);
433
+ },
434
+ ),
530
435
  claim_queued: validating(
531
436
  WS_INBOUND_FRAMES.claim_queued.payload,
532
437
  'claim_queued',