@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
|
@@ -1,23 +1,20 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { syncGroupInputSchema } from '../schema/roles.js';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* one decision") for the conceptual model. The layers, outer-to-inner:
|
|
4
|
+
* The wire schemas for coordination — the shapes that keep humans and agents
|
|
5
|
+
* from overwriting each other on a shared row. Coordination works in three
|
|
6
|
+
* layers, from outermost to innermost:
|
|
8
7
|
*
|
|
9
|
-
* 1.
|
|
10
|
-
* 2.
|
|
11
|
-
*
|
|
12
|
-
* 3.
|
|
13
|
-
*
|
|
8
|
+
* 1. Presence (observation): who is working where. It reports, never blocks.
|
|
9
|
+
* 2. Claims (pessimistic leases): `claim_begin` / `claim_abandon` grant one
|
|
10
|
+
* participant exclusive intent on a target while others wait.
|
|
11
|
+
* 3. Stale-context (optimistic): a `readAt` watermark plus an `onStale` write
|
|
12
|
+
* guard that catches a lost update when the row moved after you read it.
|
|
14
13
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* `status`/`error`, `onStale` declared 5×, `ClaimStatus` declared 2× — into
|
|
20
|
-
* a single definition that the wire ingest can also validate at runtime.
|
|
14
|
+
* These Zod schemas are the single definition of each shape. Both the client
|
|
15
|
+
* SDK and the server derive their TypeScript types from them with `z.infer`
|
|
16
|
+
* rather than re-declaring the shapes, and the server validates inbound frames
|
|
17
|
+
* against them at runtime.
|
|
21
18
|
*/
|
|
22
19
|
// ─────────────────────────────────────────────────────────────────────────
|
|
23
20
|
// Shared primitives
|
|
@@ -31,21 +28,20 @@ export const targetRangeSchema = z.object({
|
|
|
31
28
|
});
|
|
32
29
|
export const participantKindSchema = z.enum(['user', 'agent', 'system']);
|
|
33
30
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* frame still carrying `'human'`. Additive — never widens the output union.
|
|
31
|
+
* Parses a participant kind from an inbound frame, tolerating an older wire
|
|
32
|
+
* dialect. Some presence and claim frames label a non-agent participant
|
|
33
|
+
* `'human'`, while the rest of the surface uses `'user'` for the same
|
|
34
|
+
* participant. This normalizes `'human'` to `'user'` on read so every consumer
|
|
35
|
+
* switches on one vocabulary. Producers emit the canonical
|
|
36
|
+
* {@link participantKindSchema} values, and the output union is never widened.
|
|
41
37
|
*/
|
|
42
38
|
export const wireParticipantKindSchema = z.preprocess((value) => (value === 'human' ? 'user' : value), participantKindSchema);
|
|
43
39
|
/**
|
|
44
|
-
*
|
|
45
|
-
* server-stamped `participantKind` (normalized
|
|
46
|
-
* {@link wireParticipantKindSchema})
|
|
47
|
-
* field
|
|
48
|
-
* 'user' but never 'system'
|
|
40
|
+
* Resolves a peer's kind from an inbound presence or claim frame. It prefers
|
|
41
|
+
* the server-stamped `participantKind` (normalized through
|
|
42
|
+
* {@link wireParticipantKindSchema}). A frame from an older server that omits
|
|
43
|
+
* that field falls back to the `isAgent` boolean, which can tell 'agent' from
|
|
44
|
+
* 'user' but can never report 'system'.
|
|
49
45
|
*/
|
|
50
46
|
export function participantKindFromWire(wireKind, isAgent) {
|
|
51
47
|
const parsed = wireParticipantKindSchema.safeParse(wireKind);
|
|
@@ -54,18 +50,18 @@ export function participantKindFromWire(wireKind, isAgent) {
|
|
|
54
50
|
return isAgent ? 'agent' : 'user';
|
|
55
51
|
}
|
|
56
52
|
/**
|
|
57
|
-
*
|
|
58
|
-
* `meta.description`.
|
|
59
|
-
*
|
|
60
|
-
*
|
|
53
|
+
* Reads the peer-visible description a claim or presence frame carries in its
|
|
54
|
+
* opaque `meta.description`. This is the single place that unpacks that field.
|
|
55
|
+
* A caller that has an explicit `description` should prefer it
|
|
56
|
+
* (`explicit ?? fromMeta`).
|
|
61
57
|
*/
|
|
62
58
|
export function descriptionFromMeta(meta) {
|
|
63
59
|
return typeof meta?.description === 'string' ? meta.description : undefined;
|
|
64
60
|
}
|
|
65
61
|
/**
|
|
66
|
-
* What a
|
|
67
|
-
*
|
|
68
|
-
*
|
|
62
|
+
* What a coordination event points at — the locator shared by all three
|
|
63
|
+
* layers. It names an entity, optionally narrowed to a path, range, or field,
|
|
64
|
+
* and carries opaque application metadata.
|
|
69
65
|
*/
|
|
70
66
|
export const targetRefSchema = z.object({
|
|
71
67
|
entityType: z.string(),
|
|
@@ -76,18 +72,17 @@ export const targetRefSchema = z.object({
|
|
|
76
72
|
meta: z.record(z.string(), z.unknown()).optional(),
|
|
77
73
|
});
|
|
78
74
|
// ─────────────────────────────────────────────────────────────────────────
|
|
79
|
-
// Layer 3 —
|
|
75
|
+
// Layer 3 — optimistic stale-context (the write guard)
|
|
80
76
|
// ─────────────────────────────────────────────────────────────────────────
|
|
81
77
|
/**
|
|
82
|
-
*
|
|
83
|
-
* target row's latest
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
* • `reject` —
|
|
88
|
-
*
|
|
89
|
-
* • `overwrite` —
|
|
90
|
-
* signal.
|
|
78
|
+
* How the server treats a write whose snapshot watermark (`readAt`) is older
|
|
79
|
+
* than the target row's latest change. There are three dispositions:
|
|
80
|
+
* • `notify` — hold the write and return a {@link StaleNotification}
|
|
81
|
+
* carrying the current value, so the actor (agent or human)
|
|
82
|
+
* can resolve it.
|
|
83
|
+
* • `reject` — throw `AbloStaleContextError`, the default when `readAt`
|
|
84
|
+
* is present.
|
|
85
|
+
* • `overwrite` — apply the write blindly, last write wins, with no signal.
|
|
91
86
|
*/
|
|
92
87
|
export const onStaleModeSchema = z.enum(['reject', 'overwrite', 'notify']);
|
|
93
88
|
/**
|
|
@@ -102,30 +97,23 @@ export const writeGuardSchema = z.object({
|
|
|
102
97
|
bypass: z.boolean().optional(),
|
|
103
98
|
});
|
|
104
99
|
/**
|
|
105
|
-
* The advisory
|
|
106
|
-
* conflict under `onStale: 'notify'` —
|
|
107
|
-
*
|
|
100
|
+
* The advisory returned to a committer whose write hit a stale-context
|
|
101
|
+
* conflict under `onStale: 'notify'` — it reports that the value the committer
|
|
102
|
+
* reasoned against changed while they were away. Rather than throwing, the
|
|
103
|
+
* server hands back the conflicting field's current value as data so the
|
|
104
|
+
* actor — an agent or a human — can reconcile and re-commit. A claim is the
|
|
105
|
+
* prospective form of the same idea (coordinate before acting); this
|
|
106
|
+
* notification is the in-flight form (here is what changed, you resolve). It
|
|
107
|
+
* rides on the commit acknowledgement alongside `lastSyncId`; an empty or
|
|
108
|
+
* absent array means nothing the committer depended on moved.
|
|
108
109
|
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
* notification is the non-coercive path: instead of throwing, the server hands
|
|
114
|
-
* back the conflicting field's *current* value as data so the actor can solve
|
|
115
|
-
* it. The CLAIM is the prospective form of the same principle (coordinate
|
|
116
|
-
* before acting); this notification is the in-flight form (here's what changed,
|
|
117
|
-
* you resolve). Both an agent reasoning over the change and a human watching the
|
|
118
|
-
* row are valid resolvers. (Cf. CoAgent/MTPO, arXiv:2606.15376, which bets the
|
|
119
|
-
* resolver is specifically an LLM; Ablo's bet is the same non-coercion, actor
|
|
120
|
-
* left to agent or human.) Rides on the commit ack alongside `lastSyncId`; an
|
|
121
|
-
* empty/absent array means no premise moved.
|
|
122
|
-
*
|
|
123
|
-
* Only `notify` produces this: the conflicting op was HELD (not written), and
|
|
124
|
-
* the actor reconciles against `currentValues` and re-commits. (`reject` throws,
|
|
125
|
-
* `overwrite` is silent — neither notifies.)
|
|
110
|
+
* Only `onStale: 'notify'` produces this. The conflicting operation was held,
|
|
111
|
+
* not written, and the actor reconciles against `currentValues` and
|
|
112
|
+
* re-commits. `reject` throws instead, and `overwrite` proceeds silently —
|
|
113
|
+
* neither notifies.
|
|
126
114
|
*/
|
|
127
115
|
export const staleNotificationSchema = z.object({
|
|
128
|
-
/**
|
|
116
|
+
/** Names this object's type; every returned object carries such a tag. */
|
|
129
117
|
object: z.literal('stale_notification').optional(),
|
|
130
118
|
/** Model name of the conflicting row. */
|
|
131
119
|
model: z.string(),
|
|
@@ -145,8 +133,8 @@ export const staleNotificationSchema = z.object({
|
|
|
145
133
|
*/
|
|
146
134
|
conflictingFields: z.array(z.string()),
|
|
147
135
|
/**
|
|
148
|
-
*
|
|
149
|
-
* error
|
|
136
|
+
* The live values of `conflictingFields` after the conflict — the piece a
|
|
137
|
+
* plain stale error omits. It lets the actor reconcile without a follow-up read.
|
|
150
138
|
*/
|
|
151
139
|
currentValues: z.record(z.string(), z.unknown()),
|
|
152
140
|
/** Who wrote the conflicting delta. */
|
|
@@ -164,18 +152,19 @@ export const staleNotificationSchema = z.object({
|
|
|
164
152
|
group: z.string().optional(),
|
|
165
153
|
});
|
|
166
154
|
/**
|
|
167
|
-
* A read
|
|
168
|
-
* change?"
|
|
169
|
-
* written
|
|
170
|
-
* `readAt`; a moved premise fires the entry's
|
|
171
|
-
* `reject`)
|
|
172
|
-
* `reject` aborts
|
|
155
|
+
* A read that a commit declares it depended on, so the server can ask "did
|
|
156
|
+
* anything I looked at change?" — broader than the write-target check, which
|
|
157
|
+
* only validates the rows being written. The server re-runs stale detection
|
|
158
|
+
* against each declared read at its `readAt`; a moved premise fires the entry's
|
|
159
|
+
* `onStale` disposition (default `reject`) across the whole batch (`notify`
|
|
160
|
+
* holds every write and notifies, `reject` aborts, `overwrite` proceeds
|
|
161
|
+
* silently). A dependency comes at one of two granularities:
|
|
173
162
|
*
|
|
174
|
-
* •
|
|
175
|
-
*
|
|
176
|
-
* •
|
|
177
|
-
* is a sync-group key
|
|
178
|
-
*
|
|
163
|
+
* • Row — `{ model, id, readAt, fields? }`: did this specific row, or these
|
|
164
|
+
* specific fields, change?
|
|
165
|
+
* • Group — `{ group, readAt }`: did anything in this sync group change?
|
|
166
|
+
* `group` is a sync-group key such as `deck:abc` or `slide:s1`, the
|
|
167
|
+
* same unit a participant watches and claims.
|
|
179
168
|
*/
|
|
180
169
|
export const readDependencySchema = z.union([
|
|
181
170
|
z.object({
|
|
@@ -192,14 +181,13 @@ export const readDependencySchema = z.union([
|
|
|
192
181
|
}),
|
|
193
182
|
]);
|
|
194
183
|
// ─────────────────────────────────────────────────────────────────────────
|
|
195
|
-
// Layer 2 —
|
|
184
|
+
// Layer 2 — pessimistic claims and leases
|
|
196
185
|
// ─────────────────────────────────────────────────────────────────────────
|
|
197
186
|
/**
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
* resolved, not merely that it vanished.
|
|
187
|
+
* The lifecycle of a claim. When absent on the wire it means `'active'` (an
|
|
188
|
+
* additive back-compat default). The server stamps `'active'` on `claim_begin`
|
|
189
|
+
* and emits one terminal frame — `committed`, `canceled`, or `expired` — as the
|
|
190
|
+
* claim ends, so contenders learn how it resolved, not merely that it vanished.
|
|
203
191
|
*/
|
|
204
192
|
export const claimStatusSchema = z.enum([
|
|
205
193
|
'active',
|
|
@@ -241,16 +229,12 @@ export const claimErrorSchema = z.object({
|
|
|
241
229
|
policyReason: z.string().optional(),
|
|
242
230
|
});
|
|
243
231
|
/**
|
|
244
|
-
* A declared pending-mutation claim — the unit broadcast
|
|
245
|
-
* `activeClaims`.
|
|
246
|
-
* explanatory `reason`, and a chosen `claimId`; the
|
|
247
|
-
* `expiresAt` and may set `status`
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
* server (which sets them) and the SDK view (which historically omitted
|
|
251
|
-
* them). The superset is structurally assignable wherever the leaner view
|
|
252
|
-
* was used, so the two prior copies collapse into this one without breaking
|
|
253
|
-
* SDK consumers.
|
|
232
|
+
* A declared, pending-mutation claim — the unit broadcast inside a presence
|
|
233
|
+
* frame's `activeClaims`. The client supplies the descriptive `targetRef`
|
|
234
|
+
* fields, an explanatory `reason`, and a chosen `claimId`; the server stamps
|
|
235
|
+
* `declaredAt` and `expiresAt` and may set `status` and `error`. Those last
|
|
236
|
+
* two are optional, so one shape serves both the server, which sets them, and
|
|
237
|
+
* the leaner SDK view, which reads a claim without them.
|
|
254
238
|
*/
|
|
255
239
|
export const wireClaimSchema = wireClaimBaseSchema.extend({
|
|
256
240
|
error: claimErrorSchema.optional(),
|
|
@@ -266,9 +250,9 @@ export const claimRejectionSchema = z.object({
|
|
|
266
250
|
policyReason: z.string().optional(),
|
|
267
251
|
});
|
|
268
252
|
/**
|
|
269
|
-
* What a {@link ModelClaim} points at — the
|
|
270
|
-
* `model
|
|
271
|
-
* `
|
|
253
|
+
* What a {@link ModelClaim} points at — the target locator as SDK callers see
|
|
254
|
+
* it, keyed by `model` and `id` rather than the wire schema's `entityType` and
|
|
255
|
+
* `entityId`. This is the public `ModelTarget` shape.
|
|
272
256
|
*/
|
|
273
257
|
export const modelTargetSchema = z
|
|
274
258
|
.object({
|
|
@@ -281,17 +265,16 @@ export const modelTargetSchema = z
|
|
|
281
265
|
})
|
|
282
266
|
.readonly();
|
|
283
267
|
/**
|
|
284
|
-
* A claim as
|
|
285
|
-
* (`ablo.<model>.claim.state`, `/v1/claims`) — the resolved, peer-readable
|
|
286
|
-
*
|
|
287
|
-
*
|
|
288
|
-
* route copies adopt it once the engine dist is rebuilt.
|
|
268
|
+
* A claim as SDK callers and the HTTP claim routes see it
|
|
269
|
+
* (`ablo.<model>.claim.state`, `/v1/claims`) — the resolved, peer-readable view
|
|
270
|
+
* of one active or queued claim. The client's `ModelClaim` type derives from
|
|
271
|
+
* this shape.
|
|
289
272
|
*
|
|
290
|
-
* `expiresAt` is
|
|
291
|
-
*
|
|
292
|
-
* and errors
|
|
293
|
-
* `participantKind`
|
|
294
|
-
* `'human'` frame normalizes to `'user'`.
|
|
273
|
+
* `expiresAt` is epoch milliseconds (a number), the same encoding as the
|
|
274
|
+
* WebSocket {@link WireClaim}, so one timestamp representation spans the wire,
|
|
275
|
+
* the SDK, HTTP, and errors — there is no ISO string anywhere.
|
|
276
|
+
* `participantKind` is parsed through {@link wireParticipantKindSchema}, so a
|
|
277
|
+
* legacy `'human'` frame normalizes to `'user'`.
|
|
295
278
|
*/
|
|
296
279
|
export const modelClaimSchema = z
|
|
297
280
|
.object({
|
|
@@ -309,10 +292,10 @@ export const modelClaimSchema = z
|
|
|
309
292
|
})
|
|
310
293
|
.readonly();
|
|
311
294
|
/**
|
|
312
|
-
* `claim_begin` payload
|
|
313
|
-
*
|
|
314
|
-
* stamps the lifecycle
|
|
315
|
-
* shape — this is exactly what the
|
|
295
|
+
* The `claim_begin` payload a client sends. It carries the descriptive target
|
|
296
|
+
* and reason, an optional duration hint, and the opt-in fair-queue flag. The
|
|
297
|
+
* server stamps the lifecycle and timestamp fields, so they are not part of
|
|
298
|
+
* this inbound shape — this is exactly what the server validates on ingest.
|
|
316
299
|
*/
|
|
317
300
|
export const claimBeginPayloadSchema = targetRefSchema.extend({
|
|
318
301
|
claimId: z.string(),
|
|
@@ -320,19 +303,17 @@ export const claimBeginPayloadSchema = targetRefSchema.extend({
|
|
|
320
303
|
/** Hint for `expiresAt`; the server caps it. */
|
|
321
304
|
estimatedMs: z.number().optional(),
|
|
322
305
|
/**
|
|
323
|
-
* Opt into the fair wait queue
|
|
324
|
-
* enqueues this claim
|
|
325
|
-
* `claim_granted
|
|
326
|
-
* handle the grant.
|
|
306
|
+
* Opt into the fair wait queue. When the target is already held, the server
|
|
307
|
+
* enqueues this claim in FIFO order and replies `claim_queued`, then
|
|
308
|
+
* `claim_granted` later, instead of `claim_rejected`. A client that sets this
|
|
309
|
+
* must be ready to handle the grant.
|
|
327
310
|
*/
|
|
328
311
|
queue: z.boolean().optional(),
|
|
329
312
|
});
|
|
330
313
|
/**
|
|
331
|
-
* `claim_abandon` payload
|
|
332
|
-
*
|
|
333
|
-
*
|
|
334
|
-
* wire type omitted these two even though the handler reads them; the schema
|
|
335
|
-
* documents what the code actually uses.)
|
|
314
|
+
* The `claim_abandon` payload a client sends. `entityType` and `entityId` let
|
|
315
|
+
* the server dequeue a claim that is still waiting (not yet held) from the FIFO
|
|
316
|
+
* line; abandoning a claim that is already held needs only `claimId`.
|
|
336
317
|
*/
|
|
337
318
|
export const claimAbandonPayloadSchema = z.object({
|
|
338
319
|
claimId: z.string(),
|
|
@@ -340,13 +321,13 @@ export const claimAbandonPayloadSchema = z.object({
|
|
|
340
321
|
entityId: z.string().optional(),
|
|
341
322
|
});
|
|
342
323
|
/**
|
|
343
|
-
* `claim_reorder` payload
|
|
344
|
-
* supervisor over its sub-agents
|
|
345
|
-
* `order` lists waiters by `heldBy
|
|
346
|
-
* not listed
|
|
347
|
-
* who may call this
|
|
348
|
-
*
|
|
349
|
-
* positions —
|
|
324
|
+
* The `claim_reorder` payload a client sends. A privileged participant, such as
|
|
325
|
+
* a supervisor over its sub-agents, re-ranks the FIFO wait queue for an entity:
|
|
326
|
+
* `order` lists waiters by `heldBy` and `claimId` in the desired priority, and
|
|
327
|
+
* any waiter not listed keeps its relative order behind those that are. The
|
|
328
|
+
* server gates who may call this and drops an unauthorized sender. Where
|
|
329
|
+
* `claim_abandon` acts on the caller's own entry, a reorder acts on other
|
|
330
|
+
* participants' queue positions — which is why it is gated.
|
|
350
331
|
*/
|
|
351
332
|
export const claimReorderPayloadSchema = z.object({
|
|
352
333
|
entityType: z.string(),
|
|
@@ -354,15 +335,98 @@ export const claimReorderPayloadSchema = z.object({
|
|
|
354
335
|
order: z.array(z.object({ heldBy: z.string(), claimId: z.string() })),
|
|
355
336
|
});
|
|
356
337
|
// ─────────────────────────────────────────────────────────────────────────
|
|
338
|
+
// Heartbeat — the async / long-running-work surface of a claim.
|
|
339
|
+
//
|
|
340
|
+
// A claim's TTL is crash cleanup, not a work-duration estimate. Work that
|
|
341
|
+
// outlives it — an agent run, a background worker's job — keeps its lease by
|
|
342
|
+
// BEATING: request `claim_heartbeat`, reply `claim_heartbeat_ack`. One field
|
|
343
|
+
// set serves every shape; the single and batched payloads are both derived
|
|
344
|
+
// from it, and the WebSocket frame and HTTP routes are two encodings of the
|
|
345
|
+
// same messages. Everything long-running-work-related on the wire lives in
|
|
346
|
+
// this block.
|
|
347
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
348
|
+
/**
|
|
349
|
+
* The one field set behind every heartbeat message. The single-claim payload
|
|
350
|
+
* refines it; the batched payload picks from it — there is deliberately no
|
|
351
|
+
* second shape to keep in sync.
|
|
352
|
+
*/
|
|
353
|
+
const claimHeartbeatFieldsSchema = z.object({
|
|
354
|
+
claimId: z.string().optional(),
|
|
355
|
+
entityType: z.string().optional(),
|
|
356
|
+
entityId: z.string().optional(),
|
|
357
|
+
/** Requested extension from now; the server clamps it, and an extension
|
|
358
|
+
* never shortens a lease. */
|
|
359
|
+
ttlMs: z.number().positive().optional(),
|
|
360
|
+
/**
|
|
361
|
+
* Lightweight progress the beat carries along ("42/100 pages") — stored
|
|
362
|
+
* as the claim's `meta.progress` (last beat wins) and peer-visible via
|
|
363
|
+
* `claim.state` while the lease is held. This is presence, not a
|
|
364
|
+
* checkpoint: it dies with the lease. Crash-recoverable progress belongs
|
|
365
|
+
* in the data itself — write a row, and every subscriber already sees it.
|
|
366
|
+
*/
|
|
367
|
+
details: z.record(z.string(), z.unknown()).optional(),
|
|
368
|
+
});
|
|
369
|
+
/**
|
|
370
|
+
* The `claim_heartbeat` payload a client sends to extend a lease it holds (or
|
|
371
|
+
* refresh its slot in the wait queue) past the liveness window — the
|
|
372
|
+
* work-duration signal for long-running holders, distinct from the connection
|
|
373
|
+
* keepalive.
|
|
374
|
+
*
|
|
375
|
+
* The claim is identified either way: by `claimId`, or — since a claim is
|
|
376
|
+
* singular per (actor, entity) — by the full `entityType`/`entityId` target
|
|
377
|
+
* ("my claim on this row"). At least one of the two must be present. The
|
|
378
|
+
* target also lets the server resolve without a scan and is required to
|
|
379
|
+
* refresh a *queued* claim (a waiter is not in the holder set the server
|
|
380
|
+
* would otherwise search).
|
|
381
|
+
*/
|
|
382
|
+
export const claimHeartbeatPayloadSchema = claimHeartbeatFieldsSchema.refine((payload) => payload.claimId !== undefined ||
|
|
383
|
+
(payload.entityType !== undefined && payload.entityId !== undefined), {
|
|
384
|
+
message: 'a heartbeat must identify its claim — pass claimId, or entityType and entityId together',
|
|
385
|
+
});
|
|
386
|
+
/**
|
|
387
|
+
* The server's reply to a `claim_heartbeat`. For a socketless worker the
|
|
388
|
+
* heartbeat reply is the only inbound signal path, so it carries the lease's
|
|
389
|
+
* fate rather than a bare ok: `held` (extended to `expiresAt`), `queued`
|
|
390
|
+
* (slot refreshed; `position` is the current place in line), or `lost` (the
|
|
391
|
+
* lease expired and the queue moved on — the worker should abandon or
|
|
392
|
+
* re-queue, and any write it still attempts is caught by its `readAt` guard).
|
|
393
|
+
*/
|
|
394
|
+
export const claimHeartbeatAckPayloadSchema = z.object({
|
|
395
|
+
claimId: z.string(),
|
|
396
|
+
status: z.enum(['held', 'queued', 'lost']),
|
|
397
|
+
expiresAt: z.number().optional(),
|
|
398
|
+
position: z.number().optional(),
|
|
399
|
+
/**
|
|
400
|
+
* How many participants are waiting in line behind a held lease — the
|
|
401
|
+
* cooperative-yield pressure signal (present on `held`). A worker that can
|
|
402
|
+
* checkpoint may choose to release early when others wait. Hard
|
|
403
|
+
* cancellation needs no extra field: a preempted, expired, or revoked
|
|
404
|
+
* lease answers the next beat with `lost`.
|
|
405
|
+
*/
|
|
406
|
+
queueDepth: z.number().optional(),
|
|
407
|
+
});
|
|
408
|
+
/**
|
|
409
|
+
* The batched heartbeat — one request extends every lease the caller holds
|
|
410
|
+
* on its plane (the socketless twin of the WebSocket keepalive, which renews
|
|
411
|
+
* all held leases on every ping). For a worker holding many rows this is one
|
|
412
|
+
* round trip per cadence instead of one per claim. Queued slots are not
|
|
413
|
+
* batch-refreshed: a waiter knows its target and beats it directly.
|
|
414
|
+
*/
|
|
415
|
+
export const claimHeartbeatBatchPayloadSchema = claimHeartbeatFieldsSchema.pick({ ttlMs: true });
|
|
416
|
+
/** Reply to a batched heartbeat: one ack entry per lease that was extended. */
|
|
417
|
+
export const claimHeartbeatBatchAckPayloadSchema = z.object({
|
|
418
|
+
results: z.array(claimHeartbeatAckPayloadSchema),
|
|
419
|
+
});
|
|
420
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
357
421
|
// Read interest — area-of-interest navigation (update_subscription)
|
|
358
422
|
// ─────────────────────────────────────────────────────────────────────────
|
|
359
423
|
/**
|
|
360
|
-
* `update_subscription` payload
|
|
361
|
-
* connection
|
|
362
|
-
*
|
|
424
|
+
* The `update_subscription` payload a client sends. It replaces the
|
|
425
|
+
* connection's read interest with the complete set of sync groups — the read
|
|
426
|
+
* counterpart to a claim, with no write lock and no TTL. Each entry is a
|
|
363
427
|
* {@link syncGroupInputSchema} (`'default'` or a branded `kind:id`), so a
|
|
364
|
-
* malformed group is rejected
|
|
365
|
-
*
|
|
428
|
+
* malformed group is rejected on ingest rather than silently indexed. The
|
|
429
|
+
* element type is strict because this is untrusted client input.
|
|
366
430
|
*/
|
|
367
431
|
export const updateSubscriptionPayloadSchema = z.object({
|
|
368
432
|
syncGroups: z.array(syncGroupInputSchema),
|
|
@@ -405,7 +469,7 @@ export const commitOperationSchema = writeGuardSchema.extend({
|
|
|
405
469
|
transactionId: z.string().nullish(),
|
|
406
470
|
});
|
|
407
471
|
// ─────────────────────────────────────────────────────────────────────────
|
|
408
|
-
// Layer 1 —
|
|
472
|
+
// Layer 1 — presence (observation only; it never enforces)
|
|
409
473
|
// ─────────────────────────────────────────────────────────────────────────
|
|
410
474
|
export const presenceKindSchema = z.enum(['enter', 'update', 'leave']);
|
|
411
475
|
/** What a participant is actively working on (agents fill this in). */
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* A claim log — the simplest way to watch agents collide. The engine reports
|
|
3
|
+
* the claim lifecycle (`acquired → queued → granted → lost / rejected /
|
|
4
|
+
* expired`) and the notify-instead-of-abort stale write through two channels:
|
|
5
|
+
* human-readable `logger` lines (shown with `new Ablo({ debug: true })`) and
|
|
6
|
+
* structured `observability.captureClaim` / `captureConflict` calls.
|
|
3
7
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* and structured `observability.captureClaim` / `captureConflict` calls.
|
|
8
|
-
*
|
|
9
|
-
* This is the third, evals-shaped path: hand a {@link ClaimLog} to
|
|
10
|
-
* `Ablo({ observability })`, run your scenario, then read back an ordered list
|
|
11
|
-
* you can print for eyeballing or `collisions()` for assertions.
|
|
8
|
+
* This is a third channel, shaped for tests and evals. Pass a {@link ClaimLog}
|
|
9
|
+
* as `Ablo({ observability })`, run your scenario, then read back an ordered
|
|
10
|
+
* list — print it to eyeball what happened, or call `collisions()` to assert on it.
|
|
12
11
|
*/
|
|
13
12
|
import type { SyncObservabilityProvider, ClaimEvent, ConflictEvent } from '../interfaces/index.js';
|
|
14
13
|
/** A claim state change as one quiet, greppable line. */
|
|
@@ -19,7 +18,7 @@ export declare function formatConflict(e: ConflictEvent): string;
|
|
|
19
18
|
export interface ClaimLogEntry {
|
|
20
19
|
/** Monotonic order index — deterministic, clock-free, eval-friendly. */
|
|
21
20
|
readonly seq: number;
|
|
22
|
-
/** The same one-line text the console and breadcrumb
|
|
21
|
+
/** The same one-line text the console and breadcrumb channels emit. */
|
|
23
22
|
readonly line: string;
|
|
24
23
|
/** A collision worth flagging: a rejected/lost claim or a stale write. */
|
|
25
24
|
readonly collision: boolean;
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* A claim log — the simplest way to watch agents collide. The engine reports
|
|
3
|
+
* the claim lifecycle (`acquired → queued → granted → lost / rejected /
|
|
4
|
+
* expired`) and the notify-instead-of-abort stale write through two channels:
|
|
5
|
+
* human-readable `logger` lines (shown with `new Ablo({ debug: true })`) and
|
|
6
|
+
* structured `observability.captureClaim` / `captureConflict` calls.
|
|
3
7
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* and structured `observability.captureClaim` / `captureConflict` calls.
|
|
8
|
-
*
|
|
9
|
-
* This is the third, evals-shaped path: hand a {@link ClaimLog} to
|
|
10
|
-
* `Ablo({ observability })`, run your scenario, then read back an ordered list
|
|
11
|
-
* you can print for eyeballing or `collisions()` for assertions.
|
|
8
|
+
* This is a third channel, shaped for tests and evals. Pass a {@link ClaimLog}
|
|
9
|
+
* as `Ablo({ observability })`, run your scenario, then read back an ordered
|
|
10
|
+
* list — print it to eyeball what happened, or call `collisions()` to assert on it.
|
|
12
11
|
*/
|
|
13
12
|
// ─────────────────────────────────────────────
|
|
14
|
-
// Formatters — one readable line per event. Shared by the
|
|
15
|
-
//
|
|
13
|
+
// Formatters — one readable line per event. Shared by the debug logger and
|
|
14
|
+
// this log so console output and log output never drift.
|
|
16
15
|
// ─────────────────────────────────────────────
|
|
17
16
|
/** A claim state change as one quiet, greppable line. */
|
|
18
17
|
export function formatClaim(e) {
|
|
@@ -22,7 +21,7 @@ export function formatClaim(e) {
|
|
|
22
21
|
const actor = e.actor
|
|
23
22
|
? `${e.actor}${e.participantKind ? ` (${e.participantKind})` : ''}`
|
|
24
23
|
: '';
|
|
25
|
-
// `rejected`/`lost` name the
|
|
24
|
+
// `rejected`/`lost` name the blocking holder; the rest name us (or no one).
|
|
26
25
|
const by = actor
|
|
27
26
|
? e.phase === 'rejected' || e.phase === 'lost'
|
|
28
27
|
? ` — held by ${actor}`
|
|
@@ -54,7 +53,7 @@ export function formatConflict(e) {
|
|
|
54
53
|
*/
|
|
55
54
|
export class ClaimLog {
|
|
56
55
|
max;
|
|
57
|
-
// Immutable list: a
|
|
56
|
+
// Immutable list: a new array reference on every change. This is what lets
|
|
58
57
|
// `useSyncExternalStore(log.onChange, () => log.entries)` detect updates —
|
|
59
58
|
// it compares snapshots by reference, so an in-place push would never render.
|
|
60
59
|
rows = [];
|
|
@@ -64,7 +63,7 @@ export class ClaimLog {
|
|
|
64
63
|
constructor(max = 1_000) {
|
|
65
64
|
this.max = max;
|
|
66
65
|
}
|
|
67
|
-
// —— the two
|
|
66
|
+
// —— the two channels we record ——
|
|
68
67
|
captureClaim(claim) {
|
|
69
68
|
this.add({
|
|
70
69
|
line: formatClaim(claim),
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* Follows Ablo's architecture for database management.
|
|
2
|
+
* Manages the client-side IndexedDB databases the sync engine keeps in the
|
|
3
|
+
* browser. There are two tiers. A single registry database, `ablo_databases`,
|
|
4
|
+
* records metadata about every workspace database. Each user-and-workspace pair
|
|
5
|
+
* then gets its own data database, named `ablo_<hash>`, that holds the synced
|
|
6
|
+
* rows. {@link DatabaseManager} creates, versions, and deletes both tiers.
|
|
9
7
|
*/
|
|
10
8
|
export interface DatabaseInfo {
|
|
11
9
|
name: string;
|