@abloatai/ablo 0.34.1 → 0.35.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/AGENTS.md +2 -1
- package/CHANGELOG.md +674 -5
- package/README.md +39 -22
- package/dist/BaseSyncedStore.d.ts +152 -44
- package/dist/BaseSyncedStore.js +300 -184
- package/dist/Database.d.ts +9 -24
- package/dist/Database.js +37 -22
- package/dist/InstanceCache.d.ts +25 -4
- package/dist/InstanceCache.js +48 -15
- package/dist/LazyReferenceCollection.d.ts +3 -3
- package/dist/LazyReferenceCollection.js +4 -4
- package/dist/Model.d.ts +6 -6
- package/dist/Model.js +10 -10
- package/dist/ModelRegistry.d.ts +4 -4
- package/dist/ModelRegistry.js +3 -3
- package/dist/{SyncEngineContext.d.ts → RuntimeContext.d.ts} +20 -13
- package/dist/{SyncEngineContext.js → RuntimeContext.js} +11 -12
- package/dist/SyncClient.d.ts +42 -32
- package/dist/SyncClient.js +166 -110
- package/dist/ai-sdk/coordinatedTool.d.ts +2 -2
- package/dist/ai-sdk/coordinatedTool.js +1 -1
- package/dist/ai-sdk/coordinationContext.d.ts +2 -2
- package/dist/ai-sdk/coordinationContext.js +1 -1
- package/dist/ai-sdk/wrap.d.ts +3 -3
- package/dist/ai-sdk/wrap.js +2 -2
- package/dist/auth/index.d.ts +1 -156
- package/dist/auth/index.js +8 -301
- package/dist/cli.cjs +3344 -1073
- package/dist/client/Ablo.d.ts +42 -287
- package/dist/client/Ablo.js +118 -963
- package/dist/client/abloClient.d.ts +309 -0
- package/dist/client/abloClient.js +13 -0
- package/dist/client/clientPrelude.d.ts +52 -0
- package/dist/client/clientPrelude.js +60 -0
- package/dist/client/consoleLogger.d.ts +2 -2
- package/dist/client/coreClient.d.ts +60 -0
- package/dist/client/coreClient.js +118 -0
- package/dist/client/createInternalComponents.d.ts +4 -4
- package/dist/client/createInternalComponents.js +9 -8
- package/dist/client/createModelProxy.d.ts +78 -373
- package/dist/client/createModelProxy.js +114 -86
- package/dist/client/humans.d.ts +48 -0
- package/dist/client/humans.js +52 -0
- package/dist/client/modelRegistration.d.ts +1 -1
- package/dist/client/modelRegistration.js +9 -9
- package/dist/client/options.d.ts +73 -17
- package/dist/client/reactiveEngine.d.ts +48 -0
- package/dist/client/reactiveEngine.js +910 -0
- package/dist/client/resourceTypes.d.ts +9 -250
- package/dist/client/resourceTypes.js +8 -5
- package/dist/client/schemaConfig.d.ts +4 -4
- package/dist/client/schemaConfig.js +6 -2
- package/dist/client/validateAbloOptions.d.ts +3 -2
- package/dist/client/validateAbloOptions.js +1 -1
- package/dist/client/wsMutationExecutor.d.ts +3 -3
- package/dist/client/wsMutationExecutor.js +3 -3
- package/dist/context.d.ts +9 -9
- package/dist/context.js +10 -9
- package/dist/coordination/ClaimLog.d.ts +26 -0
- package/dist/coordination/ClaimLog.js +32 -0
- package/dist/coordination/index.d.ts +1 -15
- package/dist/coordination/index.js +8 -31
- package/dist/core/DatabaseManager.js +1 -1
- package/dist/core/QueryView.d.ts +1 -1
- package/dist/core/QueryView.js +1 -1
- package/dist/core/StoreManager.d.ts +4 -23
- package/dist/core/StoreManager.js +5 -55
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +2 -2
- package/dist/core/storeContract.d.ts +2 -2
- package/dist/docs/catalog.d.ts +72 -0
- package/dist/docs/catalog.js +227 -0
- package/dist/docs/index.d.ts +10 -0
- package/dist/docs/index.js +10 -0
- package/dist/environment.d.ts +1 -40
- package/dist/environment.js +8 -37
- package/dist/index.d.ts +40 -34
- package/dist/index.js +26 -20
- package/dist/interfaces/index.d.ts +44 -134
- package/dist/keys/index.d.ts +1 -77
- package/dist/keys/index.js +8 -190
- package/dist/mutators/{RecordingTransaction.d.ts → RecordingMutation.d.ts} +4 -4
- package/dist/mutators/{RecordingTransaction.js → RecordingMutation.js} +2 -2
- package/dist/mutators/Transaction.d.ts +1 -1
- package/dist/mutators/Transaction.js +1 -1
- package/dist/mutators/UndoManager.d.ts +6 -6
- package/dist/mutators/UndoManager.js +5 -5
- package/dist/mutators/defineMutators.d.ts +3 -3
- package/dist/mutators/defineMutators.js +1 -1
- package/dist/mutators/inverseOp.js +2 -2
- package/dist/mutators/mutateActions.d.ts +3 -3
- package/dist/mutators/mutateActions.js +1 -1
- package/dist/mutators/readerActions.d.ts +1 -1
- package/dist/mutators/undoApply.d.ts +1 -1
- package/dist/mutators/undoApply.js +1 -1
- package/dist/policy/index.d.ts +2 -2
- package/dist/policy/index.js +1 -1
- package/dist/query/client.d.ts +2 -2
- package/dist/query/client.js +4 -4
- package/dist/query/types.d.ts +6 -41
- package/dist/query/types.js +2 -2
- package/dist/react/AbloProvider.d.ts +6 -8
- package/dist/react/AbloProvider.js +5 -7
- package/dist/react/context.d.ts +1 -1
- package/dist/react/context.js +1 -1
- package/dist/react/index.d.ts +5 -5
- package/dist/react/index.js +3 -3
- package/dist/react/internalContext.d.ts +1 -1
- package/dist/react/useAblo.d.ts +3 -3
- package/dist/react/useAblo.js +1 -1
- package/dist/react/useCurrentUserId.js +1 -1
- package/dist/react/useErrorListener.js +1 -1
- package/dist/react/useMutationFailureListener.d.ts +2 -2
- package/dist/react/useMutationFailureListener.js +1 -1
- package/dist/react/useMutators.d.ts +3 -3
- package/dist/react/useMutators.js +3 -3
- package/dist/react/useUndoScope.d.ts +5 -5
- package/dist/react/useUndoScope.js +1 -1
- package/dist/schema/coordination.d.ts +69 -10
- package/dist/schema/coordination.js +86 -9
- package/dist/schema/ddl.js +2 -2
- package/dist/schema/diff.d.ts +1 -1
- package/dist/schema/generate.js +1 -1
- package/dist/schema/index.d.ts +10 -10
- package/dist/schema/index.js +18 -18
- package/dist/schema/queries.d.ts +27 -27
- package/dist/schema/queries.js +23 -23
- package/dist/schema/select.d.ts +3 -3
- package/dist/schema/select.js +3 -3
- package/dist/schema/serialize.d.ts +15 -6
- package/dist/schema/serialize.js +17 -3
- package/dist/schema/sugar.d.ts +6 -7
- package/dist/schema/sugar.js +9 -12
- package/dist/schema/syncDeltaRow.d.ts +4 -152
- package/dist/schema/syncDeltaRow.js +4 -105
- package/dist/server/adapter.d.ts +18 -1
- package/dist/server/commit.d.ts +10 -16
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/readConfig.d.ts +1 -1
- package/dist/source/adapters/drizzle.d.ts +1 -1
- package/dist/source/adapters/drizzle.js +2 -2
- package/dist/source/adapters/kysely.d.ts +1 -1
- package/dist/source/adapters/kysely.js +1 -1
- package/dist/source/adapters/kyselyMutationCore.d.ts +1 -1
- package/dist/source/adapters/kyselyMutationCore.js +2 -2
- package/dist/source/adapters/memory.js +1 -1
- package/dist/source/adapters/prisma.d.ts +8 -3
- package/dist/source/adapters/prisma.js +1 -1
- package/dist/source/connector.js +1 -1
- package/dist/source/connectorProtocol.d.ts +2 -8
- package/dist/source/connectorProtocol.js +3 -2
- package/dist/source/contract.d.ts +29 -17
- package/dist/source/contract.js +27 -22
- package/dist/source/factory.d.ts +1 -1
- package/dist/source/footprint.d.ts +111 -0
- package/dist/source/footprint.js +0 -0
- package/dist/source/idempotency.js +2 -2
- package/dist/source/index.d.ts +1 -0
- package/dist/source/index.js +3 -0
- package/dist/source/next.d.ts +1 -1
- package/dist/source/signing.d.ts +9 -2
- package/dist/source/signing.js +4 -1
- package/dist/source/types.d.ts +6 -4
- package/dist/source/types.js +1 -1
- package/dist/stores/ObjectStore.d.ts +1 -1
- package/dist/stores/SyncActionStore.d.ts +1 -1
- package/dist/stores/SyncActionStore.js +2 -10
- package/dist/stores/syncAction.d.ts +26 -0
- package/dist/stores/syncAction.js +16 -0
- package/dist/surface.d.ts +3 -3
- package/dist/surface.js +6 -4
- package/dist/sync/BootstrapFetcher.d.ts +123 -6
- package/dist/sync/BootstrapFetcher.js +492 -66
- package/dist/sync/ConnectionManager.d.ts +6 -198
- package/dist/sync/ConnectionManager.js +6 -677
- package/dist/sync/OnDemandLoader.d.ts +2 -2
- package/dist/sync/OnDemandLoader.js +60 -21
- package/dist/sync/SubscriptionManager.d.ts +13 -2
- package/dist/sync/SubscriptionManager.js +23 -5
- package/dist/sync/SyncWebSocket.d.ts +27 -510
- package/dist/sync/SyncWebSocket.js +76 -954
- package/dist/sync/awaitClaimGrant.d.ts +4 -44
- package/dist/sync/awaitClaimGrant.js +4 -109
- package/dist/sync/commitFrames.d.ts +6 -40
- package/dist/sync/commitFrames.js +6 -97
- package/dist/sync/contextPorts.d.ts +18 -0
- package/dist/sync/contextPorts.js +31 -0
- package/dist/sync/createClaimStream.d.ts +5 -49
- package/dist/sync/createClaimStream.js +5 -469
- package/dist/sync/createPresenceStream.d.ts +26 -4
- package/dist/sync/createPresenceStream.js +28 -20
- package/dist/sync/createSnapshot.d.ts +2 -2
- package/dist/sync/createSnapshot.js +1 -1
- package/dist/sync/credentialLifecycle.d.ts +5 -173
- package/dist/sync/credentialLifecycle.js +5 -320
- package/dist/sync/deltaPipeline.d.ts +1 -1
- package/dist/sync/participants.d.ts +5 -4
- package/dist/sync/participants.js +29 -22
- package/dist/sync/schemaDrift.d.ts +55 -0
- package/dist/sync/schemaDrift.js +53 -0
- package/dist/sync/schemas.d.ts +21 -32
- package/dist/sync/schemas.js +26 -17
- package/dist/sync/syncPlan.d.ts +3 -3
- package/dist/sync/wsFrameHandlers.d.ts +6 -114
- package/dist/sync/wsFrameHandlers.js +6 -392
- package/dist/testing/fixtures/bootstrap.d.ts +1 -1
- package/dist/testing/fixtures/deltas.d.ts +1 -1
- package/dist/testing/fixtures/httpResponses.d.ts +70 -0
- package/dist/testing/fixtures/httpResponses.js +90 -0
- package/dist/testing/fixtures/models.js +1 -1
- package/dist/testing/helpers/wait.js +1 -1
- package/dist/testing/mocks/MockMutationExecutor.d.ts +2 -2
- package/dist/testing/mocks/MockMutationExecutor.js +8 -14
- package/dist/testing/mocks/MockSyncContext.d.ts +11 -11
- package/dist/testing/mocks/MockSyncContext.js +10 -9
- package/dist/testing/mocks/MockSyncStore.js +1 -1
- package/dist/testing/mocks/MockWebSocket.d.ts +2 -2
- package/dist/transaction/ablo.d.ts +88 -0
- package/dist/transaction/ablo.js +33 -0
- package/dist/{client/auth.d.ts → transaction/auth/apiKey.d.ts} +43 -8
- package/dist/{client/auth.js → transaction/auth/apiKey.js} +19 -0
- package/dist/transaction/auth/bootstrapScope.d.ts +15 -0
- package/dist/transaction/auth/bootstrapScope.js +1 -0
- package/dist/transaction/auth/capability.d.ts +177 -0
- package/dist/transaction/auth/capability.js +199 -0
- package/dist/{auth → transaction/auth}/credentialSource.d.ts +8 -1
- package/dist/{client → transaction/auth}/identity.d.ts +8 -7
- package/dist/{client → transaction/auth}/identity.js +1 -1
- package/dist/transaction/auth/index.d.ts +162 -0
- package/dist/transaction/auth/index.js +304 -0
- package/dist/{auth → transaction/auth}/schemas.d.ts +1 -1
- package/dist/{auth → transaction/auth}/schemas.js +13 -13
- package/dist/{client → transaction/auth}/sessionMint.d.ts +8 -8
- package/dist/{client → transaction/auth}/sessionMint.js +4 -7
- package/dist/transaction/coordination/awaitClaimGrant.d.ts +49 -0
- package/dist/transaction/coordination/awaitClaimGrant.js +112 -0
- package/dist/transaction/coordination/claimMeta.d.ts +49 -0
- package/dist/transaction/coordination/claimMeta.js +52 -0
- package/dist/transaction/coordination/createClaimStream.d.ts +64 -0
- package/dist/transaction/coordination/createClaimStream.js +475 -0
- package/dist/transaction/coordination/events.d.ts +74 -0
- package/dist/transaction/coordination/events.js +7 -0
- package/dist/transaction/coordination/index.d.ts +19 -0
- package/dist/transaction/coordination/index.js +44 -0
- package/dist/transaction/coordination/locator.d.ts +83 -0
- package/dist/transaction/coordination/locator.js +82 -0
- package/dist/transaction/coordination/schema.d.ts +1473 -0
- package/dist/{coordination → transaction/coordination}/schema.js +490 -55
- package/dist/transaction/coordination/targetConflict.d.ts +2 -0
- package/dist/transaction/coordination/targetConflict.js +103 -0
- package/dist/{coordination → transaction/coordination}/trace.d.ts +7 -18
- package/dist/{coordination → transaction/coordination}/trace.js +18 -25
- package/dist/transaction/durableWrites.d.ts +62 -0
- package/dist/{client → transaction}/durableWrites.js +28 -3
- package/dist/transaction/environment.d.ts +105 -0
- package/dist/transaction/environment.js +108 -0
- package/dist/{errorCodes.d.ts → transaction/errorCodes.d.ts} +11 -11
- package/dist/{errorCodes.js → transaction/errorCodes.js} +35 -12
- package/dist/{errors.d.ts → transaction/errors.d.ts} +37 -13
- package/dist/{errors.js → transaction/errors.js} +85 -16
- package/dist/transaction/index.d.ts +20 -0
- package/dist/transaction/index.js +20 -0
- package/dist/transaction/keys/index.d.ts +87 -0
- package/dist/transaction/keys/index.js +207 -0
- package/dist/transaction/log/syncDeltaRow.d.ts +158 -0
- package/dist/transaction/log/syncDeltaRow.js +95 -0
- package/dist/{sync/syncPosition.d.ts → transaction/logPosition.d.ts} +22 -8
- package/dist/{sync/syncPosition.js → transaction/logPosition.js} +15 -6
- package/dist/transaction/logger.d.ts +16 -0
- package/dist/transaction/logger.js +7 -0
- package/dist/transaction/observability.d.ts +53 -0
- package/dist/transaction/observability.js +19 -0
- package/dist/transaction/plugin.d.ts +192 -0
- package/dist/transaction/plugin.js +87 -0
- package/dist/{policy → transaction/policy}/types.d.ts +3 -3
- package/dist/{policy → transaction/policy}/types.js +2 -0
- package/dist/transaction/resources/httpResources.d.ts +266 -0
- package/dist/transaction/resources/httpResources.js +7 -0
- package/dist/transaction/resources/modelOperations.d.ts +319 -0
- package/dist/transaction/resources/modelOperations.js +12 -0
- package/dist/transaction/resources/mutationOptions.d.ts +66 -0
- package/dist/transaction/resources/mutationOptions.js +9 -0
- package/dist/transaction/resources/where.d.ts +85 -0
- package/dist/transaction/resources/where.js +70 -0
- package/dist/{client → transaction/resources}/writeOptionsSchema.d.ts +2 -6
- package/dist/{client → transaction/resources}/writeOptionsSchema.js +9 -5
- package/dist/{schema → transaction/schema}/field.d.ts +5 -5
- package/dist/{schema → transaction/schema}/field.js +5 -5
- package/dist/transaction/schema/loadStrategy.d.ts +45 -0
- package/dist/transaction/schema/loadStrategy.js +46 -0
- package/dist/{schema → transaction/schema}/model.d.ts +50 -35
- package/dist/{schema → transaction/schema}/model.js +30 -20
- package/dist/transaction/schema/openapi.d.ts +57 -0
- package/dist/transaction/schema/openapi.js +340 -0
- package/dist/{schema → transaction/schema}/relation.d.ts +14 -14
- package/dist/{schema → transaction/schema}/relation.js +7 -7
- package/dist/{schema → transaction/schema}/residency.d.ts +0 -9
- package/dist/{schema → transaction/schema}/residency.js +0 -5
- package/dist/{schema → transaction/schema}/roles.d.ts +5 -5
- package/dist/{schema → transaction/schema}/roles.js +5 -5
- package/dist/{schema → transaction/schema}/schema.d.ts +12 -10
- package/dist/{schema → transaction/schema}/schema.js +4 -3
- package/dist/{schema → transaction/schema}/tenancy.d.ts +1 -1
- package/dist/{schema → transaction/schema}/tenancy.js +7 -4
- package/dist/transaction/transactionLayer.d.ts +82 -0
- package/dist/transaction/transactionLayer.js +24 -0
- package/dist/{transactions → transaction/transactions/settlement}/commitEnvelope.d.ts +4 -5
- package/dist/{transactions → transaction/transactions/settlement}/commitEnvelope.js +9 -13
- package/dist/{transactions → transaction/transactions/settlement}/httpCommitEnvelope.d.ts +9 -2
- package/dist/{transactions → transaction/transactions/settlement}/httpCommitEnvelope.js +5 -9
- package/dist/{transactions/durableWriteStore.d.ts → transaction/transactions/settlement/pendingWrite.d.ts} +10 -36
- package/dist/transaction/transactions/settlement/pendingWrite.js +20 -0
- package/dist/transaction/transport/commitFrames.d.ts +90 -0
- package/dist/transaction/transport/commitFrames.js +134 -0
- package/dist/transaction/transport/connectionManager.d.ts +215 -0
- package/dist/transaction/transport/connectionManager.js +673 -0
- package/dist/transaction/transport/credentialLifecycle.d.ts +177 -0
- package/dist/transaction/transport/credentialLifecycle.js +324 -0
- package/dist/{sync → transaction/transport}/heartbeat.d.ts +3 -1
- package/dist/{sync → transaction/transport}/heartbeat.js +6 -4
- package/dist/{client → transaction/transport}/httpClient.d.ts +59 -16
- package/dist/{client → transaction/transport}/httpClient.js +5 -5
- package/dist/transaction/transport/httpOptions.d.ts +33 -0
- package/dist/transaction/transport/httpOptions.js +12 -0
- package/dist/{client → transaction/transport}/httpTransport.js +171 -85
- package/dist/{sync/NetworkProbe.d.ts → transaction/transport/networkProbe.d.ts} +7 -4
- package/dist/{sync/NetworkProbe.js → transaction/transport/networkProbe.js} +14 -13
- package/dist/transaction/transport/wsFrameHandlers.d.ts +128 -0
- package/dist/transaction/transport/wsFrameHandlers.js +429 -0
- package/dist/transaction/transport/wsTransport.d.ts +576 -0
- package/dist/transaction/transport/wsTransport.js +1017 -0
- package/dist/transaction/types/assertExact.d.ts +17 -0
- package/dist/transaction/types/assertExact.js +1 -0
- package/dist/{types → transaction/types}/global.d.ts +17 -2
- package/dist/{types → transaction/types}/global.js +2 -1
- package/dist/{types → transaction/types}/index.d.ts +14 -46
- package/dist/{types → transaction/types}/index.js +7 -16
- package/dist/{types → transaction/types}/streams.d.ts +63 -45
- package/dist/{utils → transaction/utils}/json.d.ts +18 -0
- package/dist/transaction/utils/json.js +276 -0
- package/dist/transaction/wire/accountResponses.d.ts +351 -0
- package/dist/transaction/wire/accountResponses.js +255 -0
- package/dist/transaction/wire/auth.d.ts +49 -0
- package/dist/transaction/wire/auth.js +57 -0
- package/dist/transaction/wire/claimEvent.d.ts +76 -0
- package/dist/transaction/wire/claimEvent.js +73 -0
- package/dist/transaction/wire/claims.d.ts +463 -0
- package/dist/transaction/wire/claims.js +229 -0
- package/dist/{wire → transaction/wire}/commit.d.ts +125 -193
- package/dist/{wire → transaction/wire}/commit.js +68 -47
- package/dist/{wire → transaction/wire}/delta.d.ts +66 -17
- package/dist/{wire → transaction/wire}/delta.js +37 -13
- package/dist/transaction/wire/errorEnvelope.d.ts +72 -0
- package/dist/{wire → transaction/wire}/errorEnvelope.js +36 -5
- package/dist/transaction/wire/feedCursor.d.ts +60 -0
- package/dist/transaction/wire/feedCursor.js +82 -0
- package/dist/transaction/wire/feedEvent.d.ts +177 -0
- package/dist/transaction/wire/feedEvent.js +39 -0
- package/dist/transaction/wire/frames.d.ts +194 -0
- package/dist/transaction/wire/frames.js +50 -0
- package/dist/transaction/wire/inboundFrames.d.ts +552 -0
- package/dist/transaction/wire/inboundFrames.js +116 -0
- package/dist/transaction/wire/index.d.ts +50 -0
- package/dist/transaction/wire/index.js +74 -0
- package/dist/{wire → transaction/wire}/listEnvelope.d.ts +13 -14
- package/dist/transaction/wire/listEnvelope.js +42 -0
- package/dist/transaction/wire/modelResponses.d.ts +85 -0
- package/dist/transaction/wire/modelResponses.js +43 -0
- package/dist/transactions/{TransactionQueue.d.ts → mutations/MutationQueue.d.ts} +79 -38
- package/dist/transactions/{TransactionQueue.js → mutations/MutationQueue.js} +110 -59
- package/dist/transactions/{TransactionStore.d.ts → mutations/MutationStore.d.ts} +8 -8
- package/dist/transactions/{TransactionStore.js → mutations/MutationStore.js} +2 -2
- package/dist/transactions/{coalesceRules.d.ts → mutations/coalesceRules.d.ts} +10 -10
- package/dist/transactions/{coalesceRules.js → mutations/coalesceRules.js} +1 -1
- package/dist/transactions/mutations/commitLatency.d.ts +52 -0
- package/dist/transactions/mutations/commitLatency.js +130 -0
- package/dist/transactions/{commitOutboxStore.d.ts → mutations/commitOutboxStore.d.ts} +1 -5
- package/dist/transactions/{commitOutboxStore.js → mutations/commitOutboxStore.js} +1 -1
- package/dist/transactions/{commitPayload.d.ts → mutations/commitPayload.d.ts} +16 -15
- package/dist/transactions/{commitPayload.js → mutations/commitPayload.js} +12 -12
- package/dist/transactions/{deltaConfirmation.d.ts → mutations/deltaConfirmation.d.ts} +11 -11
- package/dist/transactions/{deltaConfirmation.js → mutations/deltaConfirmation.js} +7 -7
- package/dist/transactions/mutations/durableWriteStore.d.ts +14 -0
- package/dist/transactions/mutations/durableWriteStore.js +12 -0
- package/dist/transactions/{optimisticApply.d.ts → mutations/optimisticApply.d.ts} +7 -7
- package/dist/transactions/{replayValidation.d.ts → mutations/replayValidation.d.ts} +3 -3
- package/dist/transactions/{replayValidation.js → mutations/replayValidation.js} +4 -3
- package/dist/utils/mobxSetup.d.ts +1 -1
- package/dist/utils/mobxSetup.js +5 -2
- package/dist/webhooks/events.d.ts +2 -2
- package/dist/wire/index.d.ts +1 -34
- package/dist/wire/index.js +8 -49
- package/docs/agent-messaging.md +3 -3
- package/docs/agents.md +19 -12
- package/docs/api-keys.md +8 -4
- package/docs/api.md +22 -18
- package/docs/audit.md +2 -0
- package/docs/cli.md +31 -3
- package/docs/client-behavior.md +8 -6
- package/docs/concurrency-convention.md +30 -24
- package/docs/coordination.md +48 -38
- package/docs/data-sources.md +3 -1
- package/docs/debugging.md +5 -3
- package/docs/deployment.md +267 -0
- package/docs/examples/agent-human.md +49 -42
- package/docs/examples/ai-sdk-tool.md +69 -44
- package/docs/examples/existing-python-backend.md +8 -6
- package/docs/examples/nextjs.md +129 -47
- package/docs/examples/scoped-agent.md +45 -44
- package/docs/examples/server-agent.md +46 -26
- package/docs/groups.md +32 -29
- package/docs/guarantees.md +4 -2
- package/docs/how-it-works.md +9 -7
- package/docs/idempotency.md +126 -0
- package/docs/identity.md +58 -54
- package/docs/index.md +172 -86
- package/docs/integration-guide.md +17 -16
- package/docs/interaction-model.md +6 -4
- package/docs/mcp.md +41 -16
- package/docs/migration.md +63 -5
- package/docs/operating-on-your-database.md +3 -1
- package/docs/projects.md +2 -0
- package/docs/quickstart.md +22 -5
- package/docs/react.md +12 -10
- package/docs/schema-contract.md +5 -3
- package/docs/session-settings.md +108 -0
- package/docs/sessions.md +3 -1
- package/docs/webhooks.md +3 -1
- package/llms.txt +47 -17
- package/package.json +10 -8
- package/dist/agent/Agent.d.ts +0 -366
- package/dist/agent/Agent.js +0 -514
- package/dist/agent/index.d.ts +0 -115
- package/dist/agent/index.js +0 -128
- package/dist/agent/session.d.ts +0 -93
- package/dist/agent/session.js +0 -149
- package/dist/agent/types.d.ts +0 -68
- package/dist/agent/types.js +0 -9
- package/dist/client/durableWrites.d.ts +0 -21
- package/dist/coordination/schema.d.ts +0 -722
- package/dist/schema/openapi.d.ts +0 -29
- package/dist/schema/openapi.js +0 -124
- package/dist/transactions/durableWriteStore.js +0 -30
- package/dist/utils/json.js +0 -88
- package/dist/wire/errorEnvelope.d.ts +0 -55
- package/dist/wire/frames.d.ts +0 -197
- package/dist/wire/frames.js +0 -49
- package/dist/wire/listEnvelope.js +0 -18
- /package/dist/{client → transaction/auth}/credentialEndpoint.d.ts +0 -0
- /package/dist/{client → transaction/auth}/credentialEndpoint.js +0 -0
- /package/dist/{auth → transaction/auth}/credentialPolicy.d.ts +0 -0
- /package/dist/{auth → transaction/auth}/credentialPolicy.js +0 -0
- /package/dist/{auth → transaction/auth}/credentialSource.js +0 -0
- /package/dist/{client → transaction/auth}/hostedEndpoints.d.ts +0 -0
- /package/dist/{client → transaction/auth}/hostedEndpoints.js +0 -0
- /package/dist/{client → transaction/coordination}/claimHeartbeatLoop.d.ts +0 -0
- /package/dist/{client → transaction/coordination}/claimHeartbeatLoop.js +0 -0
- /package/dist/{client → transaction}/persistence.d.ts +0 -0
- /package/dist/{client → transaction}/persistence.js +0 -0
- /package/dist/{client → transaction/resources}/functionalUpdate.d.ts +0 -0
- /package/dist/{client → transaction/resources}/functionalUpdate.js +0 -0
- /package/dist/{transactions → transaction/transactions/settlement}/idempotencyKey.d.ts +0 -0
- /package/dist/{transactions → transaction/transactions/settlement}/idempotencyKey.js +0 -0
- /package/dist/{client → transaction/transport}/httpTransport.d.ts +0 -0
- /package/dist/{types → transaction/types}/modelData.d.ts +0 -0
- /package/dist/{types → transaction/types}/modelData.js +0 -0
- /package/dist/{types → transaction/types}/participant.d.ts +0 -0
- /package/dist/{types → transaction/types}/participant.js +0 -0
- /package/dist/{types → transaction/types}/streams.js +0 -0
- /package/dist/{utils → transaction/utils}/asyncIterator.d.ts +0 -0
- /package/dist/{utils → transaction/utils}/asyncIterator.js +0 -0
- /package/dist/{utils → transaction/utils}/duration.d.ts +0 -0
- /package/dist/{utils → transaction/utils}/duration.js +0 -0
- /package/dist/{wire → transaction/wire}/bootstrapReason.d.ts +0 -0
- /package/dist/{wire → transaction/wire}/bootstrapReason.js +0 -0
- /package/dist/{wire → transaction/wire}/protocol.d.ts +0 -0
- /package/dist/{wire → transaction/wire}/protocol.js +0 -0
- /package/dist/{wire → transaction/wire}/protocolVersion.d.ts +0 -0
- /package/dist/{wire → transaction/wire}/protocolVersion.js +0 -0
- /package/dist/transactions/{UnconfirmedWrites.d.ts → mutations/UnconfirmedWrites.d.ts} +0 -0
- /package/dist/transactions/{UnconfirmedWrites.js → mutations/UnconfirmedWrites.js} +0 -0
- /package/dist/transactions/{optimisticApply.js → mutations/optimisticApply.js} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,674 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.35.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 6be1d99: The `@abloatai/ablo/agent` entry point is removed, along with the single name it exported, `Agent`.
|
|
8
|
+
|
|
9
|
+
There is no separate agent SDK, and in practice there has not been one for some time: an agent is
|
|
10
|
+
the same client with a different transport. `Ablo({ schema, apiKey, transport: 'http' })` returns a
|
|
11
|
+
stateless, request/response client whose credential is its identity, carrying the same typed
|
|
12
|
+
`ablo.<model>` surface a browser or a server action uses, minus the parts that need a socket — the
|
|
13
|
+
`local` reads and `onChange`, which are absent from the type rather than failing at runtime. A
|
|
14
|
+
second entry point offered a second way in and, mostly, a second thing to keep in step with the
|
|
15
|
+
first. If you imported from `@abloatai/ablo/agent`, import from `@abloatai/ablo` and select the
|
|
16
|
+
transport.
|
|
17
|
+
|
|
18
|
+
`ClaimEvent.participantKind` is also removed. A claim event is one transition in a lease's
|
|
19
|
+
lifecycle — acquired, queued, granted, lost, rejected, expired — and it identifies the actor that
|
|
20
|
+
moved it. Whether that actor was a person, an agent, or the system is a property of the holder, not
|
|
21
|
+
of the transition, and stating it on every event meant maintaining the same fact in two shapes. Read
|
|
22
|
+
it where the holder is described instead: `ablo.<model>.claim.state({ id })` returns the current
|
|
23
|
+
holder with its `participantKind`, which is what the "an agent is editing this" affordance in a UI
|
|
24
|
+
has always read.
|
|
25
|
+
|
|
26
|
+
- 1b12959: The client is now the coordination layer, and the things built on top of it are
|
|
27
|
+
capabilities you install rather than parts of the client you get whether you use
|
|
28
|
+
them or not.
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { Ablo, humans } from '@abloatai/ablo';
|
|
32
|
+
|
|
33
|
+
const ablo = Ablo({ schema, apiKey, plugins: [humans()] });
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`humans()` declares the local, watchable copy: the offline store, live queries,
|
|
37
|
+
presence, and the framework bindings — everything that exists because a person
|
|
38
|
+
is looking at the screen. What remains underneath is what coordination actually
|
|
39
|
+
needs: commit, read, observe, claim. Omit `plugins` and `humans()` is installed
|
|
40
|
+
for you, so an application that renders anything today keeps working with no
|
|
41
|
+
change; naming it in the list only makes visible what was already true.
|
|
42
|
+
|
|
43
|
+
There is no `agents()` counterpart, and the absence is the point. An agent is
|
|
44
|
+
not a special caller that needs its own mode — it is the ordinary one, and what
|
|
45
|
+
it needs is the client without the materialiser. That is the same observation
|
|
46
|
+
behind removing the `/agent` entry point in this release: there was never a
|
|
47
|
+
second SDK to enter, only a client with a transport that does not open a socket.
|
|
48
|
+
`Ablo({ schema, apiKey, transport: 'http' })` is the whole of it, and because
|
|
49
|
+
`humans()` requires a duplex connection, asking for it on that client fails while
|
|
50
|
+
the client is being constructed, with an error naming the plugin, rather than
|
|
51
|
+
resolving to a presence stream that can never receive a frame. A plugin listed
|
|
52
|
+
twice fails the same way.
|
|
53
|
+
|
|
54
|
+
The rearrangement is also why the reactive `ablo.<model>` surface composes from
|
|
55
|
+
the transport-independent one rather than restating it — the note on the renamed
|
|
56
|
+
local reads describes what that buys — and why `track`, a durable premise that
|
|
57
|
+
keeps no local copy, sits in the core where an agent can reach it without
|
|
58
|
+
installing a materialiser it has no use for.
|
|
59
|
+
|
|
60
|
+
This is a seam, not a finished migration. `humans()` today constructs the
|
|
61
|
+
presence stream and declares the constraint that keeps it off a stateless
|
|
62
|
+
client; the rest of the materialiser — the store, the bootstrap, the delta
|
|
63
|
+
pipeline, the framework bindings — still lives in the composition root and moves
|
|
64
|
+
behind the same door as the plugin context grows to carry what each piece needs.
|
|
65
|
+
The contract those pieces will arrive through is the one published here, so a
|
|
66
|
+
capability written against it now keeps working as they do.
|
|
67
|
+
|
|
68
|
+
- 6be1d99: The shape of a claim's `meta` is now declared once, on `Register`, beside your
|
|
69
|
+
schema:
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
declare module '@abloatai/ablo' {
|
|
73
|
+
interface Register {
|
|
74
|
+
Schema: typeof schema;
|
|
75
|
+
ClaimMeta: { blocks: string[] };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
From then on `claim.state({ id })?.target.meta`, every entry in `claim.queue`,
|
|
81
|
+
and the claim you hold under `await using` all read that shape, and the `typeof`
|
|
82
|
+
guard each of those reads used to need is gone. The same declaration types what
|
|
83
|
+
you write — `claim({ id, meta: { blocks } })` — so the writer and every reader
|
|
84
|
+
agree by construction rather than by convention, which is the half a per-call
|
|
85
|
+
type argument could never reach: naming the shape at the read site said nothing
|
|
86
|
+
about what the write site was allowed to put there.
|
|
87
|
+
|
|
88
|
+
Prefer the registration to the per-call `claim.state<MyMeta>({ id })`, because
|
|
89
|
+
one declaration is one place to change when the metadata grows a field. The
|
|
90
|
+
type argument remains supported and is still the honest answer for a program
|
|
91
|
+
carrying more than one metadata shape; only its default moved, from a bare
|
|
92
|
+
record to whatever you registered. A program that registers nothing sees no
|
|
93
|
+
change at all — `target.meta` stays the loose record it has always been.
|
|
94
|
+
|
|
95
|
+
What travels between participants is untouched. Claim metadata is carried
|
|
96
|
+
verbatim and never interpreted, and the protocol keeps accepting any object it
|
|
97
|
+
is given, so a peer on a newer build can send a field this one has never heard
|
|
98
|
+
of and it still arrives intact. The declaration is a promise about what your own
|
|
99
|
+
program writes, not a filter on what it receives.
|
|
100
|
+
|
|
101
|
+
- e4b538b: A model's conflict stance can be written as a chain through one name.
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
import { coordination, model, z } from '@abloatai/ablo/schema';
|
|
105
|
+
|
|
106
|
+
export const cards = model(
|
|
107
|
+
{
|
|
108
|
+
title: z.string(),
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
conflict: coordination.humansOverwrite().agentsReject(),
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The nine disposition rules were the only loose functions left in the schema DSL.
|
|
117
|
+
Everything else there arrives under a noun that owns it — `relation.belongsTo`,
|
|
118
|
+
`field.string`, `mutable.lazy` — but declaring that humans overwrite and agents
|
|
119
|
+
yield meant importing three separate names to say one thing, and a schema with a
|
|
120
|
+
couple of stances spent more of its import list on conflict rules than on the rest
|
|
121
|
+
of the language combined.
|
|
122
|
+
|
|
123
|
+
So the rules now hang off `coordination`, and each one returns an axis that is
|
|
124
|
+
still open to the next, the way a field chains its own modifiers. The rules
|
|
125
|
+
themselves are unchanged, and each remains the single place its disposition map is
|
|
126
|
+
written; the chain delegates to the standalone function rather than restating it.
|
|
127
|
+
Those standalone functions stay exported, and `coordination()` still merges them,
|
|
128
|
+
which is the form to reach for when the rules are assembled at runtime rather than
|
|
129
|
+
written out:
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
const stance = coordination(humansOverwrite(), agentsReject());
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Both forms produce the same plain, serializable map keyed by the committer's
|
|
136
|
+
participant kind — `{ user: 'overwrite', agent: 'reject' }` — and the engine reads
|
|
137
|
+
it at the commit chokepoint exactly as before. A kind left unnamed still falls
|
|
138
|
+
through to the engine default, and a kind named twice still takes the later rule.
|
|
139
|
+
|
|
140
|
+
- 6be1d99: `captureTransactionFailure` on the observability provider is renamed to `captureMutationFailure`.
|
|
141
|
+
If you pass an `observability` provider to `Ablo(...)`, rename that one method; its argument is
|
|
142
|
+
unchanged.
|
|
143
|
+
|
|
144
|
+
The rename frees a word that had been doing two jobs. A transaction, in this system, is a unit of
|
|
145
|
+
write that settles — the thing you author, hand to the engine, and get a receipt for. But the same
|
|
146
|
+
word also named the client's optimistic queue, the buffer of writes that have been applied locally
|
|
147
|
+
and are still waiting for the server to agree. The callback belonged to the second meaning: it
|
|
148
|
+
fires when a queued write has exhausted its attempts and will never land, so the local change is
|
|
149
|
+
rolled back and something upstream needs to hear about it. Calling that a transaction failure
|
|
150
|
+
invited the reading that a settlement had failed, which is a different event with a different
|
|
151
|
+
remedy — and as the settlement layer grew a vocabulary of its own, the collision stopped being
|
|
152
|
+
cosmetic.
|
|
153
|
+
|
|
154
|
+
There is no compatibility alias, and the reason is worth stating, because it is the opposite of
|
|
155
|
+
the usual one. `captureMutationFailure` is a required member of the provider interface. A provider
|
|
156
|
+
still carrying the old spelling therefore does not satisfy that interface at all: the compiler
|
|
157
|
+
stops the build and names the missing member, at the line where the provider is constructed. An
|
|
158
|
+
optional alias would have made that build succeed while the engine went on calling a method the
|
|
159
|
+
provider does not implement — quiet in exactly the way a renamed callback must not be. The
|
|
160
|
+
compiler is the better messenger here, and this note is the rest of the message.
|
|
161
|
+
|
|
162
|
+
- 6be1d99: `debug`, `logLevel`, and `persistence` are no longer accepted on the stateless HTTP client. They
|
|
163
|
+
remain exactly where they have always worked — on the default client, over the socket.
|
|
164
|
+
|
|
165
|
+
They were reachable on `Ablo({ transport: 'http' })` only because that client's options type was
|
|
166
|
+
assembled from the whole composition bag rather than from the fields the transport actually reads,
|
|
167
|
+
and nothing on the HTTP path ever read them. `persistence` configures a local store a stateless
|
|
168
|
+
client does not keep. `debug` and `logLevel` turn on the `[Ablo]` coordination trace, which
|
|
169
|
+
narrates presence, claim grants, and queueing — frames that arrive over a socket an HTTP client
|
|
170
|
+
never opens. Passing any of the three was accepted and did nothing, which is the kind of option
|
|
171
|
+
that costs a reader an afternoon before they conclude the feature is broken.
|
|
172
|
+
|
|
173
|
+
If you passed one to an HTTP client, delete it: nothing changes, because nothing was happening.
|
|
174
|
+
If what you wanted was the trace from a server-side worker, it is not a setting you were missing —
|
|
175
|
+
a stateless agent holds no live session to narrate, and its writes are visible in the audit log and
|
|
176
|
+
in `ablo logs` instead.
|
|
177
|
+
|
|
178
|
+
- 6be1d99: A model's load strategy is now `'instant'` or `'lazy'`, and nothing else. The
|
|
179
|
+
word `'manual'` is gone, along with the `mutable.manual(...)` helper that set
|
|
180
|
+
it.
|
|
181
|
+
|
|
182
|
+
The strategy had drifted into two declarations that never agreed. The authoring
|
|
183
|
+
side, reached through `@abloatai/ablo/schema`, offered `'instant' | 'lazy' |
|
|
184
|
+
'manual'`. The runtime side, reached through `@abloatai/ablo/core` under the
|
|
185
|
+
same name, offered `instant`, `lazy`, `partial`, `explicitlyRequested`, and
|
|
186
|
+
`local`. Importing `LoadStrategy` from the two paths gave you two different
|
|
187
|
+
types, and no compiler anywhere would tell you which one you had.
|
|
188
|
+
|
|
189
|
+
Only `instant` and `lazy` were ever real. `'manual'` promised loading a model
|
|
190
|
+
"only when you explicitly call `sync.model.load()`", a method that was never
|
|
191
|
+
built; a model declared manual resolved to `lazy` and loaded on first access
|
|
192
|
+
like any other. `partial` had live branches in the client but no way to declare
|
|
193
|
+
it, and `explicitlyRequested` and `local` had neither. So the correction is
|
|
194
|
+
mostly the removal of vocabulary that described behavior the engine did not
|
|
195
|
+
have — a model that said `manual` was already behaving as `lazy`, and continues
|
|
196
|
+
to, which is why this changes what your schema is allowed to say rather than
|
|
197
|
+
what your program does.
|
|
198
|
+
|
|
199
|
+
Replace `load: 'manual'` with `load: 'lazy'`, and `mutable.manual(...)` with
|
|
200
|
+
`mutable.lazy(...)`. `readOnly.internal(...)` stays, and its guarantee is
|
|
201
|
+
unchanged and worth restating plainly: it is a write boundary, not a read one.
|
|
202
|
+
Clients cannot commit to an internal model, and the server rejects any attempt.
|
|
203
|
+
Its rows still reach a client that reads the model.
|
|
204
|
+
|
|
205
|
+
`LoadStrategy` now has one declaration behind both import paths. It reads as a
|
|
206
|
+
value where the engine branches on it — `LoadStrategy.instant` — and as the
|
|
207
|
+
plain string an author types in `model(…, { load: 'instant' })`, and the two
|
|
208
|
+
can no longer name different sets.
|
|
209
|
+
|
|
210
|
+
Two names join it on `@abloatai/ablo/schema`. `DEFAULT_LOAD_STRATEGY` is the
|
|
211
|
+
strategy a model gets when it declares none, and `loadsAtBootstrap(load)`
|
|
212
|
+
answers whether a model's rows arrive in the bootstrap payload or on first
|
|
213
|
+
access. The predicate is exported because both halves of the system have to
|
|
214
|
+
agree on it: the client builds its bootstrap subscription from the schema and
|
|
215
|
+
the server assembles the payload from the same schema, and they had been
|
|
216
|
+
asking the question in three different spellings — `load !== 'lazy'` in one
|
|
217
|
+
place, `load === 'instant'` in another. Against two members those agree. A
|
|
218
|
+
third member would have had one side enrolling it and the other withholding it,
|
|
219
|
+
which surfaces as rows that never arrive, a long way from the line responsible.
|
|
220
|
+
|
|
221
|
+
- The three synchronous reads on `ablo.<model>` are renamed. `get(id)`, `getAll(options)`,
|
|
222
|
+
and `getCount(options)` become `local.retrieve(id)`, `local.list(options)`, and
|
|
223
|
+
`local.count(options)`.
|
|
224
|
+
|
|
225
|
+
The old names were a pair that carried no information. `get` and `retrieve` are
|
|
226
|
+
synonyms in English, so a reader who found `ablo.tasks.get(id)` beside
|
|
227
|
+
`ablo.tasks.retrieve({ id })` had no way to tell from the words which one waited
|
|
228
|
+
on the network and which one did not — the difference had to be memorised, and
|
|
229
|
+
it was memorised imperfectly. Two bugs reached review that way, both of them a
|
|
230
|
+
synchronous read standing in for an asynchronous one against a graph that had
|
|
231
|
+
not warmed up yet, both of them invisible in the diff because the line read
|
|
232
|
+
exactly like the call it should have been.
|
|
233
|
+
|
|
234
|
+
The new names say the thing out loud. Every verb is the verb it already was on
|
|
235
|
+
the asynchronous side; the only addition anywhere in the design is the word
|
|
236
|
+
`local`, and it is a narrowing rather than a claim about its sibling.
|
|
237
|
+
`retrieve` consults the local graph first and falls back to the network;
|
|
238
|
+
`local.retrieve` is restricted to what is already resident, which is also why it
|
|
239
|
+
can hand back a value instead of a promise. There is nothing to await. Reading
|
|
240
|
+
`ablo.tasks.local.list({ where })`, you know both what it does and what it costs
|
|
241
|
+
without leaving the line.
|
|
242
|
+
|
|
243
|
+
The reads themselves are unchanged — same options, same lifecycle `state`
|
|
244
|
+
filter, same reactivity inside `useAblo` selectors and in render. Only the path
|
|
245
|
+
to them moved, and it is the only path: the local reads are not also exposed at
|
|
246
|
+
the top level, because a namespace that duplicates what sits beside it stops
|
|
247
|
+
disambiguating anything.
|
|
248
|
+
|
|
249
|
+
Underneath, the rename arrives with a structural change that will keep the
|
|
250
|
+
surface honest from here on. The reactive `ablo.<model>` type was hand-written as
|
|
251
|
+
a second list that happened to agree with the transport-independent one, and
|
|
252
|
+
nothing linked them: a verb added to one said nothing about the other. It is now
|
|
253
|
+
composed from that base rather than restated, so a verb added there arrives on
|
|
254
|
+
the reactive client on its own. `track` — registering a durable premise on a
|
|
255
|
+
row — is the first to arrive that way.
|
|
256
|
+
|
|
257
|
+
Migrating is mechanical, but reach for the compiler rather than a search. `getAll`
|
|
258
|
+
and `getCount` are distinctive enough to find by name; `get` is not, and in a
|
|
259
|
+
codebase of any size it hides among the `Map.get` and `headers.get` calls that a
|
|
260
|
+
search cannot tell it from. Removing the old names from the type turns the
|
|
261
|
+
question into a list of type errors, each one a site that genuinely needs to
|
|
262
|
+
move.
|
|
263
|
+
|
|
264
|
+
- 0002877: A capability is declared in the vocabulary you write, and every other spelling
|
|
265
|
+
of it derives from that declaration.
|
|
266
|
+
|
|
267
|
+
```ts
|
|
268
|
+
import { grantedOperations, modelWireNames } from '@abloatai/ablo/auth';
|
|
269
|
+
|
|
270
|
+
const operations = grantedOperations(
|
|
271
|
+
{ documents: ['read', 'update'] },
|
|
272
|
+
modelWireNames(schema.models)
|
|
273
|
+
);
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
The same grant used to be spelled five times: a literal union in the resource
|
|
277
|
+
types, a `z.array(z.string())` on the wire, a field-by-field parser in the mint
|
|
278
|
+
route, an object literal in the response type, and a hand-written `model.verb`
|
|
279
|
+
array at every caller that mints without the SDK. Nothing failed when those
|
|
280
|
+
drifted apart. The drift surfaced later as `capability_scope_denied` on a grant
|
|
281
|
+
the caller was certain it held, at a call site with no view of the spelling that
|
|
282
|
+
had gone stale.
|
|
283
|
+
|
|
284
|
+
They are now one module on `@abloatai/ablo/auth`. `capabilityOperationSchema`
|
|
285
|
+
holds the verb vocabulary; `grantedOperationSchema` derives the wire pattern
|
|
286
|
+
`<model>.<verb>` from it as a template literal, so tightening the verbs cannot
|
|
287
|
+
leave a stale regex or a stale published contract behind. `CapabilityCan<S>`
|
|
288
|
+
narrows the declaration to one schema's models, so `can: { tasks: ['update'] }`
|
|
289
|
+
fails to compile against a schema with no `tasks` model.
|
|
290
|
+
|
|
291
|
+
`modelWireNames` is the derivation worth knowing about, because it is the one
|
|
292
|
+
people got wrong by hand. A model whose type name is overridden — schema key
|
|
293
|
+
`documents`, type name `Document` — must be granted as `document.update`, and a
|
|
294
|
+
caller who works that out by eye learns otherwise at enforcement time. Pass your
|
|
295
|
+
schema's models and the map is built for you. It is a required argument rather
|
|
296
|
+
than an optional one: an omitted map would mint the schema key verbatim, which
|
|
297
|
+
is right for most models and wrong for exactly the ones that need it.
|
|
298
|
+
|
|
299
|
+
Two more derivations ride along. `expandReadYourWrites` appends `<model>.read`
|
|
300
|
+
for every model the grant can write, because a scoped agent that may update a
|
|
301
|
+
row has to be able to read it or the read gate starves the writes the grant
|
|
302
|
+
allows — write verbs stay the source of truth, reads are derived and deduped,
|
|
303
|
+
and a model the grant cannot write stays unreadable. And a grant is now checked
|
|
304
|
+
against the schema **at the mint**: `capabilityModelAliases` accepts any of the
|
|
305
|
+
three names one model answers to (type name, schema key, table name), and
|
|
306
|
+
`unresolvableOperations` reports the ones that name nothing, so a typo is a
|
|
307
|
+
rejected mint rather than a credential that looks healthy until its first write.
|
|
308
|
+
|
|
309
|
+
### Breaking
|
|
310
|
+
|
|
311
|
+
`ExchangeApiKeyRequest.operations` narrows from `readonly string[]` to
|
|
312
|
+
`readonly GrantedOperation[]`. If you assembled that array by hand, the compiler
|
|
313
|
+
will now reject any entry that is not `<model>.<verb>` — including the `'*'`
|
|
314
|
+
wildcard, which was never a member of the wire form and is no longer typable as
|
|
315
|
+
one. Build the array with `grantedOperations(can, modelWireNames(schema.models))`
|
|
316
|
+
instead of composing the strings; that is the same call the SDK makes, and it
|
|
317
|
+
resolves the type-name override you would otherwise have to remember.
|
|
318
|
+
|
|
319
|
+
- feb88a2: Three error codes carried the word "intent", left over from the vocabulary the
|
|
320
|
+
coordination layer used before it settled on claims. The word had come to mean
|
|
321
|
+
two unrelated things in the same registry, so both are now named for what they
|
|
322
|
+
are.
|
|
323
|
+
|
|
324
|
+
`invalid_intent` is removed. It was raised when a claim request failed to name
|
|
325
|
+
the model and row it targeted — which is what `malformed_claim` already
|
|
326
|
+
described, in the same words, for the same failure arriving over the WebSocket
|
|
327
|
+
instead of over HTTP. The two have been merged into `malformed_claim`; a caller
|
|
328
|
+
switching on `invalid_intent` should switch on `malformed_claim` instead, and
|
|
329
|
+
the HTTP status is 400 either way.
|
|
330
|
+
|
|
331
|
+
`slide_intent_missing_deck_id` and `slide_intent_unknown_sibling` are renamed to
|
|
332
|
+
`slide_position_missing_deck_id` and `slide_position_unknown_sibling`. These were
|
|
333
|
+
never about claims at all: they report that a slide created at a position stated
|
|
334
|
+
relative to its neighbours — "before this one" — could not have that position
|
|
335
|
+
resolved, because no deck was named or because the neighbour is not in it. An
|
|
336
|
+
earlier rename had swept the word "claim" through their descriptions and left
|
|
337
|
+
them reading as contention errors, so their messages are rewritten to say what
|
|
338
|
+
actually went wrong.
|
|
339
|
+
|
|
340
|
+
`malformed_claim` moves from the `claim` category to `validation`, alongside
|
|
341
|
+
`malformed_subscription`. The code, its 400 status, and its meaning are
|
|
342
|
+
unchanged — only the grouping it appears under in the error documentation. The
|
|
343
|
+
`claim` category now holds only genuine contention, which is what its name
|
|
344
|
+
promised.
|
|
345
|
+
|
|
346
|
+
- e4b538b: `model()` now takes a shape and one options object. Relations, which used to hold a
|
|
347
|
+
positional argument of their own in between, are the `relations` key on those options.
|
|
348
|
+
|
|
349
|
+
```ts
|
|
350
|
+
documents: model(
|
|
351
|
+
{
|
|
352
|
+
title: z.string(),
|
|
353
|
+
projectId: z.string(),
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
relations: { project: relation.belongsTo('projects', 'projectId') },
|
|
357
|
+
conflict: coordination(humansOverwrite(), agentsReject()),
|
|
358
|
+
load: 'lazy',
|
|
359
|
+
}
|
|
360
|
+
),
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
The middle argument was the problem. Most models have no relations at all, yet nearly
|
|
364
|
+
every model has something to say about itself — a table it maps to, a load strategy, a
|
|
365
|
+
conflict disposition — and the only way to reach that third argument was to write the
|
|
366
|
+
second one first. So the schema filled up with empty braces standing in for edges that
|
|
367
|
+
did not exist. In this repository alone the placeholder appeared 104 times, which made
|
|
368
|
+
`{}` the single most common thing anyone ever passed in that position: more often than
|
|
369
|
+
any real relations object. A reader opening a schema met the empty braces before they
|
|
370
|
+
met the model.
|
|
371
|
+
|
|
372
|
+
Nothing about relations themselves changes. They are built with the same
|
|
373
|
+
`relation.belongsTo` and `relation.hasMany` factories, they carry the same options, and
|
|
374
|
+
the engine reads them exactly as before — to index foreign keys, to order inserts so a
|
|
375
|
+
parent lands before its children, and to generate the accessors behind `task.project`.
|
|
376
|
+
They have simply stopped being a position and become a name, which is what they always
|
|
377
|
+
were to everything downstream.
|
|
378
|
+
|
|
379
|
+
Migrating is mechanical, and the compiler finds every site. A call that passed an empty
|
|
380
|
+
placeholder drops it:
|
|
381
|
+
|
|
382
|
+
```ts
|
|
383
|
+
-model({ title: z.string() }, {}, { groups: { root: 'workspace' } }) +
|
|
384
|
+
model({ title: z.string() }, { groups: { root: 'workspace' } });
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
A call that passed real relations folds them in under their own key:
|
|
388
|
+
|
|
389
|
+
```ts
|
|
390
|
+
- model({ title: z.string(), projectId: z.string() }, {
|
|
391
|
+
- project: relation.belongsTo('projects', 'projectId'),
|
|
392
|
+
- }, { load: 'lazy' })
|
|
393
|
+
+ model({ title: z.string(), projectId: z.string() }, {
|
|
394
|
+
+ relations: { project: relation.belongsTo('projects', 'projectId') },
|
|
395
|
+
+ load: 'lazy',
|
|
396
|
+
+ })
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
A call that passed only fields is untouched.
|
|
400
|
+
|
|
401
|
+
- e4b538b: `app.current_user_id` is now a reserved session setting, so a schema can no
|
|
402
|
+
longer claim it — and a read that cannot be scoped to the person asking says so
|
|
403
|
+
with `user_scope_not_enforced` instead of quietly returning rows.
|
|
404
|
+
|
|
405
|
+
Ablo's direct-write connection already applied a fixed bundle of settings before
|
|
406
|
+
your DML — the organization, the project, the environment, the sandbox, the
|
|
407
|
+
acting participant — and `sessionSettings` let a schema forward any of those
|
|
408
|
+
into a differently-named setting its own policies read. What a schema could
|
|
409
|
+
never do was reassign one of the engine's own settings, because that would let a
|
|
410
|
+
schema push relax the scoping under which Ablo writes. `RESERVED_SESSION_SETTINGS`
|
|
411
|
+
is the list that prevents it, and the engine now sets one more: the acting user.
|
|
412
|
+
|
|
413
|
+
The setting carries a person's id when a person is behind the write, and `'*'`
|
|
414
|
+
when the request comes from a backend credential acting as the organization
|
|
415
|
+
itself. Absent identity writes the empty string rather than leaving the previous
|
|
416
|
+
value on a pooled connection, so a policy reading it denies rather than
|
|
417
|
+
inheriting. Your own policies are welcome to read it; they simply cannot be the
|
|
418
|
+
thing that decides what it contains.
|
|
419
|
+
|
|
420
|
+
If your schema mapped `app.current_user_id` through `sessionSettings`, that entry
|
|
421
|
+
is now dropped at authoring time and at runtime, and the engine's own value
|
|
422
|
+
applies instead. Map a different name if your policies need a value you control.
|
|
423
|
+
|
|
424
|
+
The new error code covers the one arrangement where that boundary cannot hold. A
|
|
425
|
+
plane served from its retained log carries the organization and the project on
|
|
426
|
+
every row but not the owner, so a rule that names a person has nothing to act on
|
|
427
|
+
there. Rather than fold the rows and return a plausible answer, such a read is
|
|
428
|
+
declined whole — a member reading a colleague's private records would otherwise
|
|
429
|
+
be indistinguishable from a member reading their own. Reads made by a credential
|
|
430
|
+
acting for the organization are unaffected, as is every plane served from its
|
|
431
|
+
tables.
|
|
432
|
+
|
|
433
|
+
All of this now has a page. The seam has been in the SDK since 0.32.0 and under
|
|
434
|
+
this name since 0.33.0, documented both times only in a release note — which is
|
|
435
|
+
a poor place to keep a feature rather than announce one:
|
|
436
|
+
a reader with row-level-security policies had no way to discover that Ablo sets
|
|
437
|
+
an identity context at all, let alone that they could point their own policies
|
|
438
|
+
at it. **Session Settings** states what the engine sets before every write, which
|
|
439
|
+
identities a mapping may name, which settings are reserved and why, and what
|
|
440
|
+
happens on a plane served from its log. Read it with `npx ablo docs
|
|
441
|
+
session-settings`, or on the site.
|
|
442
|
+
|
|
443
|
+
- 5972a69: `causedByTaskId` is removed from `MutationOptions` — and so from the per-verb params that
|
|
444
|
+
compose it, `ModelCreateParams`, `ModelUpdateParams`, and `ModelDeleteParams` — together with
|
|
445
|
+
the seven `turn_*` error codes that existed to police it. It also leaves `CommitContext`, where
|
|
446
|
+
the server carried it from the wire to the stored row. The field was never usable for its stated
|
|
447
|
+
purpose: setting it was the one reliable way to have a write refused.
|
|
448
|
+
|
|
449
|
+
It was the surviving half of an agent-turn protocol whose other half was never built. The idea
|
|
450
|
+
had been that an agent would open a turn, write under it, and close it, so that an auditor could
|
|
451
|
+
later ask what an agent did _because_ a person asked it to — a prompt at the root of a tree of
|
|
452
|
+
changes. What actually shipped was only the check. Every commit that carried a `causedByTaskId`
|
|
453
|
+
was validated against a task record, and nothing in the system has ever opened one, so the record
|
|
454
|
+
was never there. A caller who supplied the field had the entire batch rejected with
|
|
455
|
+
`turn_validation_failed`; a caller who left it null passed straight through. The prudent thing to
|
|
456
|
+
do with a documented, public option was to never touch it, and the field spent its life as
|
|
457
|
+
plumbing threaded through the wire protocol, the settlement envelope, the stored row, and the
|
|
458
|
+
replication consumer, carrying null on every path.
|
|
459
|
+
|
|
460
|
+
Nothing is lost by its removal, because write attribution never depended on it. A delta already
|
|
461
|
+
records who made the change and on whose authority — the actor, the `onBehalfOf` principal behind
|
|
462
|
+
a delegated write, the capability that authorized it, and the claim the write was made under. Those
|
|
463
|
+
answer "who did this and by what right" without asking the caller to maintain a lifecycle the
|
|
464
|
+
engine never opened on their behalf. If your code passed `causedByTaskId`, delete the argument;
|
|
465
|
+
if it branched on `turn_validation_failed`, that branch was unreachable and can go with it.
|
|
466
|
+
|
|
467
|
+
The removed codes are `turn_validation_failed`, `turn_open_failed`, `turn_close_failed`,
|
|
468
|
+
`turn_not_found`, `turn_foreign_agent`, `parent_turn_not_found`, and `parent_turn_foreign_agent`.
|
|
469
|
+
`ERROR_CONTRACT_VERSION` moves to `2026-07-19`, as it does whenever a code leaves the registry.
|
|
470
|
+
The stored-row schema loses its provenance slice with them: `deltaProvenanceSchema` and the
|
|
471
|
+
`DeltaProvenance` type are gone, and `syncDeltaRowSchema` is now the core and attribution slices
|
|
472
|
+
composed, which is what it had always described in practice. On the wire the field was optional
|
|
473
|
+
and nullable, so a client that still sends it is accepted and ignored, and a client reading deltas
|
|
474
|
+
sees one fewer always-null key.
|
|
475
|
+
|
|
476
|
+
The storage goes with it. The `caused_by_task_id` column leaves `sync_deltas`, `agent_actions_log`,
|
|
477
|
+
and `source_write_intents`, and the two tables that were never written — `agent_tasks` and
|
|
478
|
+
`agent_task_prompts` — are dropped along with the `agent_surface` enum that typed them. This looked
|
|
479
|
+
at first like a separate versioned migration, because the audit chain hashes the lineage id into
|
|
480
|
+
every row's signature and rewriting those signatures is not something a release should do quietly.
|
|
481
|
+
It turned out not to need rewriting. Every row was hashed with a null in that position, so the
|
|
482
|
+
canonical byte layout the stored hashes were computed against is preserved exactly by passing
|
|
483
|
+
`NULL::text` where the column used to be read — which is what the migration does, in the append
|
|
484
|
+
trigger and in the rechain function, before the column is dropped. The signature is untouched and
|
|
485
|
+
every historical `row_hash` still verifies. The `packages/audit-chain` reproduction freezes the same
|
|
486
|
+
position, so the two implementations cannot drift apart on it.
|
|
487
|
+
|
|
488
|
+
- f0f4226: Eight names that were renamed in an earlier release, and have shipped since as
|
|
489
|
+
aliases beside the names that replaced them, are removed.
|
|
490
|
+
|
|
491
|
+
| Removed | Use |
|
|
492
|
+
| ----------------------- | ------------------------ |
|
|
493
|
+
| `CommitOutboxRecord` | `PendingWrite` |
|
|
494
|
+
| `CommitOutboxStore` | `DurableWriteStore` |
|
|
495
|
+
| `MeshParticipantStatus` | `ParticipantStatus` |
|
|
496
|
+
| `planeSchema` | `residencySchema` |
|
|
497
|
+
| `SchemaPlane` | `ModelResidency` |
|
|
498
|
+
| `DEFAULT_PLANE` | `DEFAULT_RESIDENCY` |
|
|
499
|
+
| `DELTA_RESIDENCY` | `DELTA_PHYSICAL_STORAGE` |
|
|
500
|
+
| `InferModel` | `Model` |
|
|
501
|
+
|
|
502
|
+
Each is a one-for-one substitution — the alias and its replacement were the same
|
|
503
|
+
type or the same value, so nothing about your program's behavior changes with
|
|
504
|
+
the name. `InferModel` is the one worth a sentence: `Model<typeof schema,
|
|
505
|
+
'tasks'>` reads as the domain rather than the machinery, which is why it became
|
|
506
|
+
the published spelling, and the old name is gone from `@abloatai/ablo/schema`
|
|
507
|
+
and from the `Ablo.Schema` namespace both. The type it named still exists
|
|
508
|
+
underneath, because `Model` is defined in terms of it rather than as a second
|
|
509
|
+
name for the same idea.
|
|
510
|
+
|
|
511
|
+
An alias earns one release of overlap. That is what the surface snapshot in
|
|
512
|
+
this repository records and enforces: a published name may leave only from a
|
|
513
|
+
release that shipped it marked deprecated, which is the release in which a
|
|
514
|
+
reader could have found out. These eight were marked in 0.34.1, so this is the
|
|
515
|
+
release that may drop them, and dropping them is what keeps the rule a rule
|
|
516
|
+
rather than a preamble on a list that only grows.
|
|
517
|
+
|
|
518
|
+
`DELTA_RESIDENCY` is the one whose replacement is not merely a rename.
|
|
519
|
+
Residency and classification are separate axes — where a column physically
|
|
520
|
+
lives, and how sensitive its contents are — and one constant named as though
|
|
521
|
+
they were the same question. `DELTA_PHYSICAL_STORAGE` answers the first;
|
|
522
|
+
`DELTA_DATA_CLASSIFICATION`, which has been exported alongside it, answers the
|
|
523
|
+
second. If you were reading `DELTA_RESIDENCY` to decide handling rather than
|
|
524
|
+
placement, the classification constant is the one you wanted.
|
|
525
|
+
|
|
526
|
+
- 320b8d7: `ablo docs` reads the documentation for the version you installed, and the
|
|
527
|
+
public docs routes now serve the whole corpus instead of two thirds of it.
|
|
528
|
+
|
|
529
|
+
A documentation URL always describes the newest release. A package in
|
|
530
|
+
`node_modules` is frozen at whatever version was published. Those two facts
|
|
531
|
+
drift apart the moment a project pins a dependency, and the drift is invisible
|
|
532
|
+
from both ends: the docs are correct, the code is correct, and the agent reading
|
|
533
|
+
one to write the other produces a call that does not exist. `get`, `getAll`, and
|
|
534
|
+
`getCount` became `retrieve` and `list` in 0.35.0, so an assistant working in a
|
|
535
|
+
project still on 0.34 reads the current page, writes `retrieve`, and watches it
|
|
536
|
+
fail against the package sitting beside it.
|
|
537
|
+
|
|
538
|
+
The documentation already travelled in the npm tarball; nothing read it there.
|
|
539
|
+
`ablo docs` lists every page with what it covers, and `ablo docs <page>` prints
|
|
540
|
+
one as markdown — from the files shipped alongside the code they describe, so
|
|
541
|
+
they cannot disagree with it, and with no network at all, which is the condition
|
|
542
|
+
most agent work now runs under. `ablo docs --json` gives the same list to a
|
|
543
|
+
program. A new `@abloatai/ablo/docs` entry point exposes the catalog for
|
|
544
|
+
anything that wants to build its own reader.
|
|
545
|
+
|
|
546
|
+
The same catalog now answers `/api/docs/*`. That surface had been maintained by
|
|
547
|
+
hand and had fallen twenty-one pages behind: `coordination`, the page explaining
|
|
548
|
+
how claims work, returned a 404 while the site published it, and so did
|
|
549
|
+
`sessions`, `webhooks`, `migration`, `deployment`, and every example but two.
|
|
550
|
+
Nothing failed when the list went stale, which is why it stayed stale. Pages are
|
|
551
|
+
now discovered rather than listed, so one is reachable the moment it is written,
|
|
552
|
+
and only what ships in the package is reachable at all.
|
|
553
|
+
|
|
554
|
+
One address changed meaning. `/api/docs/agents` returns the Agents guide, the
|
|
555
|
+
page the docs index has always linked under that name; the install playbook that
|
|
556
|
+
briefly answered there is at `/api/docs/AGENTS.md`, where it also answered
|
|
557
|
+
before.
|
|
558
|
+
|
|
559
|
+
- 0002877: `GET /v1/logs` answers two questions now — what changed, and who is working on
|
|
560
|
+
what — through one envelope and one cursor.
|
|
561
|
+
|
|
562
|
+
```ts
|
|
563
|
+
import { feedEventSchema, parseFeedCursor } from '@abloatai/ablo/wire';
|
|
564
|
+
|
|
565
|
+
const page = await fetch(`${baseUrl}/v1/logs?after=${cursor}`, {
|
|
566
|
+
headers: { authorization: `Bearer ${apiKey}` },
|
|
567
|
+
}).then((r) => r.json());
|
|
568
|
+
|
|
569
|
+
for (const event of page.data) {
|
|
570
|
+
const entry = feedEventSchema.parse(event);
|
|
571
|
+
if (entry.object === 'log_event') applyChange(entry);
|
|
572
|
+
}
|
|
573
|
+
cursor = page.next_cursor;
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
An entry is discriminated on `object`, so a reader that knows only the arm it
|
|
577
|
+
came for parses the whole page and skips the rest — which is what lets a second
|
|
578
|
+
arm be added to a feed that callers are already following. The two arms stay
|
|
579
|
+
separately sequenced on purpose. A claim is a lease rather than a settled fact,
|
|
580
|
+
and allocating its positions from the delta sequence would put ephemeral leases
|
|
581
|
+
into the log that clients materialise rows from and that WAL-echo promotion and
|
|
582
|
+
compaction operate over, where a burst of claim churn would be indistinguishable
|
|
583
|
+
from committed change. They share a reading, not a sequence.
|
|
584
|
+
|
|
585
|
+
That is why the cursor carries a position for each, encoded `"<log>.<claims>"`.
|
|
586
|
+
A bare `"<log>"` still parses and resumes at claim position zero, which is the
|
|
587
|
+
right answer for every cursor issued before this existed: a caller who never
|
|
588
|
+
asked for claim events has no claim position to preserve. `parseFeedCursor`,
|
|
589
|
+
`formatFeedCursor`, and `feedCursorAdvanced` are exported from
|
|
590
|
+
`@abloatai/ablo/wire` alongside `feedEventSchema`, `logListResponseSchema`, and
|
|
591
|
+
`claimEventSchema`, so a program that builds its own reader states the grammar
|
|
592
|
+
once. `claimRecordSchema` and `heldClaimStatusSchema` join them on
|
|
593
|
+
`@abloatai/ablo/coordination` — the one claim record, and the peer-visible
|
|
594
|
+
projection of it.
|
|
595
|
+
|
|
596
|
+
`GET /v1/claims` gains the filters the audit log already had: `actorId`,
|
|
597
|
+
`actorKind`, `onBehalfOfId`, and `capabilityId`, beside the existing `model` and
|
|
598
|
+
`id`. The names are audit's names deliberately. Asking what an agent is doing
|
|
599
|
+
and asking what it did should not require two vocabularies for one idea, and now
|
|
600
|
+
the tense is the only thing that differs.
|
|
601
|
+
|
|
602
|
+
The claim arm has no producer yet. Claim transitions are broadcast and recorded
|
|
603
|
+
nowhere, so there is no sequence to read a position from, and the route emits
|
|
604
|
+
only `log_event`s today — with no request parameter for selecting arms, because
|
|
605
|
+
a knob that cannot be honoured is how a contract comes to describe a server that
|
|
606
|
+
does not exist. What ships here is the definition that producer will fill: the
|
|
607
|
+
union parses today's pages unchanged, and the cursor already carries the second
|
|
608
|
+
position, so a follower written now keeps working when the arm arrives.
|
|
609
|
+
|
|
610
|
+
### The cursor fix
|
|
611
|
+
|
|
612
|
+
A malformed `after` is now an error rather than a position. The previous reader
|
|
613
|
+
was `parseInt(raw, 10)` with a `NaN` falling back to zero, so a truncated or
|
|
614
|
+
garbled cursor resumed from the beginning of the log and replayed it in full —
|
|
615
|
+
while looking exactly like a working follow, with the damage scaling to how long
|
|
616
|
+
the log had been running. Such a request is now declined with `invalid_request`,
|
|
617
|
+
naming the `after` parameter and how to recover. `ablo logs` carried the mirror
|
|
618
|
+
of the same bug: it read the cursor with `Number(...)`, which is harmless while a
|
|
619
|
+
cursor is a bare delta id and silently wrong the moment it has two parts —
|
|
620
|
+
`Number('42.10')` is `42.1`, which re-serialises as `'42.1'` and resumes eight
|
|
621
|
+
claim positions late. The cursor is now a string end to end, passed back exactly
|
|
622
|
+
as it was issued.
|
|
623
|
+
|
|
624
|
+
- 6be1d99: `legacyCompatibleCommitReceiptSchema` is removed from `@abloatai/ablo/wire`. The WebSocket client
|
|
625
|
+
now parses a commit acknowledgement with `commitReceiptSchema`, the canonical one, directly.
|
|
626
|
+
|
|
627
|
+
It was a compatibility decoder rather than a contract: a `z.preprocess` step that supplied fields
|
|
628
|
+
older servers omitted and coerced their string `lastSyncId` into a number before handing the result
|
|
629
|
+
to the canonical schema, which did the real work. It was careful in one respect worth recording —
|
|
630
|
+
it never invented a source correlation, so an old or malformed queued receipt still failed closed
|
|
631
|
+
rather than being promoted to something the server had not said.
|
|
632
|
+
|
|
633
|
+
The servers it decoded for predate the commit contract, and the hosted engine has not spoken that
|
|
634
|
+
shape in some time. Keeping the wrapper meant every receipt on the socket path went through a
|
|
635
|
+
normalizer for a case that no longer arrives, and it left two spellings of "a receipt" on a
|
|
636
|
+
published surface whose entire purpose is to have one.
|
|
637
|
+
|
|
638
|
+
This matters only if you run a self-hosted engine older than the commit contract: its
|
|
639
|
+
acknowledgements will now fail to parse, and the write is reported as `commit_no_result` — pending
|
|
640
|
+
and safe to retry — rather than being silently normalized. Upgrade the engine. If you imported the
|
|
641
|
+
schema to parse receipts yourself, `commitReceiptSchema` is the replacement and always was the one
|
|
642
|
+
doing the parsing.
|
|
643
|
+
|
|
644
|
+
### Patch Changes
|
|
645
|
+
|
|
646
|
+
- 320b8d7: `ablo login`, `ablo mode`, and `ablo status` now say what the key in your hand
|
|
647
|
+
can do, and `status` labels the plane your credential reaches as `acts on`
|
|
648
|
+
rather than `env`.
|
|
649
|
+
|
|
650
|
+
Logging in provisions a pair — a secret sandbox key and a restricted production
|
|
651
|
+
one — so that a stolen CLI config cannot deploy to production. That protection
|
|
652
|
+
was deliberate and it was also silent. `login` confirmed the sandbox and moved
|
|
653
|
+
on, `mode production` printed a checkmark, and `status` showed the production
|
|
654
|
+
key as twelve characters of prefix. Nothing said the key could not push, so the
|
|
655
|
+
first notice was a 403 in the middle of a deploy, at the moment it cost the
|
|
656
|
+
most to learn.
|
|
657
|
+
|
|
658
|
+
Each of those three commands now names the capability at the point it hands you
|
|
659
|
+
the credential: the key rows in `status` carry what each key does beside how
|
|
660
|
+
long it lasts, the line under `push` says which secret key deploys, and
|
|
661
|
+
`ablo status --json` reports `effectiveKey.kind` so a pipeline can check before
|
|
662
|
+
it pushes instead of after it fails. The wording has one definition site, so the
|
|
663
|
+
command that stores a key, the command that switches to it, and the command
|
|
664
|
+
that reports it cannot describe it three different ways.
|
|
665
|
+
|
|
666
|
+
The renamed `status` line is the same fix in smaller form. `mode` and `env`
|
|
667
|
+
printed as peers and were not: one is the environment you selected, the other is
|
|
668
|
+
the one your credential actually reaches, and when they diverged neither word
|
|
669
|
+
said which was which. `acts on` states the fact. The `--json` output is
|
|
670
|
+
unchanged apart from the added `kind`.
|
|
671
|
+
|
|
3
672
|
## 0.34.1
|
|
4
673
|
|
|
5
674
|
### Patch Changes
|
|
@@ -16,7 +685,7 @@ A new page, **Operating on Your Database**, sets out the safety model for workin
|
|
|
16
685
|
|
|
17
686
|
A long-running actor has a stale-context problem the per-commit read gate never reaches. The `reads` guard is a premise for the commit in hand: you declare what you looked at, the server checks it at commit, and the premise is gone. That fits an actor that reads and writes in one breath, not one that reads a row, works for minutes — an LLM call, a fetch, a human's turn — and only then writes. By the time it commits, the premise it would have declared is already old, and there was no commit in between on which to hear that the ground had shifted. This release adds `track`, the durable half of the same idea. `ablo.<model>.track({ id })` registers a read-dependency that persists on the server; the next time you commit anything, a change that landed on the tracked row since you registered rides back on the receipt's `notifications` — the same `StaleNotification` an `onStale: 'notify'` premise hands you, arriving on the write you were going to make anyway. You can also register one as part of a write, `track: [{ group: 'deck:abc' }]` alongside the batch, the standing-subscription companion to the single-commit `reads`. A track is idempotent — registering the same target again refreshes the one subscription rather than stacking duplicates — it re-baselines after it fires so a given change notifies once, and it never notifies you of your own writes, since the signal is about what others did. Delivery is on your next commit's receipt; a track does not yet push out of band between commits, so it sharpens the write-time freshness check rather than replacing a live subscription.
|
|
18
687
|
|
|
19
|
-
The model-level presence verb is renamed from `watch` to `join`. It read like a data subscription but delivered presence — who else is on a set of rows and what they hold — so it now says what it does. `ablo.<model>.join(ids, { ttl })` opens the participant handle, with `.peers`, the scoped claim stream, and `await using` disposal unchanged; the handle's `status` was already `'joined'` and the layer beneath always called itself join, so the verb now matches the thing it returns. `onChange` remains the way to hear a row's
|
|
688
|
+
The model-level presence verb is renamed from `watch` to `join`. It read like a data subscription but delivered presence — who else is on a set of rows and what they hold — so it now says what it does. `ablo.<model>.join(ids, { ttl })` opens the participant handle, with `.peers`, the scoped claim stream, and `await using` disposal unchanged; the handle's `status` was already `'joined'` and the layer beneath always called itself join, so the verb now matches the thing it returns. `onChange` remains the way to hear a row's _values_ change, and `track` is the durable read-dependency for actors — three distinct jobs that the one overloaded `watch` used to blur. The React hook follows: `useWatch` becomes `useJoin`, the `WatchOptions` / `UseWatchOptions` / `UseWatchReturn` types become `JoinOptions` / `UseJoinOptions` / `UseJoinReturn`, and the error code `model_watch_not_configured` is now `model_join_not_configured`. There is no compatibility alias — rename the call sites and the type imports. The migration guide carries the mechanical diff.
|
|
20
689
|
|
|
21
690
|
### Patch Changes
|
|
22
691
|
|
|
@@ -30,9 +699,9 @@ The declarative seam introduced in 0.32.0 for carrying tenant identity into your
|
|
|
30
699
|
|
|
31
700
|
```ts
|
|
32
701
|
defineSchema({
|
|
33
|
-
models: { document: {
|
|
702
|
+
models: { document: {/* … */} },
|
|
34
703
|
sessionSettings: { 'app.current_org': 'orgId' },
|
|
35
|
-
})
|
|
704
|
+
});
|
|
36
705
|
```
|
|
37
706
|
|
|
38
707
|
The setting name is the key, so a setting takes exactly one source and a duplicate is unrepresentable rather than something to validate away. If you adopted `tenantContext` in 0.32.0, rename it to `sessionSettings` and turn each `{ guc: 'app.current_org', from: 'orgId' }` into `'app.current_org': 'orgId'`; the exported names followed the rename — `TenantContextMapping` and `TenantContextSource` became `SessionSettings` and `SessionSettingSource`, and `RESERVED_TENANT_CONTEXT_GUCS` became `RESERVED_SESSION_SETTINGS`. The meaning is unchanged: Ablo fills only settings it resolves from your authenticated identity, never from client-supplied data, so a mapping can forward the tenant Ablo already trusts but can never widen a writer's scope, and settings the engine reserves for itself — `row_security`, the timeouts — are still refused at definition time. Schemas pushed before the rename keep parsing.
|
|
@@ -59,9 +728,9 @@ The other half of holding one key is trusting that Ablo's writer stays inside th
|
|
|
59
728
|
|
|
60
729
|
```ts
|
|
61
730
|
defineSchema({
|
|
62
|
-
models: { document: {
|
|
731
|
+
models: { document: {/* … */} },
|
|
63
732
|
tenantContext: [{ guc: 'app.current_org', from: 'orgId' }],
|
|
64
|
-
})
|
|
733
|
+
});
|
|
65
734
|
```
|
|
66
735
|
|
|
67
736
|
The `from` side is a closed set — `orgId`, `projectId`, `environment`, and the other identifiers Ablo establishes for the write from your credential. It is deliberately not free-form: a mapping can only pass through a value Ablo has already authenticated, never let a caller name its own tenant, so it can narrow what the writer sees but never widen it. Settings Ablo reserves for itself are refused at definition time. Your policies stay the sole authority on what the writer may touch; this only gives them the context they were written to read.
|