@abloatai/ablo 0.26.0 → 0.27.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.
- package/CHANGELOG.md +14 -0
- package/README.md +101 -85
- package/dist/BaseSyncedStore.d.ts +85 -88
- package/dist/BaseSyncedStore.js +131 -147
- package/dist/Database.d.ts +54 -68
- package/dist/Database.js +97 -113
- package/dist/{ObjectPool.d.ts → InstanceCache.d.ts} +10 -13
- package/dist/{ObjectPool.js → InstanceCache.js} +85 -83
- package/dist/LazyReferenceCollection.d.ts +11 -15
- package/dist/LazyReferenceCollection.js +12 -16
- package/dist/Model.d.ts +37 -52
- package/dist/Model.js +46 -61
- package/dist/ModelRegistry.d.ts +21 -19
- package/dist/ModelRegistry.js +23 -27
- package/dist/NetworkMonitor.d.ts +5 -6
- package/dist/NetworkMonitor.js +5 -6
- package/dist/SyncClient.d.ts +112 -112
- package/dist/SyncClient.js +165 -172
- package/dist/adapters/alwaysOnline.d.ts +6 -8
- package/dist/adapters/alwaysOnline.js +6 -8
- package/dist/adapters/inMemoryStorage.d.ts +9 -9
- package/dist/adapters/inMemoryStorage.js +9 -9
- package/dist/agent/Agent.d.ts +27 -32
- package/dist/agent/Agent.js +18 -19
- package/dist/agent/index.d.ts +4 -4
- package/dist/agent/index.js +5 -5
- package/dist/agent/session.d.ts +47 -44
- package/dist/agent/session.js +37 -48
- package/dist/agent/types.d.ts +26 -31
- package/dist/agent/types.js +6 -7
- package/dist/ai-sdk/coordinatedTool.d.ts +108 -0
- package/dist/ai-sdk/{coordinated-tool.js → coordinatedTool.js} +44 -38
- package/dist/ai-sdk/coordinationContext.d.ts +46 -0
- package/dist/ai-sdk/{coordination-context.js → coordinationContext.js} +26 -33
- package/dist/ai-sdk/index.d.ts +25 -22
- package/dist/ai-sdk/index.js +25 -22
- package/dist/ai-sdk/wrap.d.ts +6 -7
- package/dist/ai-sdk/wrap.js +1 -1
- package/dist/auth/credentialPolicy.d.ts +69 -74
- package/dist/auth/credentialPolicy.js +51 -56
- package/dist/auth/credentialSource.d.ts +6 -5
- package/dist/auth/credentialSource.js +9 -10
- package/dist/auth/index.d.ts +59 -58
- package/dist/auth/index.js +31 -37
- package/dist/auth/schemas.d.ts +5 -4
- package/dist/auth/schemas.js +5 -4
- package/dist/batching/index.d.ts +19 -21
- package/dist/batching/index.js +14 -17
- package/dist/cli.cjs +167 -119
- package/dist/client/Ablo.d.ts +73 -73
- package/dist/client/Ablo.js +125 -160
- package/dist/client/ApiClient.d.ts +30 -19
- package/dist/client/ApiClient.js +133 -38
- package/dist/client/auth.d.ts +47 -47
- package/dist/client/auth.js +108 -117
- package/dist/client/claimHeartbeatLoop.d.ts +50 -0
- package/dist/client/claimHeartbeatLoop.js +88 -0
- package/dist/client/consoleLogger.d.ts +5 -6
- package/dist/client/consoleLogger.js +5 -6
- package/dist/client/createInternalComponents.d.ts +14 -17
- package/dist/client/createInternalComponents.js +25 -30
- package/dist/client/createModelProxy.d.ts +130 -120
- package/dist/client/createModelProxy.js +152 -122
- package/dist/client/credentialEndpoint.d.ts +40 -42
- package/dist/client/credentialEndpoint.js +35 -36
- package/dist/client/functionalUpdate.d.ts +29 -27
- package/dist/client/functionalUpdate.js +21 -21
- package/dist/client/hostedEndpoints.d.ts +9 -12
- package/dist/client/hostedEndpoints.js +9 -12
- package/dist/client/httpClient.d.ts +57 -53
- package/dist/client/httpClient.js +29 -31
- package/dist/client/identity.d.ts +15 -20
- package/dist/client/identity.js +47 -58
- package/dist/client/modelRegistration.d.ts +5 -9
- package/dist/client/modelRegistration.js +67 -87
- package/dist/client/options.d.ts +134 -157
- package/dist/client/options.js +3 -7
- package/dist/client/registerDataSource.d.ts +9 -9
- package/dist/client/registerDataSource.js +15 -16
- package/dist/client/resourceTypes.d.ts +64 -75
- package/dist/client/resourceTypes.js +4 -10
- package/dist/client/schemaConfig.d.ts +31 -43
- package/dist/client/schemaConfig.js +38 -50
- package/dist/client/sessionMint.d.ts +16 -12
- package/dist/client/sessionMint.js +26 -31
- package/dist/client/validateAbloOptions.d.ts +12 -14
- package/dist/client/validateAbloOptions.js +8 -9
- package/dist/client/writeOptionsSchema.d.ts +18 -16
- package/dist/client/writeOptionsSchema.js +23 -20
- package/dist/client/wsMutationExecutor.d.ts +15 -20
- package/dist/client/wsMutationExecutor.js +17 -23
- package/dist/context.d.ts +6 -4
- package/dist/context.js +6 -4
- package/dist/coordination/index.d.ts +10 -8
- package/dist/coordination/index.js +14 -12
- package/dist/coordination/schema.d.ts +176 -128
- package/dist/coordination/schema.js +197 -133
- package/dist/coordination/trace.d.ts +9 -10
- package/dist/coordination/trace.js +13 -14
- package/dist/core/DatabaseManager.d.ts +5 -7
- package/dist/core/DatabaseManager.js +15 -19
- package/dist/core/QueryProcessor.d.ts +7 -9
- package/dist/core/QueryProcessor.js +22 -28
- package/dist/core/QueryView.d.ts +8 -8
- package/dist/core/QueryView.js +2 -2
- package/dist/core/StoreManager.d.ts +12 -14
- package/dist/core/StoreManager.js +21 -24
- package/dist/core/ViewRegistry.d.ts +5 -5
- package/dist/core/ViewRegistry.js +4 -4
- package/dist/core/index.d.ts +17 -12
- package/dist/core/index.js +32 -26
- package/dist/core/openIDBWithTimeout.d.ts +38 -36
- package/dist/core/openIDBWithTimeout.js +42 -43
- package/dist/core/queryUtils.d.ts +45 -0
- package/dist/core/queryUtils.js +69 -0
- package/dist/core/storeContract.d.ts +63 -61
- package/dist/core/storeContract.js +8 -12
- package/dist/environment.d.ts +28 -0
- package/dist/environment.js +21 -0
- package/dist/errorCodes.d.ts +107 -99
- package/dist/errorCodes.js +131 -132
- package/dist/errors.d.ts +160 -166
- package/dist/errors.js +155 -158
- package/dist/index.d.ts +30 -27
- package/dist/index.js +89 -86
- package/dist/interfaces/index.d.ts +102 -113
- package/dist/interfaces/index.js +5 -4
- package/dist/keys/index.d.ts +27 -29
- package/dist/keys/index.js +41 -40
- package/dist/mutators/RecordingTransaction.d.ts +16 -16
- package/dist/mutators/RecordingTransaction.js +31 -37
- package/dist/mutators/Transaction.d.ts +18 -26
- package/dist/mutators/Transaction.js +14 -20
- package/dist/mutators/UndoManager.d.ts +122 -131
- package/dist/mutators/UndoManager.js +145 -156
- package/dist/mutators/defineMutators.d.ts +23 -34
- package/dist/mutators/defineMutators.js +14 -20
- package/dist/mutators/inverseOp.d.ts +12 -15
- package/dist/mutators/inverseOp.js +12 -15
- package/dist/mutators/mutateActions.d.ts +10 -9
- package/dist/mutators/mutateActions.js +1 -1
- package/dist/mutators/readerActions.d.ts +9 -8
- package/dist/mutators/readerActions.js +2 -2
- package/dist/mutators/undoApply.d.ts +31 -27
- package/dist/mutators/undoApply.js +26 -24
- package/dist/policy/index.d.ts +5 -3
- package/dist/policy/index.js +5 -3
- package/dist/policy/types.d.ts +104 -100
- package/dist/policy/types.js +67 -66
- package/dist/query/client.d.ts +28 -23
- package/dist/query/client.js +45 -43
- package/dist/query/types.d.ts +37 -60
- package/dist/query/types.js +13 -33
- package/dist/react/AbloProvider.d.ts +1 -1
- package/dist/react/AbloProvider.js +2 -2
- package/dist/react/context.d.ts +25 -28
- package/dist/react/context.js +9 -10
- package/dist/react/index.d.ts +41 -42
- package/dist/react/index.js +37 -38
- package/dist/react/internalContext.d.ts +17 -19
- package/dist/react/useAblo.d.ts +23 -22
- package/dist/react/useAblo.js +16 -14
- package/dist/react/useCurrentUserId.d.ts +8 -7
- package/dist/react/useCurrentUserId.js +8 -7
- package/dist/react/useErrorListener.d.ts +7 -7
- package/dist/react/useErrorListener.js +10 -11
- package/dist/react/useMutationFailureListener.d.ts +8 -8
- package/dist/react/useMutationFailureListener.js +8 -8
- package/dist/react/useMutators.d.ts +11 -11
- package/dist/react/useMutators.js +3 -3
- package/dist/react/useReactive.js +2 -2
- package/dist/react/useSyncStatus.d.ts +4 -6
- package/dist/react/useUndoScope.d.ts +7 -9
- package/dist/react/useUndoScope.js +1 -1
- package/dist/schema/coordination.d.ts +21 -25
- package/dist/schema/coordination.js +21 -25
- package/dist/schema/ddl.d.ts +43 -39
- package/dist/schema/ddl.js +75 -68
- package/dist/schema/ddlLock.d.ts +20 -24
- package/dist/schema/ddlLock.js +18 -23
- package/dist/schema/diff.d.ts +99 -61
- package/dist/schema/diff.js +43 -34
- package/dist/schema/field.d.ts +37 -42
- package/dist/schema/field.js +35 -48
- package/dist/schema/generate.d.ts +12 -12
- package/dist/schema/generate.js +12 -12
- package/dist/schema/index.d.ts +2 -2
- package/dist/schema/index.js +21 -23
- package/dist/schema/model.d.ts +118 -143
- package/dist/schema/model.js +22 -33
- package/dist/schema/openapi.d.ts +10 -9
- package/dist/schema/openapi.js +5 -3
- package/dist/schema/queries.d.ts +29 -31
- package/dist/schema/queries.js +23 -25
- package/dist/schema/relation.d.ts +89 -99
- package/dist/schema/relation.js +13 -13
- package/dist/schema/residency.d.ts +16 -13
- package/dist/schema/residency.js +16 -13
- package/dist/schema/roles.d.ts +36 -43
- package/dist/schema/roles.js +31 -37
- package/dist/schema/schema.d.ts +33 -42
- package/dist/schema/schema.js +31 -32
- package/dist/schema/select.d.ts +13 -13
- package/dist/schema/select.js +13 -13
- package/dist/schema/serialize.d.ts +28 -31
- package/dist/schema/serialize.js +27 -31
- package/dist/schema/sugar.d.ts +17 -32
- package/dist/schema/sugar.js +14 -29
- package/dist/schema/{sync-delta-row.d.ts → syncDeltaRow.d.ts} +26 -49
- package/dist/schema/syncDeltaRow.js +89 -0
- package/dist/schema/tenancy.d.ts +44 -46
- package/dist/schema/tenancy.js +46 -48
- package/dist/server/adapter.d.ts +58 -58
- package/dist/server/adapter.js +13 -14
- package/dist/server/commit.d.ts +60 -64
- package/dist/server/index.d.ts +9 -10
- package/dist/server/index.js +1 -1
- package/dist/server/readConfig.d.ts +70 -0
- package/dist/server/readConfig.js +8 -0
- package/dist/server/storageMode.d.ts +23 -0
- package/dist/server/storageMode.js +17 -0
- package/dist/source/adapter.d.ts +30 -25
- package/dist/source/adapter.js +10 -10
- package/dist/source/adapters/drizzle.d.ts +28 -23
- package/dist/source/adapters/drizzle.js +30 -25
- package/dist/source/adapters/kysely.d.ts +27 -25
- package/dist/source/adapters/kysely.js +24 -23
- package/dist/source/adapters/memory.d.ts +8 -7
- package/dist/source/adapters/memory.js +9 -8
- package/dist/source/adapters/prisma.d.ts +13 -12
- package/dist/source/adapters/prisma.js +22 -25
- package/dist/source/conformance.d.ts +18 -11
- package/dist/source/conformance.js +17 -11
- package/dist/source/connector.d.ts +31 -32
- package/dist/source/connector.js +28 -28
- package/dist/source/connectorProtocol.d.ts +160 -0
- package/dist/source/connectorProtocol.js +162 -0
- package/dist/source/contract.d.ts +26 -27
- package/dist/source/contract.js +28 -29
- package/dist/source/factory.d.ts +46 -58
- package/dist/source/factory.js +22 -27
- package/dist/source/index.d.ts +7 -9
- package/dist/source/index.js +12 -14
- package/dist/source/migrations.d.ts +9 -9
- package/dist/source/migrations.js +9 -9
- package/dist/source/next.d.ts +9 -10
- package/dist/source/next.js +6 -7
- package/dist/source/pushQueue.d.ts +69 -47
- package/dist/source/pushQueue.js +32 -28
- package/dist/source/signing.d.ts +46 -17
- package/dist/source/signing.js +28 -11
- package/dist/source/types.d.ts +121 -104
- package/dist/source/types.js +13 -14
- package/dist/stores/ObjectStore.d.ts +10 -11
- package/dist/stores/ObjectStore.js +11 -12
- package/dist/stores/ObjectStoreContract.d.ts +12 -15
- package/dist/stores/SyncActionStore.d.ts +7 -11
- package/dist/stores/SyncActionStore.js +13 -17
- package/dist/surface.d.ts +27 -20
- package/dist/surface.js +27 -20
- package/dist/sync/{BootstrapHelper.d.ts → BootstrapFetcher.d.ts} +36 -42
- package/dist/sync/{BootstrapHelper.js → BootstrapFetcher.js} +76 -76
- package/dist/sync/ConnectionManager.d.ts +39 -50
- package/dist/sync/ConnectionManager.js +55 -66
- package/dist/sync/NetworkProbe.d.ts +24 -29
- package/dist/sync/NetworkProbe.js +63 -69
- package/dist/sync/{HydrationCoordinator.d.ts → OnDemandLoader.d.ts} +42 -41
- package/dist/sync/{HydrationCoordinator.js → OnDemandLoader.js} +59 -54
- package/dist/sync/{AreaOfInterestManager.d.ts → SubscriptionManager.d.ts} +43 -57
- package/dist/sync/{AreaOfInterestManager.js → SubscriptionManager.js} +43 -51
- package/dist/sync/SyncWebSocket.d.ts +139 -165
- package/dist/sync/SyncWebSocket.js +191 -223
- package/dist/sync/awaitClaimGrant.d.ts +18 -18
- package/dist/sync/awaitClaimGrant.js +11 -11
- package/dist/sync/bootstrapApply.d.ts +34 -24
- package/dist/sync/bootstrapApply.js +27 -19
- package/dist/sync/commitFrames.d.ts +21 -20
- package/dist/sync/commitFrames.js +18 -18
- package/dist/sync/createClaimStream.d.ts +23 -22
- package/dist/sync/createClaimStream.js +105 -23
- package/dist/sync/createPresenceStream.d.ts +19 -18
- package/dist/sync/createPresenceStream.js +25 -26
- package/dist/sync/createSnapshot.d.ts +12 -14
- package/dist/sync/createSnapshot.js +20 -26
- package/dist/sync/credentialLifecycle.d.ts +104 -104
- package/dist/sync/credentialLifecycle.js +140 -147
- package/dist/sync/deltaPipeline.d.ts +36 -34
- package/dist/sync/deltaPipeline.js +64 -65
- package/dist/sync/groupChange.d.ts +63 -61
- package/dist/sync/groupChange.js +74 -78
- package/dist/sync/heartbeat.d.ts +34 -33
- package/dist/sync/heartbeat.js +31 -31
- package/dist/sync/participants.d.ts +19 -19
- package/dist/sync/schemas.d.ts +3 -2
- package/dist/sync/schemas.js +14 -10
- package/dist/sync/syncCursor.d.ts +17 -21
- package/dist/sync/syncCursor.js +17 -21
- package/dist/sync/syncPlan.d.ts +28 -36
- package/dist/sync/syncPlan.js +18 -19
- package/dist/sync/syncPosition.d.ts +54 -49
- package/dist/sync/syncPosition.js +57 -52
- package/dist/sync/wsFrameHandlers.d.ts +35 -36
- package/dist/sync/wsFrameHandlers.js +63 -67
- package/dist/testing/fixtures/bootstrap.d.ts +12 -6
- package/dist/testing/fixtures/bootstrap.js +12 -6
- package/dist/testing/fixtures/deltas.d.ts +30 -33
- package/dist/testing/fixtures/deltas.js +30 -33
- package/dist/testing/fixtures/models.d.ts +11 -10
- package/dist/testing/fixtures/models.js +11 -10
- package/dist/testing/helpers/{react-wrapper.d.ts → reactWrapper.d.ts} +13 -10
- package/dist/testing/helpers/{react-wrapper.js → reactWrapper.js} +15 -12
- package/dist/testing/helpers/{sync-engine-harness.d.ts → syncEngineHarness.d.ts} +17 -15
- package/dist/testing/helpers/{sync-engine-harness.js → syncEngineHarness.js} +12 -10
- package/dist/testing/helpers/wait.d.ts +13 -8
- package/dist/testing/helpers/wait.js +13 -8
- package/dist/testing/index.d.ts +3 -3
- package/dist/testing/index.js +2 -2
- package/dist/testing/mocks/MockMutationExecutor.d.ts +18 -17
- package/dist/testing/mocks/MockMutationExecutor.js +15 -14
- package/dist/testing/mocks/MockNetworkMonitor.d.ts +8 -8
- package/dist/testing/mocks/MockNetworkMonitor.js +8 -8
- package/dist/testing/mocks/MockSyncContext.d.ts +20 -17
- package/dist/testing/mocks/MockSyncContext.js +15 -13
- package/dist/testing/mocks/MockSyncStore.d.ts +10 -10
- package/dist/testing/mocks/MockSyncStore.js +11 -11
- package/dist/testing/mocks/MockWebSocket.d.ts +26 -22
- package/dist/testing/mocks/MockWebSocket.js +22 -21
- package/dist/transactions/TransactionQueue.d.ts +181 -176
- package/dist/transactions/TransactionQueue.js +338 -350
- package/dist/transactions/TransactionStore.d.ts +6 -4
- package/dist/transactions/TransactionStore.js +6 -4
- package/dist/transactions/UnconfirmedWrites.d.ts +82 -0
- package/dist/transactions/UnconfirmedWrites.js +104 -0
- package/dist/transactions/coalesceRules.d.ts +41 -17
- package/dist/transactions/coalesceRules.js +40 -17
- package/dist/transactions/commitPayload.d.ts +48 -52
- package/dist/transactions/commitPayload.js +48 -57
- package/dist/transactions/deltaConfirmation.d.ts +20 -22
- package/dist/transactions/deltaConfirmation.js +37 -45
- package/dist/transactions/optimisticApply.d.ts +49 -0
- package/dist/transactions/optimisticApply.js +65 -0
- package/dist/transactions/{persistedReplay.d.ts → replayValidation.d.ts} +28 -22
- package/dist/transactions/{persistedReplay.js → replayValidation.js} +33 -27
- package/dist/types/global.d.ts +46 -41
- package/dist/types/global.js +20 -19
- package/dist/types/index.d.ts +71 -77
- package/dist/types/index.js +22 -22
- package/dist/types/modelData.d.ts +6 -8
- package/dist/types/modelData.js +5 -7
- package/dist/types/participant.d.ts +10 -11
- package/dist/types/participant.js +6 -8
- package/dist/types/streams.d.ts +208 -195
- package/dist/types/streams.js +7 -7
- package/dist/utils/asyncIterator.d.ts +25 -32
- package/dist/utils/asyncIterator.js +25 -32
- package/dist/utils/duration.d.ts +12 -15
- package/dist/utils/duration.js +12 -15
- package/dist/utils/mobxSetup.d.ts +53 -0
- package/dist/utils/{mobx-setup.js → mobxSetup.js} +42 -98
- package/dist/webhooks/events.d.ts +21 -16
- package/dist/webhooks/events.js +10 -8
- package/dist/webhooks/index.d.ts +5 -7
- package/dist/webhooks/index.js +5 -7
- package/dist/{schema/sync-delta-wire.d.ts → wire/delta.d.ts} +58 -41
- package/dist/wire/delta.js +114 -0
- package/dist/wire/errorEnvelope.d.ts +30 -31
- package/dist/wire/errorEnvelope.js +34 -40
- package/dist/wire/frames.d.ts +79 -86
- package/dist/wire/frames.js +26 -33
- package/dist/wire/index.d.ts +14 -12
- package/dist/wire/index.js +30 -26
- package/dist/wire/listEnvelope.d.ts +16 -23
- package/dist/wire/listEnvelope.js +7 -6
- package/dist/wire/protocol.d.ts +25 -32
- package/dist/wire/protocol.js +25 -32
- package/dist/wire/protocolVersion.d.ts +44 -40
- package/dist/wire/protocolVersion.js +44 -40
- package/docs/coordination.md +59 -0
- package/package.json +11 -10
- package/dist/ai-sdk/coordinated-tool.d.ts +0 -101
- package/dist/ai-sdk/coordination-context.d.ts +0 -52
- package/dist/core/query-utils.d.ts +0 -34
- package/dist/core/query-utils.js +0 -59
- package/dist/schema/sync-delta-row.js +0 -103
- package/dist/schema/sync-delta-wire.js +0 -102
- package/dist/server/read-config.d.ts +0 -67
- package/dist/server/read-config.js +0 -8
- package/dist/server/storage-mode.d.ts +0 -8
- package/dist/server/storage-mode.js +0 -28
- package/dist/source/connector-protocol.d.ts +0 -159
- package/dist/source/connector-protocol.js +0 -161
- package/dist/transactions/OptimisticEchoTracker.d.ts +0 -82
- package/dist/transactions/OptimisticEchoTracker.js +0 -104
- package/dist/transactions/mutation-error-handler.d.ts +0 -5
- package/dist/transactions/mutation-error-handler.js +0 -39
- package/dist/transactions/optimistic.d.ts +0 -24
- package/dist/transactions/optimistic.js +0 -45
- package/dist/utils/mobx-setup.d.ts +0 -42
package/dist/wire/protocol.d.ts
CHANGED
|
@@ -1,45 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* cadence and the
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* The timing constants both sides of the protocol must agree on: the ping
|
|
3
|
+
* cadence and the lease window derived from it. Defining them here once keeps
|
|
4
|
+
* the client and the server from skewing apart — a change to the ping interval
|
|
5
|
+
* that did not also move the lease window would make claim expiry and presence
|
|
6
|
+
* timeouts disagree between the two.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
8
|
+
* {@link PING_INTERVAL_MS} is how often the connection pings to prove it is
|
|
9
|
+
* alive. {@link LEASE_TTL_MS} is how long a claim or presence entry stays valid
|
|
10
|
+
* without a renewing ping. On the client, these set the heartbeat cadence and
|
|
11
|
+
* the fallback expiry for a claim taken without an explicit lease. On the
|
|
12
|
+
* server, they set the keepalive interval, the lease granted per keepalive, and
|
|
13
|
+
* the presence-entry lifetime, so a silently disconnected client drops off the
|
|
14
|
+
* roster within one lease window.
|
|
13
15
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* presence-entry TTL (a silently-dead client leaves the roster within
|
|
21
|
-
* one lease window).
|
|
22
|
-
*
|
|
23
|
-
* INVARIANT: `LEASE_TTL_MS === 3 * PING_INTERVAL_MS`. The lease is renewed
|
|
24
|
-
* on every ping, so a live holder always has ≥ 2 ping intervals of runway,
|
|
25
|
-
* and a silent one lapses ~2 missed pings after it stops renewing. TTL is
|
|
26
|
-
* liveness, not work-duration — never widen the lease without widening the
|
|
27
|
-
* ping (or holders will flap), and never derive either value locally.
|
|
16
|
+
* The lease is three ping intervals long and is renewed on every ping, so a
|
|
17
|
+
* live holder always has at least two intervals of runway and a silent one
|
|
18
|
+
* lapses about two missed pings after it stops renewing. The window measures
|
|
19
|
+
* liveness, not how long a task may run: widening the lease without also
|
|
20
|
+
* widening the ping makes holders flap, and neither value should be redefined
|
|
21
|
+
* anywhere else.
|
|
28
22
|
*/
|
|
29
23
|
export declare const PING_INTERVAL_MS = 30000;
|
|
30
24
|
export declare const LEASE_TTL_MS: number;
|
|
31
25
|
/**
|
|
32
|
-
* WebSocket subprotocols
|
|
26
|
+
* The WebSocket subprotocols that carry the bearer credential out of the URL.
|
|
33
27
|
*
|
|
34
|
-
*
|
|
28
|
+
* A browser cannot set an `Authorization` header on a WebSocket, so the client
|
|
35
29
|
* offers the token as a `Sec-WebSocket-Protocol` value — `ablo.bearer.<token>` —
|
|
36
30
|
* alongside the real `ablo.sync.v1` protocol the server selects. This keeps the
|
|
37
|
-
* credential out of the query string, which
|
|
38
|
-
*
|
|
39
|
-
* echoes back
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* Re-exported from `auth/credentialSource.ts` for existing SDK importers.
|
|
31
|
+
* credential out of the query string, which access logs, proxies, and browser
|
|
32
|
+
* history would otherwise capture. The server reads the token from the
|
|
33
|
+
* subprotocol and echoes back only `ablo.sync.v1`, never the token-bearing
|
|
34
|
+
* value. Both the client and the server import these constants, so the
|
|
35
|
+
* handshake format cannot drift.
|
|
43
36
|
*/
|
|
44
37
|
export declare const WS_BEARER_SUBPROTOCOL_PREFIX = "ablo.bearer.";
|
|
45
38
|
export declare const WS_SYNC_SUBPROTOCOL = "ablo.sync.v1";
|
package/dist/wire/protocol.js
CHANGED
|
@@ -1,45 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* cadence and the
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* The timing constants both sides of the protocol must agree on: the ping
|
|
3
|
+
* cadence and the lease window derived from it. Defining them here once keeps
|
|
4
|
+
* the client and the server from skewing apart — a change to the ping interval
|
|
5
|
+
* that did not also move the lease window would make claim expiry and presence
|
|
6
|
+
* timeouts disagree between the two.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
8
|
+
* {@link PING_INTERVAL_MS} is how often the connection pings to prove it is
|
|
9
|
+
* alive. {@link LEASE_TTL_MS} is how long a claim or presence entry stays valid
|
|
10
|
+
* without a renewing ping. On the client, these set the heartbeat cadence and
|
|
11
|
+
* the fallback expiry for a claim taken without an explicit lease. On the
|
|
12
|
+
* server, they set the keepalive interval, the lease granted per keepalive, and
|
|
13
|
+
* the presence-entry lifetime, so a silently disconnected client drops off the
|
|
14
|
+
* roster within one lease window.
|
|
13
15
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* presence-entry TTL (a silently-dead client leaves the roster within
|
|
21
|
-
* one lease window).
|
|
22
|
-
*
|
|
23
|
-
* INVARIANT: `LEASE_TTL_MS === 3 * PING_INTERVAL_MS`. The lease is renewed
|
|
24
|
-
* on every ping, so a live holder always has ≥ 2 ping intervals of runway,
|
|
25
|
-
* and a silent one lapses ~2 missed pings after it stops renewing. TTL is
|
|
26
|
-
* liveness, not work-duration — never widen the lease without widening the
|
|
27
|
-
* ping (or holders will flap), and never derive either value locally.
|
|
16
|
+
* The lease is three ping intervals long and is renewed on every ping, so a
|
|
17
|
+
* live holder always has at least two intervals of runway and a silent one
|
|
18
|
+
* lapses about two missed pings after it stops renewing. The window measures
|
|
19
|
+
* liveness, not how long a task may run: widening the lease without also
|
|
20
|
+
* widening the ping makes holders flap, and neither value should be redefined
|
|
21
|
+
* anywhere else.
|
|
28
22
|
*/
|
|
29
23
|
export const PING_INTERVAL_MS = 30_000;
|
|
30
24
|
export const LEASE_TTL_MS = 3 * PING_INTERVAL_MS;
|
|
31
25
|
/**
|
|
32
|
-
* WebSocket subprotocols
|
|
26
|
+
* The WebSocket subprotocols that carry the bearer credential out of the URL.
|
|
33
27
|
*
|
|
34
|
-
*
|
|
28
|
+
* A browser cannot set an `Authorization` header on a WebSocket, so the client
|
|
35
29
|
* offers the token as a `Sec-WebSocket-Protocol` value — `ablo.bearer.<token>` —
|
|
36
30
|
* alongside the real `ablo.sync.v1` protocol the server selects. This keeps the
|
|
37
|
-
* credential out of the query string, which
|
|
38
|
-
*
|
|
39
|
-
* echoes back
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* Re-exported from `auth/credentialSource.ts` for existing SDK importers.
|
|
31
|
+
* credential out of the query string, which access logs, proxies, and browser
|
|
32
|
+
* history would otherwise capture. The server reads the token from the
|
|
33
|
+
* subprotocol and echoes back only `ablo.sync.v1`, never the token-bearing
|
|
34
|
+
* value. Both the client and the server import these constants, so the
|
|
35
|
+
* handshake format cannot drift.
|
|
43
36
|
*/
|
|
44
37
|
export const WS_BEARER_SUBPROTOCOL_PREFIX = 'ablo.bearer.';
|
|
45
38
|
export const WS_SYNC_SUBPROTOCOL = 'ablo.sync.v1';
|
|
@@ -1,56 +1,60 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The sync protocol version —
|
|
3
|
-
* everything client and server must agree on to
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* (
|
|
7
|
-
*
|
|
8
|
-
* with no detector at all.
|
|
2
|
+
* The sync protocol version — a single integer, increasing over time, that
|
|
3
|
+
* covers everything the client and server must agree on to talk to each other:
|
|
4
|
+
* the WebSocket frame shapes, the HTTP request and response envelopes, and the
|
|
5
|
+
* delta encodings a client replays. It is separate from the app-schema hash
|
|
6
|
+
* (WebSocket close code 4009), which detects drift in your data model; this
|
|
7
|
+
* detects drift in the protocol itself.
|
|
9
8
|
*
|
|
10
|
-
* Deploy
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
9
|
+
* Deploy ordering: the server is deployed first. It accepts every version in
|
|
10
|
+
* the inclusive range from {@link MIN_SUPPORTED_PROTOCOL_VERSION} to
|
|
11
|
+
* {@link PROTOCOL_VERSION}, and a client is never expected to connect to a
|
|
12
|
+
* server older than itself. If that does happen — for example a partial server
|
|
13
|
+
* rollback — {@link protocolVersionProblem} reports it as `too_new` so the
|
|
14
|
+
* mismatch is visible rather than undefined.
|
|
14
15
|
*
|
|
15
|
-
*
|
|
16
|
-
* 1. Make the
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* 3. The contract test
|
|
23
|
-
*
|
|
16
|
+
* To change the protocol:
|
|
17
|
+
* 1. Make the change backward-tolerant where you can. The server ignores
|
|
18
|
+
* unknown payload keys, so an additive field usually needs no version bump.
|
|
19
|
+
* 2. For a breaking change, bump {@link PROTOCOL_VERSION}, add a changelog
|
|
20
|
+
* entry below, and keep {@link MIN_SUPPORTED_PROTOCOL_VERSION} low enough
|
|
21
|
+
* to cover every client still in use; raise it only after a deprecation
|
|
22
|
+
* window.
|
|
23
|
+
* 3. The protocol-version contract test fails on any bump — update it in the
|
|
24
|
+
* same change, deliberately.
|
|
24
25
|
*
|
|
25
|
-
*
|
|
26
|
-
* v1 (2026-07-03) — the protocol as of
|
|
27
|
-
*
|
|
28
|
-
* commit
|
|
29
|
-
* delta batches,
|
|
30
|
-
*
|
|
31
|
-
* `protocolVersion`
|
|
32
|
-
*
|
|
26
|
+
* Changelog
|
|
27
|
+
* v1 (2026-07-03) — the protocol as of this field's introduction: the
|
|
28
|
+
* sync-request frame (`cursor`, `lastSyncId`, `capabilities`, optional
|
|
29
|
+
* `protocolVersion`), the commit, mutation, claim, release, ack, and
|
|
30
|
+
* presence-update frames, the bootstrap and delta batches, and the HTTP
|
|
31
|
+
* error and list envelopes. A client that predates this field sends no
|
|
32
|
+
* `protocolVersion` and is treated as v1, since introducing the field
|
|
33
|
+
* changed no behavior.
|
|
33
34
|
*/
|
|
34
35
|
export declare const PROTOCOL_VERSION = 1;
|
|
35
36
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* changelog entry.
|
|
37
|
+
* The oldest client protocol version this build still serves. Raising it cuts
|
|
38
|
+
* off clients that have not upgraded, so do it only after a deprecation window
|
|
39
|
+
* and with a changelog entry.
|
|
39
40
|
*/
|
|
40
41
|
export declare const MIN_SUPPORTED_PROTOCOL_VERSION = 1;
|
|
41
42
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
43
|
+
* The WebSocket close code the server sends to reject a protocol-version
|
|
44
|
+
* mismatch. It sits alongside the other application close codes (4001 for a
|
|
45
|
+
* credential problem, 4009 for app-schema drift), and its reason string is the
|
|
46
|
+
* error code `protocol_version_unsupported`. A client should treat this close
|
|
47
|
+
* as terminal: reconnecting cannot heal a version mismatch, but upgrading the
|
|
48
|
+
* client or rolling the server forward can.
|
|
47
49
|
*/
|
|
48
50
|
export declare const WS_CLOSE_PROTOCOL_VERSION = 4010;
|
|
49
51
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
52
|
+
* Classifies a peer's announced protocol version against what this build
|
|
53
|
+
* supports, returning `'too_old'`, `'too_new'`, or `null` when the versions are
|
|
54
|
+
* compatible. An `undefined` version — a client from before versioning existed
|
|
55
|
+
* — counts as v1. A non-integer value is treated as `'too_old'` so the check
|
|
56
|
+
* fails closed and visibly.
|
|
53
57
|
*/
|
|
54
58
|
export declare function protocolVersionProblem(announced: number | undefined): 'too_old' | 'too_new' | null;
|
|
55
|
-
/** HTTP request header
|
|
59
|
+
/** The HTTP request header a client uses to announce its protocol version. */
|
|
56
60
|
export declare const PROTOCOL_VERSION_HEADER = "Ablo-Protocol-Version";
|
|
@@ -1,55 +1,59 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The sync protocol version —
|
|
3
|
-
* everything client and server must agree on to
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* (
|
|
7
|
-
*
|
|
8
|
-
* with no detector at all.
|
|
2
|
+
* The sync protocol version — a single integer, increasing over time, that
|
|
3
|
+
* covers everything the client and server must agree on to talk to each other:
|
|
4
|
+
* the WebSocket frame shapes, the HTTP request and response envelopes, and the
|
|
5
|
+
* delta encodings a client replays. It is separate from the app-schema hash
|
|
6
|
+
* (WebSocket close code 4009), which detects drift in your data model; this
|
|
7
|
+
* detects drift in the protocol itself.
|
|
9
8
|
*
|
|
10
|
-
* Deploy
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
9
|
+
* Deploy ordering: the server is deployed first. It accepts every version in
|
|
10
|
+
* the inclusive range from {@link MIN_SUPPORTED_PROTOCOL_VERSION} to
|
|
11
|
+
* {@link PROTOCOL_VERSION}, and a client is never expected to connect to a
|
|
12
|
+
* server older than itself. If that does happen — for example a partial server
|
|
13
|
+
* rollback — {@link protocolVersionProblem} reports it as `too_new` so the
|
|
14
|
+
* mismatch is visible rather than undefined.
|
|
14
15
|
*
|
|
15
|
-
*
|
|
16
|
-
* 1. Make the
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* 3. The contract test
|
|
23
|
-
*
|
|
16
|
+
* To change the protocol:
|
|
17
|
+
* 1. Make the change backward-tolerant where you can. The server ignores
|
|
18
|
+
* unknown payload keys, so an additive field usually needs no version bump.
|
|
19
|
+
* 2. For a breaking change, bump {@link PROTOCOL_VERSION}, add a changelog
|
|
20
|
+
* entry below, and keep {@link MIN_SUPPORTED_PROTOCOL_VERSION} low enough
|
|
21
|
+
* to cover every client still in use; raise it only after a deprecation
|
|
22
|
+
* window.
|
|
23
|
+
* 3. The protocol-version contract test fails on any bump — update it in the
|
|
24
|
+
* same change, deliberately.
|
|
24
25
|
*
|
|
25
|
-
*
|
|
26
|
-
* v1 (2026-07-03) — the protocol as of
|
|
27
|
-
*
|
|
28
|
-
* commit
|
|
29
|
-
* delta batches,
|
|
30
|
-
*
|
|
31
|
-
* `protocolVersion`
|
|
32
|
-
*
|
|
26
|
+
* Changelog
|
|
27
|
+
* v1 (2026-07-03) — the protocol as of this field's introduction: the
|
|
28
|
+
* sync-request frame (`cursor`, `lastSyncId`, `capabilities`, optional
|
|
29
|
+
* `protocolVersion`), the commit, mutation, claim, release, ack, and
|
|
30
|
+
* presence-update frames, the bootstrap and delta batches, and the HTTP
|
|
31
|
+
* error and list envelopes. A client that predates this field sends no
|
|
32
|
+
* `protocolVersion` and is treated as v1, since introducing the field
|
|
33
|
+
* changed no behavior.
|
|
33
34
|
*/
|
|
34
35
|
export const PROTOCOL_VERSION = 1;
|
|
35
36
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* changelog entry.
|
|
37
|
+
* The oldest client protocol version this build still serves. Raising it cuts
|
|
38
|
+
* off clients that have not upgraded, so do it only after a deprecation window
|
|
39
|
+
* and with a changelog entry.
|
|
39
40
|
*/
|
|
40
41
|
export const MIN_SUPPORTED_PROTOCOL_VERSION = 1;
|
|
41
42
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
43
|
+
* The WebSocket close code the server sends to reject a protocol-version
|
|
44
|
+
* mismatch. It sits alongside the other application close codes (4001 for a
|
|
45
|
+
* credential problem, 4009 for app-schema drift), and its reason string is the
|
|
46
|
+
* error code `protocol_version_unsupported`. A client should treat this close
|
|
47
|
+
* as terminal: reconnecting cannot heal a version mismatch, but upgrading the
|
|
48
|
+
* client or rolling the server forward can.
|
|
47
49
|
*/
|
|
48
50
|
export const WS_CLOSE_PROTOCOL_VERSION = 4010;
|
|
49
51
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
52
|
+
* Classifies a peer's announced protocol version against what this build
|
|
53
|
+
* supports, returning `'too_old'`, `'too_new'`, or `null` when the versions are
|
|
54
|
+
* compatible. An `undefined` version — a client from before versioning existed
|
|
55
|
+
* — counts as v1. A non-integer value is treated as `'too_old'` so the check
|
|
56
|
+
* fails closed and visibly.
|
|
53
57
|
*/
|
|
54
58
|
export function protocolVersionProblem(announced) {
|
|
55
59
|
const v = announced ?? 1;
|
|
@@ -59,5 +63,5 @@ export function protocolVersionProblem(announced) {
|
|
|
59
63
|
return 'too_new';
|
|
60
64
|
return null;
|
|
61
65
|
}
|
|
62
|
-
/** HTTP request header
|
|
66
|
+
/** The HTTP request header a client uses to announce its protocol version. */
|
|
63
67
|
export const PROTOCOL_VERSION_HEADER = 'Ablo-Protocol-Version';
|
package/docs/coordination.md
CHANGED
|
@@ -416,6 +416,65 @@ try {
|
|
|
416
416
|
}
|
|
417
417
|
```
|
|
418
418
|
|
|
419
|
+
### `heartbeat` — holding a claim for long-running work
|
|
420
|
+
|
|
421
|
+
```ts
|
|
422
|
+
held.heartbeat(ttl?: Duration): Promise<{ expiresAt: number }>
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
A claim's TTL is crash cleanup, not a work-duration estimate — so a task that
|
|
426
|
+
outlives it (an agent run, a background worker's job) keeps its lease by
|
|
427
|
+
**beating**, the same pattern as an SQS visibility heartbeat or a Temporal
|
|
428
|
+
activity heartbeat. Each beat extends the lease from now (never shortens it,
|
|
429
|
+
and each extension is clamped server-side); a crashed worker stops beating and
|
|
430
|
+
its lease lapses within one beat window, promoting the next waiter.
|
|
431
|
+
|
|
432
|
+
Usually **implicit** — pass `heartbeat` when claiming and the SDK beats every
|
|
433
|
+
third of the TTL until release:
|
|
434
|
+
|
|
435
|
+
```ts
|
|
436
|
+
await using claim = await ablo.reports.claim({
|
|
437
|
+
id: 'report_q3',
|
|
438
|
+
reason: 'generating',
|
|
439
|
+
ttl: '5m',
|
|
440
|
+
heartbeat: true, // or an explicit cadence: heartbeat: '2m'
|
|
441
|
+
onHeartbeatLost: () => abortWork(),
|
|
442
|
+
});
|
|
443
|
+
await runLongGeneration(claim.data); // lease held for the duration
|
|
444
|
+
// scope exit releases; the loop stops with it
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
A beat that comes back with a definitive loss — the lease expired and the
|
|
448
|
+
queue moved on — rejects with `AbloClaimedError` (`claim_lost`) and stops the
|
|
449
|
+
auto-loop. For a worker with no socket, **the failed beat is the loss
|
|
450
|
+
notification**; abandon or re-claim, and remember any write attempted under
|
|
451
|
+
the old lease is independently rejected by its `readAt` guard. Transient
|
|
452
|
+
failures (a connection blip) don't stop the loop — the next tick retries.
|
|
453
|
+
|
|
454
|
+
Each beat's answer carries two more things:
|
|
455
|
+
|
|
456
|
+
- **`queueDepth`** — how many participants wait in line behind the lease.
|
|
457
|
+
This is the cooperative-yield pressure signal: a worker that can checkpoint
|
|
458
|
+
may release early when others wait. Read it from the resolved beat, or pass
|
|
459
|
+
`onHeartbeat` when claiming to observe every auto-beat.
|
|
460
|
+
- **progress `details`** — `held.heartbeat({ details: { pages: 42, of: 100 } })`
|
|
461
|
+
stores the payload as the claim's peer-visible `meta.progress` (last beat
|
|
462
|
+
wins, via `claim.state`). This is presence, not a checkpoint: it dies with
|
|
463
|
+
the lease. Durable progress belongs in the data itself — write a row, and
|
|
464
|
+
every subscriber already sees it.
|
|
465
|
+
|
|
466
|
+
Works identically on both transports: the realtime client sends a
|
|
467
|
+
`claim_heartbeat` frame; the HTTP client posts
|
|
468
|
+
`POST /v1/models/{model}/{id}/claim/heartbeat` (`{ ttl?, claimId?, details? }`).
|
|
469
|
+
Over HTTP, a **queued** claim can heartbeat too — it refreshes the waiter's
|
|
470
|
+
slot in the line (a queued slot is TTL'd like a lease) and reports
|
|
471
|
+
`{ status: 'queued', position }`.
|
|
472
|
+
|
|
473
|
+
A stateless worker holding **many** rows beats them all in one round trip:
|
|
474
|
+
`ablo.claims.heartbeatAll({ ttl: '5m' })` → `POST /v1/claims/heartbeat`, one
|
|
475
|
+
entry per extended lease. This is the socketless twin of the realtime
|
|
476
|
+
keepalive, which already renews every held lease on each ping.
|
|
477
|
+
|
|
419
478
|
### `watch` — presence for a set of rows
|
|
420
479
|
|
|
421
480
|
Reading or claiming a row auto-enrolls you in its sync group, which is enough for
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abloatai/ablo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"description": "The Collaboration Layer For AI Agents",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -150,10 +150,10 @@
|
|
|
150
150
|
"pretest": "node scripts/check-dist-fresh.mjs",
|
|
151
151
|
"test": "jest",
|
|
152
152
|
"test:quickstart": "node scripts/test-quickstart.mjs",
|
|
153
|
-
"test:unit": "jest --
|
|
154
|
-
"test:integration": "jest --
|
|
155
|
-
"test:contract": "jest --
|
|
156
|
-
"test:property": "jest --
|
|
153
|
+
"test:unit": "jest --testPathPatterns __tests__/unit",
|
|
154
|
+
"test:integration": "jest --testPathPatterns __tests__/integration",
|
|
155
|
+
"test:contract": "jest --testPathPatterns __tests__/contract",
|
|
156
|
+
"test:property": "jest --testPathPatterns __tests__/property",
|
|
157
157
|
"test:coverage": "jest --coverage",
|
|
158
158
|
"test:e2e": "E2E_TEST=true jest --config jest.e2e.config.ts",
|
|
159
159
|
"test:e2e:up": "docker compose -f docker-compose.test.yml up -d --wait",
|
|
@@ -212,9 +212,9 @@
|
|
|
212
212
|
"devDependencies": {
|
|
213
213
|
"@ai-sdk/provider": "^3.0.0",
|
|
214
214
|
"@clack/prompts": "^0.11.0",
|
|
215
|
-
"@jest/globals": "^
|
|
215
|
+
"@jest/globals": "^30.2.0",
|
|
216
216
|
"@prisma/client": "^7.3.0",
|
|
217
|
-
"@types/jest": "^
|
|
217
|
+
"@types/jest": "^30.0.0",
|
|
218
218
|
"@types/node": "^22.0.0",
|
|
219
219
|
"@types/react": "^19.0.0",
|
|
220
220
|
"@types/uuid": "^10.0.0",
|
|
@@ -225,13 +225,14 @@
|
|
|
225
225
|
"fake-indexeddb": "^6.0.0",
|
|
226
226
|
"fast-check": "^3.0.0",
|
|
227
227
|
"globals": "^16.5.0",
|
|
228
|
-
"jest": "^
|
|
229
|
-
"jest-environment-jsdom": "^
|
|
228
|
+
"jest": "^30.2.0",
|
|
229
|
+
"jest-environment-jsdom": "^30.2.0",
|
|
230
|
+
"jest-environment-node": "^30.2.0",
|
|
230
231
|
"picocolors": "^1.1.0",
|
|
231
232
|
"postgres": "^3.4.0",
|
|
232
233
|
"react": "^19.0.0",
|
|
233
234
|
"react-dom": "^19.0.0",
|
|
234
|
-
"ts-jest": "^29.4.
|
|
235
|
+
"ts-jest": "^29.4.5",
|
|
235
236
|
"ts-morph": "^26.0.0",
|
|
236
237
|
"tsup": "^8.0.0",
|
|
237
238
|
"tsx": "^4.19.0",
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `coordinatedTool` — the one-liner that turns an Ablo model write into a Vercel
|
|
3
|
-
* AI SDK tool with multi-agent coordination already handled, so an AI agent can
|
|
4
|
-
* contribute to shared state without ever silently clobbering a concurrent
|
|
5
|
-
* writer.
|
|
6
|
-
*
|
|
7
|
-
* The base `./ai-sdk` pattern (see index.ts) is "write your own `tool()` and
|
|
8
|
-
* call `ablo.<model>.update({ id, data, claim })` inside `execute`". That's the
|
|
9
|
-
* right amount of control when a tool does something bespoke. But the *common*
|
|
10
|
-
* case — "the agent produced some content; save it into the shared row" — should
|
|
11
|
-
* not require every integration to re-derive optimistic concurrency by hand. This
|
|
12
|
-
* collapses it to a declaration:
|
|
13
|
-
*
|
|
14
|
-
* ```ts
|
|
15
|
-
* import { coordinatedTool } from '@abloatai/ablo/ai-sdk';
|
|
16
|
-
* import { z } from 'zod';
|
|
17
|
-
*
|
|
18
|
-
* const saveSection = coordinatedTool(ablo.documents, {
|
|
19
|
-
* description: 'Save your section into the shared document.',
|
|
20
|
-
* inputSchema: z.object({ text: z.string() }),
|
|
21
|
-
* id: () => DOC_ID,
|
|
22
|
-
* apply: (current, { text }) => ({ content: appendBlock(current.content, text) }),
|
|
23
|
-
* // strategy: 'merge' ← the default
|
|
24
|
-
* });
|
|
25
|
-
*
|
|
26
|
-
* await streamText({ model, messages, tools: { saveSection } });
|
|
27
|
-
* ```
|
|
28
|
-
*
|
|
29
|
-
* `apply` is the whole API: a pure function of `(freshest row, tool input) →
|
|
30
|
-
* patch`, exactly like React's `setState(prev => next)`. Everything underneath —
|
|
31
|
-
* reading the latest row, the compare-and-swap, the jittered backoff between
|
|
32
|
-
* reconcile rounds, releasing claims — is the runtime's job, not yours.
|
|
33
|
-
*
|
|
34
|
-
* ## Strategies (pick by how writers should relate; all verified to converge
|
|
35
|
-
* under N-way agent contention)
|
|
36
|
-
*
|
|
37
|
-
* - `'merge'` *(default)* — delegates straight to the functional update
|
|
38
|
-
* `ablo.<model>.update(id, current => apply(current, input))`. The SDK re-reads
|
|
39
|
-
* and re-applies `apply` on top of every concurrent write and backs off between
|
|
40
|
-
* rounds, so N agents *accumulate* into one row and the model never sees a
|
|
41
|
-
* conflict. **Requires the model's agent conflict policy to be `reject`** (the
|
|
42
|
-
* default, or `agentsReject()`); a model declaring `agentsNotify()` HOLDS the
|
|
43
|
-
* losing write instead of rejecting it, which defeats the reconcile — use
|
|
44
|
-
* `claim`/`queue` there, or switch the policy.
|
|
45
|
-
*
|
|
46
|
-
* - `'claim'` — mutual exclusion. Takes a fail-fast claim; if another participant
|
|
47
|
-
* holds the row it returns `{ status: 'claimed' }` so the *model* decides to
|
|
48
|
-
* retry (a legible signal beats a hidden wait when the agent might do something
|
|
49
|
-
* better with its turn). Works regardless of conflict policy.
|
|
50
|
-
*
|
|
51
|
-
* - `'queue'` — fair-ish serialization over stateless HTTP, the SQS shape: a
|
|
52
|
-
* client poll-acquire loop (true FIFO needs a socket) until the claim is granted
|
|
53
|
-
* or `poll.timeoutMs` elapses. The model calls once and the tool waits its turn.
|
|
54
|
-
*/
|
|
55
|
-
import type { z } from 'zod';
|
|
56
|
-
import type { ModelOperations } from '../client/createModelProxy.js';
|
|
57
|
-
export type CoordinationStrategy = 'merge' | 'claim' | 'queue';
|
|
58
|
-
/** The structured result the tool hands back to the model (or the caller). */
|
|
59
|
-
export interface CoordinatedWriteResult<T> {
|
|
60
|
-
/**
|
|
61
|
-
* `'written'` — saved. `'claimed'` — another participant holds the row; NOT
|
|
62
|
-
* saved, the model should try again. `'timeout'` — the queue strategy could not
|
|
63
|
-
* acquire the row within `poll.timeoutMs`.
|
|
64
|
-
*/
|
|
65
|
-
status: 'written' | 'claimed' | 'timeout';
|
|
66
|
-
/** The reconciled row, on `'written'`. */
|
|
67
|
-
row?: T;
|
|
68
|
-
message?: string;
|
|
69
|
-
/** On `'written'` via the `queue` strategy, how long the tool waited in line. */
|
|
70
|
-
waitedMs?: number;
|
|
71
|
-
}
|
|
72
|
-
export interface CoordinatedToolOptions<TInput, T> {
|
|
73
|
-
/** Tool description shown to the model. */
|
|
74
|
-
description: string;
|
|
75
|
-
/** What the model may send — a normal AI SDK / zod input schema. */
|
|
76
|
-
inputSchema: z.ZodType<TInput>;
|
|
77
|
-
/** Which row this write targets, derived from the tool input. */
|
|
78
|
-
id: (input: TInput) => string;
|
|
79
|
-
/**
|
|
80
|
-
* Produce the write patch from the freshest current row + the tool input — a
|
|
81
|
-
* pure `(prev, input) => next`. Under `merge` it re-runs on every concurrent
|
|
82
|
-
* write, so it must be idempotent w.r.t. its own contribution (e.g. skip if its
|
|
83
|
-
* marker is already present) to be safe across reconcile rounds.
|
|
84
|
-
*/
|
|
85
|
-
apply: (current: T, input: TInput) => Partial<T>;
|
|
86
|
-
/** How concurrent writers relate. Defaults to `'merge'`. */
|
|
87
|
-
strategy?: CoordinationStrategy;
|
|
88
|
-
/** Human-legible coordination metadata attached to the claim (`claim`/`queue`). */
|
|
89
|
-
claim?: {
|
|
90
|
-
reason?: string;
|
|
91
|
-
description?: string;
|
|
92
|
-
};
|
|
93
|
-
/** Reconcile budget for `merge` (rounds before `AbloContentionError`). */
|
|
94
|
-
retries?: number;
|
|
95
|
-
/** Poll cadence / ceiling for `queue` (defaults 250ms / 30s). */
|
|
96
|
-
poll?: {
|
|
97
|
-
intervalMs?: number;
|
|
98
|
-
timeoutMs?: number;
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
export declare function coordinatedTool<TInput, T = Record<string, unknown>, CreateInput = Partial<T>>(model: ModelOperations<T, CreateInput>, options: CoordinatedToolOptions<TInput, T>): import("ai").Tool<TInput, CoordinatedWriteResult<T>>;
|