@abloatai/ablo 0.36.0 → 0.37.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 -2
- package/CHANGELOG.md +71 -2013
- package/NOTICE +2 -2
- package/README.md +25 -71
- package/assets/banner.png +0 -0
- package/dist/auth.d.ts +2 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +2 -0
- package/dist/auth.js.map +1 -0
- package/dist/client.d.ts +3 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +3 -0
- package/dist/client.js.map +1 -0
- package/dist/coordination.d.ts +2 -0
- package/dist/coordination.d.ts.map +1 -0
- package/dist/coordination.js +2 -0
- package/dist/coordination.js.map +1 -0
- package/dist/index.d.ts +3 -114
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -163
- package/dist/index.js.map +1 -0
- package/dist/react.d.ts +4 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +3 -0
- package/dist/react.js.map +1 -0
- package/dist/schema.d.ts +2 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +2 -0
- package/dist/schema.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +2 -0
- package/dist/server.js.map +1 -0
- package/dist/source-conformance.d.ts +2 -0
- package/dist/source-conformance.d.ts.map +1 -0
- package/dist/source-conformance.js +2 -0
- package/dist/source-conformance.js.map +1 -0
- package/dist/source-drizzle.d.ts +2 -0
- package/dist/source-drizzle.d.ts.map +1 -0
- package/dist/source-drizzle.js +2 -0
- package/dist/source-drizzle.js.map +1 -0
- package/dist/source-kysely.d.ts +2 -0
- package/dist/source-kysely.d.ts.map +1 -0
- package/dist/source-kysely.js +2 -0
- package/dist/source-kysely.js.map +1 -0
- package/dist/source-next.d.ts +2 -0
- package/dist/source-next.d.ts.map +1 -0
- package/dist/source-next.js +2 -0
- package/dist/source-next.js.map +1 -0
- package/dist/source.d.ts +2 -0
- package/dist/source.d.ts.map +1 -0
- package/dist/source.js +2 -0
- package/dist/source.js.map +1 -0
- package/dist/wire.d.ts +2 -0
- package/dist/wire.d.ts.map +1 -0
- package/dist/wire.js +2 -0
- package/dist/wire.js.map +1 -0
- package/docs/agents.md +1 -1
- package/docs/api-keys.md +7 -6
- package/docs/api.md +10 -10
- package/docs/client-behavior.md +5 -5
- package/docs/coordination.md +52 -62
- package/docs/data-sources.md +1 -1
- package/docs/examples/agent-human.md +4 -4
- package/docs/examples/ai-sdk-tool.md +1 -1
- package/docs/examples/existing-python-backend.md +15 -4
- package/docs/examples/nextjs.md +27 -6
- package/docs/examples/server-agent.md +2 -2
- package/docs/how-it-works.md +4 -4
- package/docs/identity.md +2 -1
- package/docs/integration-guide.md +24 -13
- package/docs/internal/README.md +18 -0
- package/docs/internal/agent-fleet-coordination-design.md +171 -0
- package/docs/internal/agent-orchestration.md +58 -0
- package/docs/internal/commit-identifiers.md +91 -0
- package/docs/internal/concurrency-open-decisions.md +37 -0
- package/docs/internal/data-source-reverse-channel.md +150 -0
- package/docs/internal/per-field-conflict-detection.md +165 -0
- package/docs/internal/postgres-replication.md +64 -0
- package/docs/internal/serializable-schema.md +119 -0
- package/docs/internal/structure.md +32 -0
- package/docs/mcp.md +2 -2
- package/docs/migration.md +3 -3
- package/docs/quickstart.md +2 -2
- package/docs/react.md +5 -5
- package/docs/schema-contract.md +3 -3
- package/docs/sessions.md +91 -37
- package/examples/README.md +2 -2
- package/examples/data-source/README.md +1 -1
- package/examples/data-source/ablo-driver.ts +1 -1
- package/examples/data-source/customer-server.ts +1 -1
- package/examples/data-source/run.ts +1 -1
- package/examples/data-source/schema.ts +1 -1
- package/examples/quickstart.ts +2 -2
- package/llms.txt +8 -8
- package/package.json +63 -166
- package/bin/ablo.cjs +0 -39
- package/dist/BaseSyncedStore.d.ts +0 -843
- package/dist/BaseSyncedStore.js +0 -1971
- package/dist/Database.d.ts +0 -323
- package/dist/Database.js +0 -1502
- package/dist/InstanceCache.d.ts +0 -237
- package/dist/InstanceCache.js +0 -1166
- package/dist/LazyReferenceCollection.d.ts +0 -177
- package/dist/LazyReferenceCollection.js +0 -461
- package/dist/Model.d.ts +0 -454
- package/dist/Model.js +0 -919
- package/dist/ModelRegistry.d.ts +0 -225
- package/dist/ModelRegistry.js +0 -539
- package/dist/NetworkMonitor.d.ts +0 -28
- package/dist/NetworkMonitor.js +0 -79
- package/dist/RuntimeContext.d.ts +0 -52
- package/dist/RuntimeContext.js +0 -80
- package/dist/SyncClient.d.ts +0 -541
- package/dist/SyncClient.js +0 -2202
- package/dist/adapters/alwaysOnline.d.ts +0 -14
- package/dist/adapters/alwaysOnline.js +0 -17
- package/dist/adapters/inMemoryStorage.d.ts +0 -31
- package/dist/adapters/inMemoryStorage.js +0 -110
- package/dist/ai-sdk/coordinatedTool.d.ts +0 -120
- package/dist/ai-sdk/coordinatedTool.js +0 -134
- package/dist/ai-sdk/coordinationContext.d.ts +0 -46
- package/dist/ai-sdk/coordinationContext.js +0 -106
- package/dist/ai-sdk/index.d.ts +0 -121
- package/dist/ai-sdk/index.js +0 -121
- package/dist/ai-sdk/wrap.d.ts +0 -65
- package/dist/ai-sdk/wrap.js +0 -39
- package/dist/auth/index.d.ts +0 -1
- package/dist/auth/index.js +0 -8
- package/dist/batching/index.d.ts +0 -55
- package/dist/batching/index.js +0 -147
- package/dist/client/Ablo.d.ts +0 -231
- package/dist/client/Ablo.js +0 -160
- package/dist/client/abloClient.d.ts +0 -309
- package/dist/client/abloClient.js +0 -13
- package/dist/client/clientPrelude.d.ts +0 -52
- package/dist/client/clientPrelude.js +0 -60
- package/dist/client/consoleLogger.d.ts +0 -35
- package/dist/client/consoleLogger.js +0 -44
- package/dist/client/coreClient.d.ts +0 -60
- package/dist/client/coreClient.js +0 -118
- package/dist/client/createInternalComponents.d.ts +0 -50
- package/dist/client/createInternalComponents.js +0 -98
- package/dist/client/createModelProxy.d.ts +0 -248
- package/dist/client/createModelProxy.js +0 -884
- package/dist/client/humans.d.ts +0 -69
- package/dist/client/humans.js +0 -78
- package/dist/client/modelRegistration.d.ts +0 -10
- package/dist/client/modelRegistration.js +0 -312
- package/dist/client/options.d.ts +0 -461
- package/dist/client/options.js +0 -7
- package/dist/client/reactiveEngine.d.ts +0 -53
- package/dist/client/reactiveEngine.js +0 -688
- package/dist/client/resourceTypes.d.ts +0 -12
- package/dist/client/resourceTypes.js +0 -10
- package/dist/client/schemaConfig.d.ts +0 -44
- package/dist/client/schemaConfig.js +0 -185
- package/dist/client/storeCluster.d.ts +0 -47
- package/dist/client/storeCluster.js +0 -118
- package/dist/client/storeLifecycle.d.ts +0 -61
- package/dist/client/storeLifecycle.js +0 -231
- package/dist/client/validateAbloOptions.d.ts +0 -42
- package/dist/client/validateAbloOptions.js +0 -43
- package/dist/client/wsMutationExecutor.d.ts +0 -27
- package/dist/client/wsMutationExecutor.js +0 -72
- package/dist/context.d.ts +0 -42
- package/dist/context.js +0 -81
- package/dist/coordination/ClaimLog.d.ts +0 -26
- package/dist/coordination/ClaimLog.js +0 -32
- package/dist/coordination/index.d.ts +0 -1
- package/dist/coordination/index.js +0 -8
- package/dist/core/index.d.ts +0 -33
- package/dist/core/index.js +0 -48
- package/dist/docs/catalog.d.ts +0 -72
- package/dist/docs/catalog.js +0 -230
- package/dist/docs/index.d.ts +0 -10
- package/dist/docs/index.js +0 -10
- package/dist/environment.d.ts +0 -1
- package/dist/environment.js +0 -8
- package/dist/interfaces/index.d.ts +0 -311
- package/dist/interfaces/index.js +0 -9
- package/dist/keys/index.d.ts +0 -1
- package/dist/keys/index.js +0 -8
- package/dist/mutators/RecordingMutation.d.ts +0 -36
- package/dist/mutators/RecordingMutation.js +0 -182
- package/dist/mutators/Transaction.d.ts +0 -40
- package/dist/mutators/Transaction.js +0 -58
- package/dist/mutators/UndoManager.d.ts +0 -258
- package/dist/mutators/UndoManager.js +0 -658
- package/dist/mutators/defineMutators.d.ts +0 -60
- package/dist/mutators/defineMutators.js +0 -18
- package/dist/mutators/inverseOp.d.ts +0 -126
- package/dist/mutators/inverseOp.js +0 -71
- package/dist/mutators/mutateActions.d.ts +0 -45
- package/dist/mutators/mutateActions.js +0 -105
- package/dist/mutators/readerActions.d.ts +0 -33
- package/dist/mutators/readerActions.js +0 -57
- package/dist/mutators/undoApply.d.ts +0 -51
- package/dist/mutators/undoApply.js +0 -117
- package/dist/policy/index.d.ts +0 -21
- package/dist/policy/index.js +0 -20
- package/dist/query/QueryProcessor.d.ts +0 -75
- package/dist/query/QueryProcessor.js +0 -255
- package/dist/query/client.d.ts +0 -64
- package/dist/query/client.js +0 -138
- package/dist/query/types.d.ts +0 -85
- package/dist/query/types.js +0 -16
- package/dist/react/AbloProvider.d.ts +0 -242
- package/dist/react/AbloProvider.js +0 -458
- package/dist/react/ClientSideSuspense.d.ts +0 -36
- package/dist/react/ClientSideSuspense.js +0 -17
- package/dist/react/DefaultFallback.d.ts +0 -24
- package/dist/react/DefaultFallback.js +0 -43
- package/dist/react/context.d.ts +0 -55
- package/dist/react/context.js +0 -29
- package/dist/react/createAbloReact.d.ts +0 -56
- package/dist/react/createAbloReact.js +0 -51
- package/dist/react/index.d.ts +0 -62
- package/dist/react/index.js +0 -69
- package/dist/react/internalContext.d.ts +0 -33
- package/dist/react/internalContext.js +0 -3
- package/dist/react/useAblo.d.ts +0 -82
- package/dist/react/useAblo.js +0 -120
- package/dist/react/useCurrentUserId.d.ts +0 -22
- package/dist/react/useCurrentUserId.js +0 -34
- package/dist/react/useErrorListener.d.ts +0 -20
- package/dist/react/useErrorListener.js +0 -38
- package/dist/react/useMutationFailureListener.d.ts +0 -26
- package/dist/react/useMutationFailureListener.js +0 -38
- package/dist/react/useMutators.d.ts +0 -56
- package/dist/react/useMutators.js +0 -84
- package/dist/react/useReactive.d.ts +0 -35
- package/dist/react/useReactive.js +0 -123
- package/dist/react/useSyncStatus.d.ts +0 -59
- package/dist/react/useSyncStatus.js +0 -76
- package/dist/react/useUndoScope.d.ts +0 -34
- package/dist/react/useUndoScope.js +0 -81
- package/dist/schema/coordination.d.ts +0 -112
- package/dist/schema/coordination.js +0 -133
- package/dist/schema/ddl.d.ts +0 -97
- package/dist/schema/ddl.js +0 -491
- package/dist/schema/ddlLock.d.ts +0 -35
- package/dist/schema/ddlLock.js +0 -46
- package/dist/schema/diff.d.ts +0 -225
- package/dist/schema/diff.js +0 -289
- package/dist/schema/generate.d.ts +0 -19
- package/dist/schema/generate.js +0 -86
- package/dist/schema/index.d.ts +0 -42
- package/dist/schema/index.js +0 -80
- package/dist/schema/queries.d.ts +0 -201
- package/dist/schema/queries.js +0 -144
- package/dist/schema/select.d.ts +0 -40
- package/dist/schema/select.js +0 -90
- package/dist/schema/serialize.d.ts +0 -115
- package/dist/schema/serialize.js +0 -265
- package/dist/schema/sugar.d.ts +0 -109
- package/dist/schema/sugar.js +0 -83
- package/dist/schema/syncDeltaRow.d.ts +0 -6
- package/dist/schema/syncDeltaRow.js +0 -6
- package/dist/server/adapter.d.ts +0 -173
- package/dist/server/adapter.js +0 -18
- package/dist/server/commit.d.ts +0 -107
- package/dist/server/commit.js +0 -1
- package/dist/server/index.d.ts +0 -14
- package/dist/server/index.js +0 -2
- package/dist/server/readConfig.d.ts +0 -80
- package/dist/server/readConfig.js +0 -8
- package/dist/server/storageMode.d.ts +0 -23
- package/dist/server/storageMode.js +0 -17
- package/dist/source/adapter.d.ts +0 -83
- package/dist/source/adapter.js +0 -24
- package/dist/source/adapters/drizzle.d.ts +0 -48
- package/dist/source/adapters/drizzle.js +0 -219
- package/dist/source/adapters/kysely.d.ts +0 -42
- package/dist/source/adapters/kysely.js +0 -205
- package/dist/source/adapters/kyselyMutationCore.d.ts +0 -76
- package/dist/source/adapters/kyselyMutationCore.js +0 -125
- package/dist/source/adapters/memory.d.ts +0 -13
- package/dist/source/adapters/memory.js +0 -130
- package/dist/source/adapters/prisma.d.ts +0 -63
- package/dist/source/adapters/prisma.js +0 -202
- package/dist/source/conformance.d.ts +0 -37
- package/dist/source/conformance.js +0 -215
- package/dist/source/connector.d.ts +0 -95
- package/dist/source/connector.js +0 -266
- package/dist/source/connectorProtocol.d.ts +0 -154
- package/dist/source/connectorProtocol.js +0 -163
- package/dist/source/contract.d.ts +0 -195
- package/dist/source/contract.js +0 -164
- package/dist/source/factory.d.ts +0 -92
- package/dist/source/factory.js +0 -286
- package/dist/source/idempotency.d.ts +0 -61
- package/dist/source/idempotency.js +0 -144
- package/dist/source/index.d.ts +0 -23
- package/dist/source/index.js +0 -30
- package/dist/source/migrations.d.ts +0 -21
- package/dist/source/migrations.js +0 -103
- package/dist/source/next.d.ts +0 -32
- package/dist/source/next.js +0 -25
- package/dist/source/pushQueue.d.ts +0 -134
- package/dist/source/pushQueue.js +0 -256
- package/dist/source/signing.d.ts +0 -92
- package/dist/source/signing.js +0 -162
- package/dist/source/types.d.ts +0 -401
- package/dist/source/types.js +0 -59
- package/dist/storeContract.d.ts +0 -145
- package/dist/storeContract.js +0 -12
- package/dist/stores/DatabaseManager.d.ts +0 -107
- package/dist/stores/DatabaseManager.js +0 -388
- package/dist/stores/ObjectStore.d.ts +0 -115
- package/dist/stores/ObjectStore.js +0 -393
- package/dist/stores/ObjectStoreContract.d.ts +0 -38
- package/dist/stores/ObjectStoreContract.js +0 -1
- package/dist/stores/StoreManager.d.ts +0 -114
- package/dist/stores/StoreManager.js +0 -304
- package/dist/stores/SyncActionStore.d.ts +0 -99
- package/dist/stores/SyncActionStore.js +0 -506
- package/dist/stores/openIDBWithTimeout.d.ts +0 -65
- package/dist/stores/openIDBWithTimeout.js +0 -153
- package/dist/stores/syncAction.d.ts +0 -26
- package/dist/stores/syncAction.js +0 -16
- package/dist/surface.d.ts +0 -36
- package/dist/surface.js +0 -75
- package/dist/sync/BootstrapFetcher.d.ts +0 -284
- package/dist/sync/BootstrapFetcher.js +0 -964
- package/dist/sync/ConnectionManager.d.ts +0 -8
- package/dist/sync/ConnectionManager.js +0 -8
- package/dist/sync/OnDemandLoader.d.ts +0 -231
- package/dist/sync/OnDemandLoader.js +0 -743
- package/dist/sync/SubscriptionManager.d.ts +0 -159
- package/dist/sync/SubscriptionManager.js +0 -243
- package/dist/sync/SyncWebSocket.d.ts +0 -173
- package/dist/sync/SyncWebSocket.js +0 -438
- package/dist/sync/awaitClaimGrant.d.ts +0 -6
- package/dist/sync/awaitClaimGrant.js +0 -6
- package/dist/sync/bootstrapApply.d.ts +0 -73
- package/dist/sync/bootstrapApply.js +0 -73
- package/dist/sync/commitFrames.d.ts +0 -8
- package/dist/sync/commitFrames.js +0 -8
- package/dist/sync/contextPorts.d.ts +0 -18
- package/dist/sync/contextPorts.js +0 -31
- package/dist/sync/createClaimStream.d.ts +0 -7
- package/dist/sync/createClaimStream.js +0 -7
- package/dist/sync/createPresenceStream.d.ts +0 -69
- package/dist/sync/createPresenceStream.js +0 -200
- package/dist/sync/createSnapshot.d.ts +0 -29
- package/dist/sync/createSnapshot.js +0 -118
- package/dist/sync/credentialLifecycle.d.ts +0 -7
- package/dist/sync/credentialLifecycle.js +0 -7
- package/dist/sync/deltaPipeline.d.ts +0 -114
- package/dist/sync/deltaPipeline.js +0 -278
- package/dist/sync/groupChange.d.ts +0 -116
- package/dist/sync/groupChange.js +0 -244
- package/dist/sync/participants.d.ts +0 -132
- package/dist/sync/participants.js +0 -346
- package/dist/sync/persistedPrefix.d.ts +0 -12
- package/dist/sync/persistedPrefix.js +0 -22
- package/dist/sync/schemaDrift.d.ts +0 -55
- package/dist/sync/schemaDrift.js +0 -53
- package/dist/sync/schemas.d.ts +0 -71
- package/dist/sync/schemas.js +0 -94
- package/dist/sync/syncCursor.d.ts +0 -40
- package/dist/sync/syncCursor.js +0 -55
- package/dist/sync/syncPlan.d.ts +0 -54
- package/dist/sync/syncPlan.js +0 -50
- package/dist/sync/wsFrameHandlers.d.ts +0 -8
- package/dist/sync/wsFrameHandlers.js +0 -8
- package/dist/syncLog/contract.d.ts +0 -20
- package/dist/syncLog/contract.js +0 -19
- package/dist/syncLog/index.d.ts +0 -1
- package/dist/syncLog/index.js +0 -1
- package/dist/transaction/ablo.d.ts +0 -88
- package/dist/transaction/ablo.js +0 -33
- package/dist/transaction/auth/apiKey.d.ts +0 -152
- package/dist/transaction/auth/apiKey.js +0 -419
- package/dist/transaction/auth/bootstrapScope.d.ts +0 -15
- package/dist/transaction/auth/bootstrapScope.js +0 -1
- package/dist/transaction/auth/capability.d.ts +0 -212
- package/dist/transaction/auth/capability.js +0 -224
- package/dist/transaction/auth/credentialEndpoint.d.ts +0 -61
- package/dist/transaction/auth/credentialEndpoint.js +0 -86
- package/dist/transaction/auth/credentialPolicy.d.ts +0 -148
- package/dist/transaction/auth/credentialPolicy.js +0 -125
- package/dist/transaction/auth/credentialSource.d.ts +0 -30
- package/dist/transaction/auth/credentialSource.js +0 -55
- package/dist/transaction/auth/hostedEndpoints.d.ts +0 -21
- package/dist/transaction/auth/hostedEndpoints.js +0 -21
- package/dist/transaction/auth/identity.d.ts +0 -55
- package/dist/transaction/auth/identity.js +0 -210
- package/dist/transaction/auth/index.d.ts +0 -162
- package/dist/transaction/auth/index.js +0 -304
- package/dist/transaction/auth/schemas.d.ts +0 -59
- package/dist/transaction/auth/schemas.js +0 -85
- package/dist/transaction/auth/sessionMint.d.ts +0 -28
- package/dist/transaction/auth/sessionMint.js +0 -85
- package/dist/transaction/coordination/awaitClaimGrant.d.ts +0 -56
- package/dist/transaction/coordination/awaitClaimGrant.js +0 -124
- package/dist/transaction/coordination/claimHeartbeatLoop.d.ts +0 -84
- package/dist/transaction/coordination/claimHeartbeatLoop.js +0 -108
- package/dist/transaction/coordination/claimMeta.d.ts +0 -49
- package/dist/transaction/coordination/claimMeta.js +0 -52
- package/dist/transaction/coordination/createClaimStream.d.ts +0 -64
- package/dist/transaction/coordination/createClaimStream.js +0 -475
- package/dist/transaction/coordination/events.d.ts +0 -74
- package/dist/transaction/coordination/events.js +0 -7
- package/dist/transaction/coordination/index.d.ts +0 -19
- package/dist/transaction/coordination/index.js +0 -45
- package/dist/transaction/coordination/locator.d.ts +0 -104
- package/dist/transaction/coordination/locator.js +0 -102
- package/dist/transaction/coordination/schema.d.ts +0 -1536
- package/dist/transaction/coordination/schema.js +0 -1177
- package/dist/transaction/coordination/targetConflict.d.ts +0 -2
- package/dist/transaction/coordination/targetConflict.js +0 -107
- package/dist/transaction/coordination/trace.d.ts +0 -78
- package/dist/transaction/coordination/trace.js +0 -138
- package/dist/transaction/durableWrites.d.ts +0 -62
- package/dist/transaction/durableWrites.js +0 -71
- package/dist/transaction/environment.d.ts +0 -105
- package/dist/transaction/environment.js +0 -108
- package/dist/transaction/errorCodes.d.ts +0 -403
- package/dist/transaction/errorCodes.js +0 -484
- package/dist/transaction/errors.d.ts +0 -428
- package/dist/transaction/errors.js +0 -686
- package/dist/transaction/footprint.d.ts +0 -111
- package/dist/transaction/footprint.js +0 -0
- package/dist/transaction/index.d.ts +0 -20
- package/dist/transaction/index.js +0 -20
- package/dist/transaction/keys/index.d.ts +0 -87
- package/dist/transaction/keys/index.js +0 -207
- package/dist/transaction/log/syncDeltaRow.d.ts +0 -158
- package/dist/transaction/log/syncDeltaRow.js +0 -95
- package/dist/transaction/logPosition.d.ts +0 -97
- package/dist/transaction/logPosition.js +0 -125
- package/dist/transaction/logger.d.ts +0 -16
- package/dist/transaction/logger.js +0 -7
- package/dist/transaction/observability.d.ts +0 -53
- package/dist/transaction/observability.js +0 -19
- package/dist/transaction/persistence.d.ts +0 -12
- package/dist/transaction/persistence.js +0 -11
- package/dist/transaction/plugin.d.ts +0 -285
- package/dist/transaction/plugin.js +0 -106
- package/dist/transaction/policy/types.d.ts +0 -217
- package/dist/transaction/policy/types.js +0 -126
- package/dist/transaction/resources/functionalUpdate.d.ts +0 -79
- package/dist/transaction/resources/functionalUpdate.js +0 -87
- package/dist/transaction/resources/httpResources.d.ts +0 -321
- package/dist/transaction/resources/httpResources.js +0 -7
- package/dist/transaction/resources/modelOperations.d.ts +0 -427
- package/dist/transaction/resources/modelOperations.js +0 -12
- package/dist/transaction/resources/mutationOptions.d.ts +0 -66
- package/dist/transaction/resources/mutationOptions.js +0 -9
- package/dist/transaction/resources/where.d.ts +0 -101
- package/dist/transaction/resources/where.js +0 -115
- package/dist/transaction/resources/writeOptionsSchema.d.ts +0 -47
- package/dist/transaction/resources/writeOptionsSchema.js +0 -73
- package/dist/transaction/schema/field.d.ts +0 -120
- package/dist/transaction/schema/field.js +0 -265
- package/dist/transaction/schema/fieldRef.d.ts +0 -38
- package/dist/transaction/schema/fieldRef.js +0 -11
- package/dist/transaction/schema/loadStrategy.d.ts +0 -45
- package/dist/transaction/schema/loadStrategy.js +0 -46
- package/dist/transaction/schema/model.d.ts +0 -379
- package/dist/transaction/schema/model.js +0 -123
- package/dist/transaction/schema/openapi.d.ts +0 -58
- package/dist/transaction/schema/openapi.js +0 -501
- package/dist/transaction/schema/relation.d.ts +0 -204
- package/dist/transaction/schema/relation.js +0 -104
- package/dist/transaction/schema/residency.d.ts +0 -29
- package/dist/transaction/schema/residency.js +0 -25
- package/dist/transaction/schema/roles.d.ts +0 -249
- package/dist/transaction/schema/roles.js +0 -230
- package/dist/transaction/schema/schema.d.ts +0 -351
- package/dist/transaction/schema/schema.js +0 -325
- package/dist/transaction/schema/tenancy.d.ts +0 -139
- package/dist/transaction/schema/tenancy.js +0 -190
- package/dist/transaction/transactionLayer.d.ts +0 -82
- package/dist/transaction/transactionLayer.js +0 -24
- package/dist/transaction/transactions/settlement/commitEnvelope.d.ts +0 -143
- package/dist/transaction/transactions/settlement/commitEnvelope.js +0 -161
- package/dist/transaction/transactions/settlement/httpCommitEnvelope.d.ts +0 -53
- package/dist/transaction/transactions/settlement/httpCommitEnvelope.js +0 -207
- package/dist/transaction/transactions/settlement/idempotencyKey.d.ts +0 -10
- package/dist/transaction/transactions/settlement/idempotencyKey.js +0 -9
- package/dist/transaction/transactions/settlement/pendingWrite.d.ts +0 -112
- package/dist/transaction/transactions/settlement/pendingWrite.js +0 -20
- package/dist/transaction/transport/commitFrames.d.ts +0 -90
- package/dist/transaction/transport/commitFrames.js +0 -134
- package/dist/transaction/transport/connectionManager.d.ts +0 -215
- package/dist/transaction/transport/connectionManager.js +0 -673
- package/dist/transaction/transport/credentialLifecycle.d.ts +0 -177
- package/dist/transaction/transport/credentialLifecycle.js +0 -324
- package/dist/transaction/transport/heartbeat.d.ts +0 -65
- package/dist/transaction/transport/heartbeat.js +0 -93
- package/dist/transaction/transport/httpClient.d.ts +0 -131
- package/dist/transaction/transport/httpClient.js +0 -146
- package/dist/transaction/transport/httpOptions.d.ts +0 -33
- package/dist/transaction/transport/httpOptions.js +0 -12
- package/dist/transaction/transport/httpTransport.d.ts +0 -8
- package/dist/transaction/transport/httpTransport.js +0 -1388
- package/dist/transaction/transport/networkProbe.d.ts +0 -84
- package/dist/transaction/transport/networkProbe.js +0 -207
- package/dist/transaction/transport/wsFrameHandlers.d.ts +0 -128
- package/dist/transaction/transport/wsFrameHandlers.js +0 -429
- package/dist/transaction/transport/wsTransport.d.ts +0 -574
- package/dist/transaction/transport/wsTransport.js +0 -1023
- package/dist/transaction/types/assertExact.d.ts +0 -17
- package/dist/transaction/types/assertExact.js +0 -1
- package/dist/transaction/types/global.d.ts +0 -107
- package/dist/transaction/types/global.js +0 -40
- package/dist/transaction/types/index.d.ts +0 -205
- package/dist/transaction/types/index.js +0 -56
- package/dist/transaction/types/modelData.d.ts +0 -10
- package/dist/transaction/types/modelData.js +0 -9
- package/dist/transaction/types/participant.d.ts +0 -20
- package/dist/transaction/types/participant.js +0 -10
- package/dist/transaction/types/streams.d.ts +0 -550
- package/dist/transaction/types/streams.js +0 -11
- package/dist/transaction/utils/asyncIterator.d.ts +0 -34
- package/dist/transaction/utils/asyncIterator.js +0 -135
- package/dist/transaction/utils/duration.d.ts +0 -50
- package/dist/transaction/utils/duration.js +0 -77
- package/dist/transaction/utils/json.d.ts +0 -57
- package/dist/transaction/utils/json.js +0 -276
- package/dist/transaction/wire/accountResponses.d.ts +0 -420
- package/dist/transaction/wire/accountResponses.js +0 -290
- package/dist/transaction/wire/auth.d.ts +0 -56
- package/dist/transaction/wire/auth.js +0 -63
- package/dist/transaction/wire/bootstrapReason.d.ts +0 -9
- package/dist/transaction/wire/bootstrapReason.js +0 -8
- package/dist/transaction/wire/claimEvent.d.ts +0 -76
- package/dist/transaction/wire/claimEvent.js +0 -73
- package/dist/transaction/wire/claims.d.ts +0 -530
- package/dist/transaction/wire/claims.js +0 -327
- package/dist/transaction/wire/commit.d.ts +0 -603
- package/dist/transaction/wire/commit.js +0 -321
- package/dist/transaction/wire/delta.d.ts +0 -250
- package/dist/transaction/wire/delta.js +0 -147
- package/dist/transaction/wire/errorEnvelope.d.ts +0 -72
- package/dist/transaction/wire/errorEnvelope.js +0 -123
- package/dist/transaction/wire/feedCursor.d.ts +0 -60
- package/dist/transaction/wire/feedCursor.js +0 -82
- package/dist/transaction/wire/feedEvent.d.ts +0 -204
- package/dist/transaction/wire/feedEvent.js +0 -65
- package/dist/transaction/wire/frames.d.ts +0 -194
- package/dist/transaction/wire/frames.js +0 -50
- package/dist/transaction/wire/inboundFrames.d.ts +0 -562
- package/dist/transaction/wire/inboundFrames.js +0 -116
- package/dist/transaction/wire/index.d.ts +0 -54
- package/dist/transaction/wire/index.js +0 -83
- package/dist/transaction/wire/listEnvelope.d.ts +0 -37
- package/dist/transaction/wire/listEnvelope.js +0 -42
- package/dist/transaction/wire/modelMutations.d.ts +0 -31
- package/dist/transaction/wire/modelMutations.js +0 -52
- package/dist/transaction/wire/modelResponses.d.ts +0 -85
- package/dist/transaction/wire/modelResponses.js +0 -43
- package/dist/transaction/wire/modelShape.d.ts +0 -78
- package/dist/transaction/wire/modelShape.js +0 -74
- package/dist/transaction/wire/protocol.d.ts +0 -38
- package/dist/transaction/wire/protocol.js +0 -38
- package/dist/transaction/wire/protocolVersion.d.ts +0 -73
- package/dist/transaction/wire/protocolVersion.js +0 -83
- package/dist/transactions/mutations/MutationQueue.d.ts +0 -661
- package/dist/transactions/mutations/MutationQueue.js +0 -2807
- package/dist/transactions/mutations/MutationStore.d.ts +0 -20
- package/dist/transactions/mutations/MutationStore.js +0 -53
- package/dist/transactions/mutations/UnconfirmedWrites.d.ts +0 -82
- package/dist/transactions/mutations/UnconfirmedWrites.js +0 -104
- package/dist/transactions/mutations/coalesceRules.d.ts +0 -58
- package/dist/transactions/mutations/coalesceRules.js +0 -140
- package/dist/transactions/mutations/commitLatency.d.ts +0 -52
- package/dist/transactions/mutations/commitLatency.js +0 -130
- package/dist/transactions/mutations/commitOutboxStore.d.ts +0 -28
- package/dist/transactions/mutations/commitOutboxStore.js +0 -26
- package/dist/transactions/mutations/commitPayload.d.ts +0 -165
- package/dist/transactions/mutations/commitPayload.js +0 -152
- package/dist/transactions/mutations/deltaConfirmation.d.ts +0 -63
- package/dist/transactions/mutations/deltaConfirmation.js +0 -235
- package/dist/transactions/mutations/durableWriteStore.d.ts +0 -14
- package/dist/transactions/mutations/durableWriteStore.js +0 -12
- package/dist/transactions/mutations/optimisticApply.d.ts +0 -49
- package/dist/transactions/mutations/optimisticApply.js +0 -65
- package/dist/transactions/mutations/replayValidation.d.ts +0 -187
- package/dist/transactions/mutations/replayValidation.js +0 -164
- package/dist/utils/mobxSetup.d.ts +0 -53
- package/dist/utils/mobxSetup.js +0 -330
- package/dist/views/QueryView.d.ts +0 -79
- package/dist/views/QueryView.js +0 -218
- package/dist/views/ViewRegistry.d.ts +0 -20
- package/dist/views/ViewRegistry.js +0 -55
- package/dist/views/incrementalView.d.ts +0 -45
- package/dist/views/incrementalView.js +0 -69
- package/dist/webhooks/events.d.ts +0 -43
- package/dist/webhooks/events.js +0 -42
- package/dist/webhooks/index.d.ts +0 -8
- package/dist/webhooks/index.js +0 -8
- package/dist/wire/index.d.ts +0 -1
- package/dist/wire/index.js +0 -8
|
@@ -30,6 +30,7 @@ The normal integration is one client:
|
|
|
30
30
|
|
|
31
31
|
```ts
|
|
32
32
|
import Ablo from '@abloatai/ablo';
|
|
33
|
+
import { credentialEndpointSuccessSchema } from '@abloatai/ablo/auth';
|
|
33
34
|
import { defineSchema, model, z } from '@abloatai/ablo/schema';
|
|
34
35
|
```
|
|
35
36
|
|
|
@@ -158,7 +159,7 @@ For rows that don't carry `organization_id` themselves but inherit tenancy via a
|
|
|
158
159
|
foreign key, set `policy: { by: 'parent', fk: '<fk>', parent: '<parentTable>' }`.
|
|
159
160
|
For genuinely global/reference data, `policy: { by: 'none' }`. ⚠ `by: 'none'`
|
|
160
161
|
exposes the whole table cross-tenant, so it's an explicit, named branch — never a
|
|
161
|
-
falsy flag. See `packages/
|
|
162
|
+
falsy flag. See `packages/transaction/src/schema/model.ts` for the full option set.
|
|
162
163
|
|
|
163
164
|
## 2. Create The Client
|
|
164
165
|
|
|
@@ -220,8 +221,18 @@ const sync = Ablo({ schema, apiKey: process.env.ABLO_API_KEY });
|
|
|
220
221
|
|
|
221
222
|
export async function POST() {
|
|
222
223
|
const session = await auth(); // your own auth — returns the signed-in user
|
|
223
|
-
const { token } = await sync.sessions.create({
|
|
224
|
-
|
|
224
|
+
const { token, expiresAt } = await sync.sessions.create({
|
|
225
|
+
user: { id: session.userId },
|
|
226
|
+
can: { tasks: ['read', 'update'] },
|
|
227
|
+
});
|
|
228
|
+
return Response.json(
|
|
229
|
+
credentialEndpointSuccessSchema.parse({
|
|
230
|
+
token,
|
|
231
|
+
expiresAt,
|
|
232
|
+
credentialKind: 'ephemeral',
|
|
233
|
+
}),
|
|
234
|
+
{ headers: { 'Cache-Control': 'no-store' } },
|
|
235
|
+
);
|
|
225
236
|
}
|
|
226
237
|
```
|
|
227
238
|
|
|
@@ -255,8 +266,8 @@ refreshes before expiry.
|
|
|
255
266
|
## 3. Read State
|
|
256
267
|
|
|
257
268
|
Reads come in two flavors, and you pick based on whether you can wait.
|
|
258
|
-
`
|
|
259
|
-
store) — they're async, so you `await` them. `get(id)
|
|
269
|
+
`get({ id })` and `list({ where })` hit the server (and hydrate the local
|
|
270
|
+
store) — they're async, so you `await` them. `local.get(id)`,
|
|
260
271
|
`local.list({ where })`, and `local.count({ where })` read the already-synced local
|
|
261
272
|
graph synchronously, so they're the ones you call in render — and the ones you
|
|
262
273
|
use inside a `useAblo` selector, never the async `retrieve`/`list`.
|
|
@@ -267,15 +278,15 @@ and waits.
|
|
|
267
278
|
```ts
|
|
268
279
|
await ablo.ready();
|
|
269
280
|
|
|
270
|
-
const report = await ablo.weatherReports.
|
|
281
|
+
const report = await ablo.weatherReports.get({ id: 'report_stockholm' });
|
|
271
282
|
if (!report) throw new Error('report not found');
|
|
272
283
|
```
|
|
273
284
|
|
|
274
|
-
Use `local.
|
|
285
|
+
Use `local.get`, `local.list`, and `local.count` for synchronous local-graph reads after
|
|
275
286
|
data has synced.
|
|
276
287
|
|
|
277
288
|
```ts
|
|
278
|
-
const report = ablo.weatherReports.local.
|
|
289
|
+
const report = ablo.weatherReports.local.get('report_stockholm');
|
|
279
290
|
const activeReports = ablo.weatherReports.local.list({
|
|
280
291
|
where: { projectId: 'proj_123' },
|
|
281
292
|
filter: (report) => report.status !== 'ready',
|
|
@@ -296,7 +307,7 @@ export function ReportRow({
|
|
|
296
307
|
}: {
|
|
297
308
|
report: { id: string; location: string; status: string };
|
|
298
309
|
}) {
|
|
299
|
-
const report = useAblo((ablo) => ablo.weatherReports.local.
|
|
310
|
+
const report = useAblo((ablo) => ablo.weatherReports.local.get(serverReport.id)) ?? serverReport;
|
|
300
311
|
const active = useAblo((ablo) => ablo.weatherReports.claim.state({ id: serverReport.id }));
|
|
301
312
|
|
|
302
313
|
return <button disabled={Boolean(active) || report.status === 'ready'}>{report.location}</button>;
|
|
@@ -387,7 +398,7 @@ The migration can be gradual:
|
|
|
387
398
|
|
|
388
399
|
1. Declare schema for one model, such as `reports`.
|
|
389
400
|
2. Keep existing server loads for first paint.
|
|
390
|
-
3. Add `useAblo((ablo) => ablo.weatherReports.local.
|
|
401
|
+
3. Add `useAblo((ablo) => ablo.weatherReports.local.get(id)) ?? serverReport` for live rows.
|
|
391
402
|
4. Add one Data Source endpoint that calls the existing service layer.
|
|
392
403
|
5. Move one mutation button from `fetch('/api/reports/...')` to `ablo.weatherReports.update(...)`.
|
|
393
404
|
6. Add an outbox/events path for writes that still happen outside Ablo.
|
|
@@ -516,9 +527,9 @@ them.
|
|
|
516
527
|
|
|
517
528
|
| Method | Use it for |
|
|
518
529
|
| -------------------------------------- | -------------------------------------------------------------------------------- |
|
|
519
|
-
| `
|
|
530
|
+
| `get({ id })` | Async read of one row from the server (await it). |
|
|
520
531
|
| `list({ where })` | Async read of many rows from the server (await it). |
|
|
521
|
-
| `get(id)`
|
|
532
|
+
| `local.get(id)` | Synchronous local read of one synced row (use in render). |
|
|
522
533
|
| `local.list({ where })` | Synchronous local read of many synced rows. |
|
|
523
534
|
| `local.count({ where })` | Synchronous local count of synced rows. |
|
|
524
535
|
| `create({ data, id? })` | Create through the model client. |
|
|
@@ -528,5 +539,5 @@ them.
|
|
|
528
539
|
| `claim({ id, description?, ttl? })` | Acquire a disposable handle: wait for your turn, re-read, and hold the row. |
|
|
529
540
|
|
|
530
541
|
Keep first integrations on the model methods above. Every mutation and
|
|
531
|
-
server-read verb takes one options object;
|
|
542
|
+
server-read verb takes one options object; the synchronous `local.get(id)` stays
|
|
532
543
|
positional.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Internal Architecture Notes
|
|
2
|
+
|
|
3
|
+
These documents explain implementation and protocol decisions for contributors.
|
|
4
|
+
They are not extra public import paths.
|
|
5
|
+
|
|
6
|
+
The ownership rule is:
|
|
7
|
+
|
|
8
|
+
- transaction owns transport-neutral and HTTP contracts;
|
|
9
|
+
- humans owns reactive state, WebSockets, presence, browser persistence, and React;
|
|
10
|
+
- agent owns agent-specific behavior and perception;
|
|
11
|
+
- the branded `@abloatai/ablo` package maps stable public entrypoints to those
|
|
12
|
+
owners;
|
|
13
|
+
- `apps/sync-server` owns backend execution.
|
|
14
|
+
|
|
15
|
+
Consumer code should import `@abloatai/ablo` and its documented subpaths.
|
|
16
|
+
Contributor code should import the narrow owner package or module it actually
|
|
17
|
+
uses. Do not add forwarding compatibility packages or duplicate contract
|
|
18
|
+
definitions.
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Coordination as Eyes and Ears for Agent Fleets
|
|
2
|
+
|
|
3
|
+
The design intent behind claims, presence, and stale-context — stated as one
|
|
4
|
+
picture so it can be argued about and built against, not re-derived each time
|
|
5
|
+
someone asks "how do the coordination agents work?"
|
|
6
|
+
|
|
7
|
+
## The thesis
|
|
8
|
+
|
|
9
|
+
Humans coordinating in a shared document already have everything they need:
|
|
10
|
+
they *see* each other's cursors, they *hover* to highlight the region they're
|
|
11
|
+
touching, and they *say* what they're doing ("I'm rewriting the intro"). Nobody
|
|
12
|
+
overwrites anybody because everybody has eyes and ears.
|
|
13
|
+
|
|
14
|
+
Agents don't. They work directly and silently, at machine speed, in fleets — say
|
|
15
|
+
100 agents across 10 groups, each group on its own area of the data. The
|
|
16
|
+
coordination layer's job is to give that fleet the same social awareness a room
|
|
17
|
+
of humans has, expressed as a protocol: **see who is working where, learn what
|
|
18
|
+
they are doing, and take a turn instead of a collision.**
|
|
19
|
+
|
|
20
|
+
At fleet scale the load-bearing property is that this stays *local*. The layer
|
|
21
|
+
does not lock "the fleet." It coordinates per row. 100 agents over 10
|
|
22
|
+
non-overlapping areas are 100 parallel tracks that only ever meet on the
|
|
23
|
+
handful of rows two agents genuinely both want. Cost is paid at the overlap, not
|
|
24
|
+
across the fleet — which is why adding agents on separate areas adds no
|
|
25
|
+
coordination cost.
|
|
26
|
+
|
|
27
|
+
## The one principle: two channels, never crossed
|
|
28
|
+
|
|
29
|
+
Awareness and safety are different channels, and keeping them apart is what makes
|
|
30
|
+
this safe *and* loop-free at machine speed.
|
|
31
|
+
|
|
32
|
+
- **Safety is pull, at write time.** An agent acts on its best read and its write
|
|
33
|
+
is rejected if the row moved underneath it. The rejection is the signal, and it
|
|
34
|
+
only ever fires when an agent actually chooses to write. A pull channel cannot
|
|
35
|
+
loop — nothing is being pushed.
|
|
36
|
+
- **Awareness is push, and it is the only channel that can storm.** So it is the
|
|
37
|
+
only channel we coalesce and rate-limit. Hot data that changes every
|
|
38
|
+
millisecond lives entirely on the safe *pull* side and therefore generates zero
|
|
39
|
+
awareness traffic — an agent that cares about a fast-ticking value just tries
|
|
40
|
+
its write and re-reads if it lost, rather than being woken on every tick.
|
|
41
|
+
|
|
42
|
+
Collapse the two channels — "notify every reader on every change" — and a
|
|
43
|
+
millisecond-ticking field produces read → notify → re-read → act → notify →
|
|
44
|
+
forever. Keeping them separate is the whole reason that loop can't form.
|
|
45
|
+
|
|
46
|
+
## The six behaviors
|
|
47
|
+
|
|
48
|
+
### 1. Eyes: see who is working where
|
|
49
|
+
|
|
50
|
+
Presence broadcasts, live, which agent holds which row. Before an agent commits
|
|
51
|
+
to work, it can see the area is already taken. This is advisory: it informs, it
|
|
52
|
+
forces nothing.
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
const who = ablo.tasks.claim.state({ id: 'task_123' }); // holder or null
|
|
56
|
+
// who.heldBy === 'agent:forecaster'
|
|
57
|
+
// who.description === 'rewriting the risk section to match Q3'
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### 2. Ears: learn *what* they are doing
|
|
61
|
+
|
|
62
|
+
A claim carries a single `description` — the machine version of the
|
|
63
|
+
hover-highlight plus the spoken "what I'm doing," in one field. It is the
|
|
64
|
+
sentence a peer reads to decide whether to wait, work elsewhere, or move on. It
|
|
65
|
+
defaults to `'editing'` when a claim is taken without one.
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
await using claim = await ablo.tasks.claim({
|
|
69
|
+
id: 'task_123',
|
|
70
|
+
description: 'rewriting the risk section to match Q3 numbers',
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 3. Reject *before* the tokens are spent
|
|
75
|
+
|
|
76
|
+
The claim is a **cheap pre-flight, taken before the generation, not before the
|
|
77
|
+
write.** A human wastes nothing by starting to type into a locked paragraph; an
|
|
78
|
+
agent wastes a whole expensive completion. So the discipline is:
|
|
79
|
+
|
|
80
|
+
```txt
|
|
81
|
+
claim (cheap) -> if granted: generate the block -> write
|
|
82
|
+
\-> if held: never generate anything
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
An agent that is told "no" at the claim never produced the write that would have
|
|
86
|
+
lost — the large token spend simply did not happen. This is the single most
|
|
87
|
+
important reason the claim exists before the work, not after it.
|
|
88
|
+
|
|
89
|
+
### 4. Reject *with* the description, so the blocked agent can decide
|
|
90
|
+
|
|
91
|
+
A bare "taken" forces a blind retry. The rejection carries the holder's
|
|
92
|
+
`description`, and the SDK renders it into the `AbloClaimedError` message:
|
|
93
|
+
*"Claimed by agent:forecaster: rewriting the risk section to match Q3."* So the
|
|
94
|
+
blocked agent reasons on real information: wait for the turn, go work somewhere
|
|
95
|
+
else, or drop the task because the work is already being done. "No, because
|
|
96
|
+
someone is rewriting the risk section" is actionable in a way "no" is not.
|
|
97
|
+
|
|
98
|
+
### 5. Queue: take a turn, with an opt-out if the line is long
|
|
99
|
+
|
|
100
|
+
Contention is a fair FIFO queue: the blocked agent waits its turn and is
|
|
101
|
+
*notified* the moment it arrives (push, not poll — it does not sit and spin).
|
|
102
|
+
When promoted, it re-reads so it works from the latest, with the previous
|
|
103
|
+
holder's change already in place. And the queue has an opt-out: past a depth
|
|
104
|
+
bound, an agent is told the area is too busy and moves on rather than joining a
|
|
105
|
+
long line.
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
await using claim = await ablo.tasks.claim({
|
|
109
|
+
id: 'task_123',
|
|
110
|
+
description: '...',
|
|
111
|
+
maxQueueDepth: 3, // don't join a line deeper than this
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### 6. Notify on change: without acting on stale data, without looping
|
|
116
|
+
|
|
117
|
+
An agent that read a row and is about to act on it is stopped if the row moved
|
|
118
|
+
since the read; it re-reads instead of acting stale. Where a genuine
|
|
119
|
+
notification is wanted, it is **coalesced** (one settled signal, not a stream)
|
|
120
|
+
and **relevance-gated** (only the fields a decision depends on can wake the
|
|
121
|
+
agent). A fast-ticking value never wakes anyone; a rarely-changing value that
|
|
122
|
+
matters can push one settled signal. Same primitive, two behaviors, chosen by
|
|
123
|
+
whether reacting is worth it — see the two-channel principle above.
|
|
124
|
+
|
|
125
|
+
## The three layers, as a rising scale
|
|
126
|
+
|
|
127
|
+
The behaviors above compose into three layers of increasing firmness. An agent
|
|
128
|
+
climbs only as high as the situation needs.
|
|
129
|
+
|
|
130
|
+
| Layer | Kind | What it does | Forces anything? |
|
|
131
|
+
| --- | --- | --- | --- |
|
|
132
|
+
| **Presence** | awareness (push) | Shows who holds what, and why, live. | No: informs only. |
|
|
133
|
+
| **Stale-context** | safety (pull) | Rejects a write built on a read the row has moved past. | Yes: at write time. |
|
|
134
|
+
| **Claim + queue** | reservation (push) | Reserves a row across a slow gap; contenders take turns. | Yes: mutual exclusion. |
|
|
135
|
+
|
|
136
|
+
Most work is a quick write and needs only the safety layer. An agent reaches for
|
|
137
|
+
a claim only when it will *hold* a row across a slow gap (read → LLM → write) —
|
|
138
|
+
the case where taking a turn beats colliding.
|
|
139
|
+
|
|
140
|
+
## What's shipped, and the one open point
|
|
141
|
+
|
|
142
|
+
One piece of the fleet story that once read as future design work is already
|
|
143
|
+
built; one is genuinely still open. Both are called out so neither is misjudged.
|
|
144
|
+
|
|
145
|
+
1. **Rich work surfaced at reject time — shipped.** A claim carries a single
|
|
146
|
+
`description` (behavior 2) as a first-class field on the wire. It rides the
|
|
147
|
+
presence broadcast, comes back inside the rejection's holder summary
|
|
148
|
+
(`heldByClaim`), and the SDK's `formatClaimedErrorMessage` renders it into the
|
|
149
|
+
`AbloClaimedError`. So "no" already becomes "no, because someone is rewriting
|
|
150
|
+
the risk section" (behavior 4) — the piece that prevents the wasteful blind
|
|
151
|
+
retry works today.
|
|
152
|
+
|
|
153
|
+
2. **Coalesced, relevance-gated notify — open.** The anti-loop guarantee
|
|
154
|
+
(behavior 6) depends on the awareness channel being coalesced and gated by
|
|
155
|
+
relevance, and on hot data staying on the pull side. This is the sharp one,
|
|
156
|
+
and it is the one not yet built: what exists is the write-time pull guard
|
|
157
|
+
(`onStale`) and operation-level batching, not a coalesced, relevance-gated
|
|
158
|
+
*push* on the presence broadcast. Get it wrong and a millisecond-ticking field
|
|
159
|
+
storms the fleet. The rule to hold: an agent is *rejected at write time* on
|
|
160
|
+
hot data, never *subscribed-and-woken* by it.
|
|
161
|
+
|
|
162
|
+
## Related
|
|
163
|
+
|
|
164
|
+
- [`coordination.md`](../coordination.md) — the public claim/queue/stale-context
|
|
165
|
+
reference this note motivates.
|
|
166
|
+
- [`agent-orchestration.md`](./agent-orchestration.md) — parent/child agent work
|
|
167
|
+
modeled through claimed job rows; this note is the coordination substrate under
|
|
168
|
+
it.
|
|
169
|
+
- ADR 0009 (`docs/decisions/0009-claim-durability-two-reclaim-clocks.md`) — what a
|
|
170
|
+
claim survives when a holder vanishes, and why liveness can be best-effort while
|
|
171
|
+
correctness is fenced at commit.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Agent Orchestration
|
|
2
|
+
|
|
3
|
+
Do not model parent and child agents as directly talking to each other over WebSocket.
|
|
4
|
+
|
|
5
|
+
Model them as actors coordinating through models:
|
|
6
|
+
|
|
7
|
+
```txt
|
|
8
|
+
parent creates job -> child claims job -> child commits result -> parent reads result
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The WebSocket is delivery infrastructure. The product model is shared state.
|
|
12
|
+
|
|
13
|
+
## Model Shape
|
|
14
|
+
|
|
15
|
+
A parent creates a job through its typed model client:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
const jobId = `forecast:${runId}`;
|
|
19
|
+
await ablo.agentJobs.create({
|
|
20
|
+
id: jobId,
|
|
21
|
+
idempotencyKey: `job:${runId}`,
|
|
22
|
+
data: {
|
|
23
|
+
status: 'open',
|
|
24
|
+
kind: 'forecast_report',
|
|
25
|
+
target: { model: 'weatherReports', id: 'report_stockholm', field: 'forecast' },
|
|
26
|
+
},
|
|
27
|
+
wait: 'confirmed',
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The child claims the job. If another worker holds it, the claim waits fairly,
|
|
32
|
+
then returns the fresh row:
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
await using claim = await ablo.agentJobs.claim({
|
|
36
|
+
id: jobId,
|
|
37
|
+
description: 'complete',
|
|
38
|
+
ttl: '5m',
|
|
39
|
+
});
|
|
40
|
+
const job = claim.data;
|
|
41
|
+
|
|
42
|
+
await ablo.agentJobs.update({
|
|
43
|
+
id: job.id,
|
|
44
|
+
data: {
|
|
45
|
+
status: 'completed',
|
|
46
|
+
result: { text },
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The child commits completion through the normal `update`, which is stale-guarded
|
|
52
|
+
under the held claim. The claim releases when its scope exits.
|
|
53
|
+
|
|
54
|
+
The parent retrieves the job result by model ID. Later, `ablo.events` can make that reactive, but the state model does not change.
|
|
55
|
+
|
|
56
|
+
## Rule
|
|
57
|
+
|
|
58
|
+
Nested agents should create or complete models. They should not require a separate agent-to-agent protocol for normal work.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Commit identifiers: the two axes
|
|
2
|
+
|
|
3
|
+
Maintainer reference. A commit carries several ids, and they are easy to
|
|
4
|
+
conflate because they travel together and are all "some number attached to a
|
|
5
|
+
write." They are not interchangeable. Each answers a different question, and
|
|
6
|
+
they split cleanly along **one line**: does this id help decide whether the
|
|
7
|
+
write *wins*, or does it only help *identify* the write after the fact?
|
|
8
|
+
|
|
9
|
+
Keeping the two axes separate is what lets each id be reasoned about — and
|
|
10
|
+
audited — on its own. This doc is the single place they sit side by side.
|
|
11
|
+
|
|
12
|
+
## The line
|
|
13
|
+
|
|
14
|
+
| axis | the question it answers | when it acts | if it's absent |
|
|
15
|
+
|---|---|---|---|
|
|
16
|
+
| **Conflict resolution** | *should this write land, given what else happened?* | at the commit chokepoint, before the row is written | the write is unguarded (last-writer-wins) |
|
|
17
|
+
| **Correlation / audit** | *which write is this, and have I seen it before?* | on receipt (dedup) and after the fact (attribution) | the write still lands; you just can't dedup or trace it as precisely |
|
|
18
|
+
|
|
19
|
+
A conflict-resolution id can **reject** a commit. A correlation id never does —
|
|
20
|
+
at most it makes a retried commit a no-op (idempotency). Never reach for one to
|
|
21
|
+
do the other's job: a correlation id can't fence a stale write, and a fence
|
|
22
|
+
can't dedup a retry.
|
|
23
|
+
|
|
24
|
+
## Axis 1: conflict resolution (does this write win?)
|
|
25
|
+
|
|
26
|
+
Evaluated inside `executeCommit`'s transaction, atomic with the delta write.
|
|
27
|
+
Three independent fences, each catching what the others can't; the full
|
|
28
|
+
narrative is [ADR 0009 §6](../../../../docs/decisions/0009-claim-durability-two-reclaim-clocks.md)
|
|
29
|
+
and the [coordination reference](../coordination.md).
|
|
30
|
+
|
|
31
|
+
| id | wire field | persisted to | what it asserts | rejects when |
|
|
32
|
+
|---|---|---|---|---|
|
|
33
|
+
| **read basis** | per-op `readAt` | `sync_deltas.read_at_sync_id` | "the state I reasoned **from**": a version watermark | the row moved since `readAt` (version-CAS), under `onStale: 'reject'` |
|
|
34
|
+
| **fencing token** | per-op `fenceToken` | `sync_deltas.fence_token` **and** `claim_fence_watermark.fence_token` | "the lease generation I was authorized **at**": a monotonic per-entity high-water | the token is below the entity's persisted high-water: a lapsed holder writing after its successor already claimed, wrote, and released |
|
|
35
|
+
| **claim / lease** | `claimId`, `heldBy` on the `WireClaim` | the coordination store (Redis), not `sync_deltas` | "I hold this row right now": live mutual exclusion | a non-holder writes a row another participant holds |
|
|
36
|
+
|
|
37
|
+
`onStale` (`notify` / `reject` / `overwrite`) is **not** an id — it's the
|
|
38
|
+
disposition that decides what a stale `readAt` *does*. It rides with the read
|
|
39
|
+
basis but is policy, not evidence, so it isn't persisted.
|
|
40
|
+
|
|
41
|
+
Why the token is a distinct id from `readAt`, and not just reused `sync_id`:
|
|
42
|
+
`readAt` advances on every **write** and asserts *from what data*; the token
|
|
43
|
+
advances on every **grant** and asserts *at what lease generation*. Their events
|
|
44
|
+
differ, so one can't stand in for the other — a lapsed holder that skips
|
|
45
|
+
version-CAS (no `readAt`, a blind write) is invisible to the read basis but
|
|
46
|
+
still carries a stale token. That is precisely fence (c) closing what (a) can't.
|
|
47
|
+
The reasoning in full lives in
|
|
48
|
+
[the fencing-token scope doc](../../../../docs/plans/claim-fencing-token-option-b-scope.md).
|
|
49
|
+
|
|
50
|
+
## Axis 2: correlation / audit (which write is this?)
|
|
51
|
+
|
|
52
|
+
Never decides a conflict. These are how a write is recognized — as a duplicate,
|
|
53
|
+
as your own echo, or as one row in a signed history.
|
|
54
|
+
|
|
55
|
+
| id | wire field | persisted to | purpose |
|
|
56
|
+
|---|---|---|---|
|
|
57
|
+
| **idempotency key** | batch `clientTxId` (public alias `idempotencyKey`) | dedup ledger keyed by it | a retried batch commits **once**: the second attempt is recognized and folded to a no-op, not re-applied |
|
|
58
|
+
| **per-op transaction id** | per-op `transactionId` | `sync_deltas.transaction_id` | echo detection: the broadcast delta arrives at the originating client carrying the **same** id its queue marked pending, so it reconciles its optimistic write instead of double-applying |
|
|
59
|
+
| **sync id** | assigned server-side (`next_sync_id`) | `sync_deltas.id` | the monotonic total order: the serialization order every reader tails and every `readAt` names. It is *assigned*, never client-supplied |
|
|
60
|
+
| **attribution** | actor / capability / delegation on the frame | `sync_deltas` actor columns + the signed audit chain | who acted, on whose behalf, under which key: the [audit log](../audit.md)'s who/when |
|
|
61
|
+
|
|
62
|
+
The batch key and the per-op id are deliberately separate: a multi-row commit is
|
|
63
|
+
**one** idempotent unit (one `clientTxId`) made of **many** individually-echoable
|
|
64
|
+
ops (each its own `transactionId`). Collapsing them would make echo detection
|
|
65
|
+
batch-coarse and break optimistic reconciliation for multi-op commits.
|
|
66
|
+
|
|
67
|
+
## The evidence tuple on a `sync_deltas` row
|
|
68
|
+
|
|
69
|
+
Both axes leave their mark on the delta, which is what makes a delta a complete,
|
|
70
|
+
self-describing audit record — you can reconstruct the full justification of a
|
|
71
|
+
write from the row alone, never from a live lease that has since vanished:
|
|
72
|
+
|
|
73
|
+
- **who:** `actor_id` / `capability_id` (+ the signed chain)
|
|
74
|
+
- **what:** `data` / `previous_data`
|
|
75
|
+
- **when:** `id` (`sync_id`) / `created_at`
|
|
76
|
+
- **from what known state:** `read_at_sync_id` (the read basis)
|
|
77
|
+
- **at what lease generation:** `fence_token` (the token the commit fenced)
|
|
78
|
+
- **as which client operation:** `transaction_id` (echo identity)
|
|
79
|
+
|
|
80
|
+
`read_at_sync_id` and `fence_token` are companions: the first records the data
|
|
81
|
+
version the write reasoned against, the second the lease generation it was
|
|
82
|
+
authorized at. Both are `NULL` when the write carried none (an unclaimed write, a
|
|
83
|
+
human `user` committer exempt under Law 7, or a legacy row) — **never fabricated
|
|
84
|
+
server-side**. The evidence derives only from what the write actually presented,
|
|
85
|
+
so the audit row can't drift from what the fence enforced.
|
|
86
|
+
|
|
87
|
+
## One-line test for "which id is this?"
|
|
88
|
+
|
|
89
|
+
> If removing it could turn an accepted commit into a rejected one, it's
|
|
90
|
+
> **Axis 1**. If removing it only costs you dedup, echo reconciliation, or
|
|
91
|
+
> traceability — while the write still lands — it's **Axis 2**.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Concurrency Convention: Open Decisions
|
|
2
|
+
|
|
3
|
+
Maintainer notes for [`concurrency-convention.md`](../concurrency-convention.md).
|
|
4
|
+
These are decisions the team has deliberately not made yet. They change public
|
|
5
|
+
behaviour, so they are tracked here rather than in the public contract, where an
|
|
6
|
+
unmade decision reads as an unsettled guarantee.
|
|
7
|
+
|
|
8
|
+
## Default disposition for agents
|
|
9
|
+
|
|
10
|
+
Should an agent-participant guarded write default to `notify` (philosophy
|
|
11
|
+
aligned: surface, do not overwrite) instead of `reject` (back-compat)?
|
|
12
|
+
|
|
13
|
+
The trade-off is alignment against a behaviour change for existing agent
|
|
14
|
+
callers. Today a guarded write with `readAt` but no `onStale` defaults to
|
|
15
|
+
`reject` for every participant kind.
|
|
16
|
+
|
|
17
|
+
## Batch premises through the policy seam
|
|
18
|
+
|
|
19
|
+
Should premise conflicts also pass through `ConflictPolicy`, or stay on the
|
|
20
|
+
direct `onStale` mapping?
|
|
21
|
+
|
|
22
|
+
Routing them through the seam requires a group-aware conflict shape, because a
|
|
23
|
+
batch premise can name a sync group rather than a row. Custom `ConflictPolicy`
|
|
24
|
+
functions currently see write-target conflicts only (`stale_context` /
|
|
25
|
+
`claim_held`); batch-premise conflicts resolve directly through each entry's
|
|
26
|
+
`onStale`.
|
|
27
|
+
|
|
28
|
+
## The serializability floor
|
|
29
|
+
|
|
30
|
+
A batch premise is a sound check, not a full precedence-graph guarantee: it
|
|
31
|
+
catches only what the caller declared. A caller that declares nothing gets no
|
|
32
|
+
check at all, because write-target checking needs a `readAt` to check against,
|
|
33
|
+
and a plain write is last-writer-wins.
|
|
34
|
+
|
|
35
|
+
The floor is therefore zero, and closing that gap is the subject of ADR 0018.
|
|
36
|
+
The public page states the resulting behaviour as a limit; the framing of it as
|
|
37
|
+
a gap to be closed belongs here.
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Data Source Reverse Channel (local-dev parity)
|
|
2
|
+
|
|
3
|
+
Maintainer scoping doc. Closes the one real day-one DX gap in Data Source
|
|
4
|
+
mode: the `commit`/`load`/`list` legs are inbound webhooks (Ablo → your
|
|
5
|
+
endpoint), so on `localhost` they need a tunnel (ngrok/cloudflared). This
|
|
6
|
+
scopes a built-in reverse channel so Data Source works on localhost the way
|
|
7
|
+
managed mode already does — the Stripe-CLI `stripe listen` pattern.
|
|
8
|
+
|
|
9
|
+
## The gap, precisely
|
|
10
|
+
|
|
11
|
+
Data Source has two directions today:
|
|
12
|
+
|
|
13
|
+
| Leg | Direction | Transport(s) today | localhost-friendly? |
|
|
14
|
+
|---|---|---|---|
|
|
15
|
+
| `events` (external writes → Ablo) | customer → Ablo | **poll** (`events` handler, Ablo calls you) **+ push** (`createPushQueue` → `POST /api/source/events`, you call Ablo) | ✅ yes, via push |
|
|
16
|
+
| `commit` / `load` / `list` | Ablo → customer | **inbound webhook only** (`dataSource()` route) | ❌ no: needs public URL |
|
|
17
|
+
|
|
18
|
+
The asymmetry is the whole bug. `events` already ships an outbound transport
|
|
19
|
+
(`src/source/pushQueue.ts`), so external writes reach Ablo from localhost
|
|
20
|
+
without a tunnel. The `commit`/`load`/`list` leg never got one, so Ablo Cloud
|
|
21
|
+
has no way to reach a `localhost:3000` dev server.
|
|
22
|
+
|
|
23
|
+
Inbound is exactly what localhost cannot receive. Managed mode has no inbound
|
|
24
|
+
leg (the browser/SDK opens the only connection, outbound to Ablo), which is why
|
|
25
|
+
managed mode "just works" locally and Data Source doesn't.
|
|
26
|
+
|
|
27
|
+
## Prior art
|
|
28
|
+
|
|
29
|
+
- **Stripe CLI `stripe listen`:** the canonical fix. The CLI opens an
|
|
30
|
+
*outbound* WebSocket to Stripe; Stripe drains webhook events down it and the
|
|
31
|
+
CLI forwards them to `localhost`. No public URL, no tunnel. We want the same
|
|
32
|
+
for the `commit`/`load`/`list` leg.
|
|
33
|
+
- **Our own `createPushQueue`:** already proves the outbound-from-customer
|
|
34
|
+
pattern for the `events` leg. The reverse channel is the symmetric primitive
|
|
35
|
+
for the other direction.
|
|
36
|
+
|
|
37
|
+
## Design
|
|
38
|
+
|
|
39
|
+
A customer-run **source connector** dials out to Ablo Cloud and serves
|
|
40
|
+
`commit`/`load`/`list` over the open connection, instead of Ablo making inbound
|
|
41
|
+
HTTP calls.
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
LOCAL DEV (no public URL)
|
|
45
|
+
|
|
46
|
+
ablo client ──ws──▶ Ablo Cloud ──┐
|
|
47
|
+
│ (no inbound HTTP to localhost)
|
|
48
|
+
customer connector ──ws (dial-out)──▶ Ablo Cloud
|
|
49
|
+
│ drains pending commit/load/list for this source
|
|
50
|
+
▼
|
|
51
|
+
dataSource(options) ← UNCHANGED handler, fed a synthesized Request
|
|
52
|
+
▼
|
|
53
|
+
local Postgres
|
|
54
|
+
│ signed response posted back up the same ws
|
|
55
|
+
└──────────────────────────────────────▶ Ablo Cloud ──ws──▶ ablo client
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Why it's small
|
|
59
|
+
|
|
60
|
+
`dataSource(options)` is already `(request: Request) => Promise<Response>` in
|
|
61
|
+
`src/source/factory.ts`. The connector does not reimplement any handler logic —
|
|
62
|
+
it:
|
|
63
|
+
|
|
64
|
+
1. Opens a WS to a new Ablo Cloud endpoint (e.g. `/v1/source/listen`),
|
|
65
|
+
authenticating with the project API key.
|
|
66
|
+
2. Registers which source/org it serves. Ablo Cloud routes that source's
|
|
67
|
+
`commit`/`load`/`list` requests to this socket instead of the configured
|
|
68
|
+
webhook URL (when a live connector is attached).
|
|
69
|
+
3. For each drained request frame: synthesize a `Request` with the same signed
|
|
70
|
+
headers Ablo would have sent, call the customer's existing `dataSource`
|
|
71
|
+
handler, and post the `Response` back up the socket.
|
|
72
|
+
|
|
73
|
+
Customer-side surface is one wrapper around the handler they already wrote:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
// dev only — same handler object as the deployed route
|
|
77
|
+
import { dataSource, createSourceConnector } from '@abloatai/ablo';
|
|
78
|
+
import { sourceOptions } from './ablo.source'; // shared with route.ts
|
|
79
|
+
|
|
80
|
+
const connector = createSourceConnector({
|
|
81
|
+
apiKey: process.env.ABLO_API_KEY!, // sk_test_*
|
|
82
|
+
handler: dataSource(sourceOptions), // the unchanged (Request)=>Response
|
|
83
|
+
});
|
|
84
|
+
await connector.run(abortSignal);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`route.ts` (deployed) and the connector (local) share the same
|
|
88
|
+
`sourceOptions` — zero handler drift.
|
|
89
|
+
|
|
90
|
+
### Server side (sync-server)
|
|
91
|
+
|
|
92
|
+
- New WS endpoint `/v1/source/listen`. Auth: project API key → resolves the
|
|
93
|
+
source. Reject if the key isn't `sk_test_*` unless the source explicitly
|
|
94
|
+
opts into reverse-channel for production (see "Production" below).
|
|
95
|
+
- Per-source request queue. When a `commit`/`load`/`list` needs the customer
|
|
96
|
+
and a connector is attached, enqueue + drain down the socket instead of
|
|
97
|
+
POSTing the webhook URL. Reuse the same signed-envelope shape so the
|
|
98
|
+
customer handler verifies identically (`verifyAbloSourceRequest` unchanged).
|
|
99
|
+
- Fallback: no connector attached → existing inbound webhook path. The
|
|
100
|
+
reverse channel is purely additive; nothing changes for deployed apps.
|
|
101
|
+
|
|
102
|
+
### Signature / security
|
|
103
|
+
|
|
104
|
+
- The drained frames carry the **same** Standard Webhooks signature
|
|
105
|
+
(`webhook-id`/`webhook-timestamp`/`webhook-signature`) computed with the
|
|
106
|
+
project key, so the connector verifies them through the existing
|
|
107
|
+
`verifyAbloSourceRequest` with no special-casing. The transport changes; the
|
|
108
|
+
trust model does not.
|
|
109
|
+
- Gate to `sk_test_*` by default. The DB still stays canonical in the
|
|
110
|
+
customer's process; nothing here gives Ablo the `DATABASE_URL`.
|
|
111
|
+
|
|
112
|
+
## Test-mode interplay
|
|
113
|
+
|
|
114
|
+
`SourceRequestContext.mode` (`src/source/types.ts`) already distinguishes
|
|
115
|
+
`test`/`live`. The reverse channel is the natural home for `mode: 'test'`
|
|
116
|
+
traffic: a local connector attached with an `sk_test_*` key receives the
|
|
117
|
+
source's test commits, runs them against the customer's test DB, and the SDK
|
|
118
|
+
sees confirmed rows + fan-out exactly as in production. This is the missing
|
|
119
|
+
piece that makes `sk_test_*` a complete local loop rather than just a data
|
|
120
|
+
namespace.
|
|
121
|
+
|
|
122
|
+
## Production stance
|
|
123
|
+
|
|
124
|
+
Keep the inbound webhook as the default deployed transport — it's lower
|
|
125
|
+
latency (no long-lived socket to babysit) and stateless. The reverse channel
|
|
126
|
+
is primarily the **dev** affordance. A secondary, opt-in use is a
|
|
127
|
+
"no-public-URL deploy" mode for customers who cannot expose an inbound
|
|
128
|
+
endpoint at all (locked-down VPCs); that's a follow-on, not the initial scope.
|
|
129
|
+
|
|
130
|
+
## Scope boundary (what this is NOT)
|
|
131
|
+
|
|
132
|
+
- Not a generic tunnel — it forwards only signed Ablo source frames for one
|
|
133
|
+
source, not arbitrary traffic.
|
|
134
|
+
- Not a change to the handler contract — `dataSource` is untouched; the
|
|
135
|
+
connector wraps its handler.
|
|
136
|
+
- Not a managed-mode change — managed mode has no inbound leg and is unaffected.
|
|
137
|
+
|
|
138
|
+
## Touch list (when built)
|
|
139
|
+
|
|
140
|
+
- `packages/transaction/src/source/connector.ts` — `createSourceConnector`
|
|
141
|
+
(dial-out WS client; synthesize Request → existing handler → post Response).
|
|
142
|
+
- `packages/transaction` export surface — expose `createSourceConnector` next to
|
|
143
|
+
`createPushQueue`.
|
|
144
|
+
- `apps/sync-server` — `/v1/source/listen` WS endpoint + per-source request
|
|
145
|
+
queue + "drain to connector if attached, else webhook" branch in the source
|
|
146
|
+
dispatch path.
|
|
147
|
+
- `docs/data-sources.md` — document the local-dev loop (the current docs only
|
|
148
|
+
describe the public-HTTPS webhook).
|
|
149
|
+
- Tests: connector round-trip (drained commit → handler → response), signature
|
|
150
|
+
parity with the webhook path, fallback-to-webhook when no connector attached.
|