@abloatai/ablo 0.35.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 -1929
- package/NOTICE +2 -2
- package/README.md +23 -532
- 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 -112
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -161
- 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 +2 -2
- package/docs/api-keys.md +13 -12
- package/docs/api.md +15 -53
- package/docs/audit.md +4 -3
- package/docs/cli.md +11 -11
- package/docs/client-behavior.md +9 -9
- package/docs/concurrency-convention.md +28 -42
- package/docs/coordination.md +228 -86
- package/docs/data-sources.md +5 -5
- package/docs/debugging.md +34 -12
- package/docs/deployment.md +8 -8
- 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/scoped-agent.md +3 -3
- package/docs/examples/server-agent.md +2 -2
- package/docs/groups.md +57 -3
- package/docs/guarantees.md +37 -10
- package/docs/how-it-works.md +32 -8
- package/docs/idempotency.md +6 -6
- package/docs/identity.md +24 -24
- package/docs/index.md +8 -8
- package/docs/integration-guide.md +38 -16
- 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 +9 -9
- package/docs/migration.md +37 -18
- package/docs/projects.md +1 -1
- package/docs/quickstart.md +2 -2
- package/docs/react.md +24 -13
- package/docs/schema-contract.md +3 -3
- package/docs/sessions.md +91 -37
- package/docs/webhooks.md +9 -9
- 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 +12 -12
- package/package.json +64 -174
- package/dist/BaseSyncedStore.d.ts +0 -823
- package/dist/BaseSyncedStore.js +0 -1955
- package/dist/Database.d.ts +0 -335
- package/dist/Database.js +0 -1500
- package/dist/InstanceCache.d.ts +0 -233
- package/dist/InstanceCache.js +0 -1164
- package/dist/LazyReferenceCollection.d.ts +0 -177
- package/dist/LazyReferenceCollection.js +0 -461
- package/dist/Model.d.ts +0 -444
- package/dist/Model.js +0 -909
- package/dist/ModelRegistry.d.ts +0 -221
- package/dist/ModelRegistry.js +0 -537
- package/dist/NetworkMonitor.d.ts +0 -26
- package/dist/NetworkMonitor.js +0 -77
- package/dist/RuntimeContext.d.ts +0 -52
- package/dist/RuntimeContext.js +0 -80
- package/dist/SyncClient.d.ts +0 -551
- package/dist/SyncClient.js +0 -2199
- 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/cli.cjs +0 -288600
- package/dist/client/Ablo.d.ts +0 -231
- package/dist/client/Ablo.js +0 -149
- 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 -46
- package/dist/client/createInternalComponents.js +0 -92
- package/dist/client/createModelProxy.d.ts +0 -228
- package/dist/client/createModelProxy.js +0 -818
- package/dist/client/humans.d.ts +0 -48
- package/dist/client/humans.js +0 -52
- 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 -48
- package/dist/client/reactiveEngine.js +0 -910
- 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/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 -29
- package/dist/context.js +0 -58
- 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/DatabaseManager.d.ts +0 -105
- package/dist/core/DatabaseManager.js +0 -387
- package/dist/core/QueryProcessor.d.ts +0 -75
- package/dist/core/QueryProcessor.js +0 -255
- package/dist/core/QueryView.d.ts +0 -79
- package/dist/core/QueryView.js +0 -218
- package/dist/core/StoreManager.d.ts +0 -112
- package/dist/core/StoreManager.js +0 -302
- package/dist/core/ViewRegistry.d.ts +0 -20
- package/dist/core/ViewRegistry.js +0 -55
- package/dist/core/index.d.ts +0 -33
- package/dist/core/index.js +0 -48
- package/dist/core/openIDBWithTimeout.d.ts +0 -65
- package/dist/core/openIDBWithTimeout.js +0 -153
- package/dist/core/queryUtils.d.ts +0 -45
- package/dist/core/queryUtils.js +0 -69
- package/dist/core/storeContract.d.ts +0 -145
- package/dist/core/storeContract.js +0 -12
- package/dist/docs/catalog.d.ts +0 -72
- package/dist/docs/catalog.js +0 -227
- 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/client.d.ts +0 -61
- package/dist/query/client.js +0 -137
- package/dist/query/types.d.ts +0 -85
- package/dist/query/types.js +0 -16
- package/dist/react/AbloProvider.d.ts +0 -230
- package/dist/react/AbloProvider.js +0 -455
- 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/index.d.ts +0 -61
- package/dist/react/index.js +0 -66
- package/dist/react/internalContext.d.ts +0 -33
- package/dist/react/internalContext.js +0 -3
- package/dist/react/useAblo.d.ts +0 -75
- package/dist/react/useAblo.js +0 -102
- 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 -129
- 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 -41
- package/dist/schema/index.js +0 -76
- 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 -87
- package/dist/schema/serialize.d.ts +0 -115
- package/dist/schema/serialize.js +0 -262
- 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 -81
- package/dist/source/adapter.js +0 -22
- 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/footprint.d.ts +0 -111
- package/dist/source/footprint.js +0 -0
- 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/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/SyncActionStore.d.ts +0 -97
- package/dist/stores/SyncActionStore.js +0 -504
- 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 -280
- package/dist/sync/BootstrapFetcher.js +0 -962
- package/dist/sync/ConnectionManager.d.ts +0 -8
- package/dist/sync/ConnectionManager.js +0 -8
- package/dist/sync/OnDemandLoader.d.ts +0 -228
- package/dist/sync/OnDemandLoader.js +0 -742
- 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 -70
- 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 -113
- package/dist/sync/deltaPipeline.js +0 -261
- package/dist/sync/groupChange.d.ts +0 -113
- package/dist/sync/groupChange.js +0 -242
- package/dist/sync/participants.d.ts +0 -115
- package/dist/sync/participants.js +0 -344
- 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 -70
- 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/testing/fixtures/bootstrap.d.ts +0 -49
- package/dist/testing/fixtures/bootstrap.js +0 -59
- package/dist/testing/fixtures/deltas.d.ts +0 -83
- package/dist/testing/fixtures/deltas.js +0 -136
- package/dist/testing/fixtures/httpResponses.d.ts +0 -70
- package/dist/testing/fixtures/httpResponses.js +0 -90
- package/dist/testing/fixtures/models.d.ts +0 -83
- package/dist/testing/fixtures/models.js +0 -272
- package/dist/testing/helpers/reactWrapper.d.ts +0 -69
- package/dist/testing/helpers/reactWrapper.js +0 -67
- package/dist/testing/helpers/syncEngineHarness.d.ts +0 -54
- package/dist/testing/helpers/syncEngineHarness.js +0 -73
- package/dist/testing/helpers/wait.d.ts +0 -30
- package/dist/testing/helpers/wait.js +0 -49
- package/dist/testing/index.d.ts +0 -23
- package/dist/testing/index.js +0 -33
- package/dist/testing/mocks/FakeDatabase.d.ts +0 -18
- package/dist/testing/mocks/FakeDatabase.js +0 -10
- package/dist/testing/mocks/MockMutationExecutor.d.ts +0 -87
- package/dist/testing/mocks/MockMutationExecutor.js +0 -186
- package/dist/testing/mocks/MockNetworkMonitor.d.ts +0 -20
- package/dist/testing/mocks/MockNetworkMonitor.js +0 -46
- package/dist/testing/mocks/MockSyncContext.d.ts +0 -51
- package/dist/testing/mocks/MockSyncContext.js +0 -72
- package/dist/testing/mocks/MockSyncStore.d.ts +0 -88
- package/dist/testing/mocks/MockSyncStore.js +0 -171
- package/dist/testing/mocks/MockWebSocket.d.ts +0 -71
- package/dist/testing/mocks/MockWebSocket.js +0 -118
- 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 -177
- package/dist/transaction/auth/capability.js +0 -199
- 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 -49
- package/dist/transaction/coordination/awaitClaimGrant.js +0 -112
- package/dist/transaction/coordination/claimHeartbeatLoop.d.ts +0 -50
- package/dist/transaction/coordination/claimHeartbeatLoop.js +0 -88
- 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 -44
- package/dist/transaction/coordination/locator.d.ts +0 -83
- package/dist/transaction/coordination/locator.js +0 -82
- package/dist/transaction/coordination/schema.d.ts +0 -1473
- package/dist/transaction/coordination/schema.js +0 -1013
- package/dist/transaction/coordination/targetConflict.d.ts +0 -2
- package/dist/transaction/coordination/targetConflict.js +0 -103
- 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 -480
- package/dist/transaction/errors.d.ts +0 -428
- package/dist/transaction/errors.js +0 -686
- 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 -192
- package/dist/transaction/plugin.js +0 -87
- 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 -266
- package/dist/transaction/resources/httpResources.js +0 -7
- package/dist/transaction/resources/modelOperations.d.ts +0 -319
- 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 -85
- package/dist/transaction/resources/where.js +0 -70
- 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 -126
- package/dist/transaction/schema/field.js +0 -265
- 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 -57
- package/dist/transaction/schema/openapi.js +0 -340
- package/dist/transaction/schema/relation.d.ts +0 -199
- 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 -324
- package/dist/transaction/schema/schema.js +0 -305
- 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 -123
- package/dist/transaction/transport/httpClient.js +0 -145
- 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 -1276
- 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 -576
- package/dist/transaction/transport/wsTransport.js +0 -1017
- 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 -540
- 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 -25
- package/dist/transaction/utils/duration.js +0 -45
- 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 -351
- package/dist/transaction/wire/accountResponses.js +0 -255
- package/dist/transaction/wire/auth.d.ts +0 -49
- package/dist/transaction/wire/auth.js +0 -57
- 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 -463
- package/dist/transaction/wire/claims.js +0 -229
- 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 -177
- package/dist/transaction/wire/feedEvent.js +0 -39
- 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 -552
- package/dist/transaction/wire/inboundFrames.js +0 -116
- package/dist/transaction/wire/index.d.ts +0 -50
- package/dist/transaction/wire/index.js +0 -74
- package/dist/transaction/wire/listEnvelope.d.ts +0 -37
- package/dist/transaction/wire/listEnvelope.js +0 -42
- package/dist/transaction/wire/modelResponses.d.ts +0 -85
- package/dist/transaction/wire/modelResponses.js +0 -43
- 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 -655
- package/dist/transactions/mutations/MutationQueue.js +0 -2797
- 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 -164
- package/dist/transactions/mutations/commitPayload.js +0 -152
- package/dist/transactions/mutations/deltaConfirmation.d.ts +0 -59
- package/dist/transactions/mutations/deltaConfirmation.js +0 -233
- 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 -186
- package/dist/transactions/mutations/replayValidation.js +0 -163
- package/dist/utils/mobxSetup.d.ts +0 -53
- package/dist/utils/mobxSetup.js +0 -330
- 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
- package/docs/interaction-model.md +0 -99
package/docs/identity.md
CHANGED
|
@@ -24,9 +24,9 @@ that.
|
|
|
24
24
|
A **sync group** is a named channel of shared state — a string like
|
|
25
25
|
`org:acme` or `workspace:abc123`. It is simultaneously:
|
|
26
26
|
|
|
27
|
-
- **the unit of fan-out
|
|
27
|
+
- **the unit of fan-out:** a confirmed write to a row publishes a delta to
|
|
28
28
|
every participant subscribed to that row's sync group(s), and
|
|
29
|
-
- **the unit of access
|
|
29
|
+
- **the unit of access:** a participant receives a row's deltas *only if* the
|
|
30
30
|
row's sync group is in their allowed set.
|
|
31
31
|
|
|
32
32
|
There is no built-in `org` / `team` / `user` concept in the engine. Those are
|
|
@@ -101,16 +101,16 @@ const ablo = Ablo({ schema, apiKey: session.token });
|
|
|
101
101
|
|
|
102
102
|
That's the whole surface. The rest of this doc is the *why* behind each line.
|
|
103
103
|
|
|
104
|
-
## Two kinds of group
|
|
104
|
+
## Two kinds of group: the whole mental model
|
|
105
105
|
|
|
106
106
|
You just saw a human get `org` / `team` groups and an agent get one `workspace`
|
|
107
107
|
group. That split is the model. Every sync group is named after one of two
|
|
108
108
|
things:
|
|
109
109
|
|
|
110
|
-
- **Membership groups
|
|
110
|
+
- **Membership groups:** named after *who you are*: `org:{id}`, `team:{id}`,
|
|
111
111
|
`user:{id}`. Produced from **identity** (`identityRoles`, Half 1). They're
|
|
112
112
|
standing and durable — they don't change as you work.
|
|
113
|
-
- **Entity groups
|
|
113
|
+
- **Entity groups:** named after *a thing*: `dataroom:{id}`, `workspace:{id}`,
|
|
114
114
|
`document:{id}`. Produced from a **row's id** (a model's entity scope, Half 2).
|
|
115
115
|
They're granular — one per record — and any participant can be pointed at a
|
|
116
116
|
specific set of them.
|
|
@@ -122,8 +122,8 @@ they are, so you declare them once in the schema.
|
|
|
122
122
|
|
|
123
123
|
| | Subscribed by | Declared where | Gets |
|
|
124
124
|
| --- | --- | --- | --- |
|
|
125
|
-
| **Human** | *who they are
|
|
126
|
-
| **Agent** | *what it's been given
|
|
125
|
+
| **Human** | *who they are*: membership | **the schema** (`identityRoles`): a rule, written once | every `org` / `team` / `user` group their identity implies: their whole standing world |
|
|
126
|
+
| **Agent** | *what it's been given*: entities | **code, at the spawn site**: chosen per run | a handful of entity groups: the dataroom it's in, the documents it has read: never beyond what its user's membership could reach |
|
|
127
127
|
|
|
128
128
|
> **One line:** humans subscribe by who they are; agents subscribe by what
|
|
129
129
|
> they've been given.
|
|
@@ -180,6 +180,7 @@ await mintUserSessionKey({
|
|
|
180
180
|
organizationId: schemaOwnerOrgId,
|
|
181
181
|
projectId: schemaProjectId,
|
|
182
182
|
},
|
|
183
|
+
operations: ['task.read', 'task.update'],
|
|
183
184
|
ttlSeconds: 3600,
|
|
184
185
|
});
|
|
185
186
|
```
|
|
@@ -197,7 +198,7 @@ Scoping is two declarations that meet in the middle. One describes the
|
|
|
197
198
|
(which group does this row belong to?). A participant sees a row **iff** the
|
|
198
199
|
row's sync group is in the participant's allowed set.
|
|
199
200
|
|
|
200
|
-
### Half 1
|
|
201
|
+
### Half 1 (`identityRoles`): identity → allowed groups
|
|
201
202
|
|
|
202
203
|
Declared once, on the schema, via the `identityRole({ kind, source })` factory.
|
|
203
204
|
Each role is **pure data**: a `kind` (the group's prefix — `org`, `user`, `team`)
|
|
@@ -238,7 +239,7 @@ in-process and on a hosted server that only ever sees the compiled JSON.
|
|
|
238
239
|
> `user:{id}` role above already covers it — see
|
|
239
240
|
> [Agents are participants too](#agents-are-participants-too).
|
|
240
241
|
|
|
241
|
-
### Half 2
|
|
242
|
+
### Half 2 (per-model scope): row → group
|
|
242
243
|
|
|
243
244
|
You never write a sync-group string for a row. You declare a model's *place* in
|
|
244
245
|
the entity graph and the engine derives the groups its rows fan out on. Three
|
|
@@ -308,9 +309,9 @@ exposed the whole table cross-tenant, so `validate_schema` rejects the removed
|
|
|
308
309
|
options as `tenancy-option-removed` errors and steers you to `policy: { by:
|
|
309
310
|
'parent' }` (FK inheritance) or, for genuinely global reference data, the
|
|
310
311
|
explicit `policy: { by: 'none' }`. See
|
|
311
|
-
`packages/
|
|
312
|
+
`packages/transaction/src/schema/model.ts` for the full option set.
|
|
312
313
|
|
|
313
|
-
## How identity reaches Ablo
|
|
314
|
+
## How identity reaches Ablo: the proxy model
|
|
314
315
|
|
|
315
316
|
This is the part the README's "authenticates with the signed-in user's
|
|
316
317
|
session" glossed over. Concretely:
|
|
@@ -400,9 +401,9 @@ What carries identity — and just as importantly, what does *not* set the bound
|
|
|
400
401
|
|
|
401
402
|
| Where | Purpose |
|
|
402
403
|
| ------------ | ------------------------------------------------------------------------------------------------ |
|
|
403
|
-
| `userId` prop | App-level participant id, used for app-owned fields and read by your `identityRole` `source`. **Not** the security boundary
|
|
404
|
+
| `userId` prop | App-level participant id, used for app-owned fields and read by your `identityRole` `source`. **Not** the security boundary: the server enforces scope from the authenticated request. |
|
|
404
405
|
| `teamIds` (on the client) | Team ids expanded into team sync groups via your `identityRoles`. |
|
|
405
|
-
| `syncGroups` (at session mint) | Optional. **Narrows** a minted session's subscription to a subset of what auth already allows
|
|
406
|
+
| `syncGroups` (at session mint) | Optional. **Narrows** a minted session's subscription to a subset of what auth already allows: it can never widen it. Passed to `sessions.create({ user \| agent, syncGroups })`; build entries with `syncGroup(kind, id)`. Use it to scope an agent (or a focused page's session) to one entity, e.g. `[syncGroup('workspace', 'abc123')]`. |
|
|
406
407
|
|
|
407
408
|
Because the server is the boundary, a client that changes `userId` to another
|
|
408
409
|
user's id does not gain their data — the server resolves and enforces the real
|
|
@@ -431,7 +432,7 @@ agent authority = (triggering user's allowed set) ← ceiling, inherited (on-
|
|
|
431
432
|
```
|
|
432
433
|
|
|
433
434
|
Concretely: each model an agent edits declares a `scope`
|
|
434
|
-
([Half 2](#half-2
|
|
435
|
+
([Half 2](#half-2-per-model-scope-row--group)), so each row forms its own
|
|
435
436
|
group. The agent subscribes only to the groups for the rows it touches. Declare
|
|
436
437
|
an entity anchor on the models an agent operates on:
|
|
437
438
|
|
|
@@ -475,22 +476,21 @@ not *what's reachable*.
|
|
|
475
476
|
Three rules make agent access safe, and they fall out of the model above rather
|
|
476
477
|
than needing a separate agent permission system:
|
|
477
478
|
|
|
478
|
-
- **Inherit the user, and no more
|
|
479
|
+
- **Inherit the user, and no more:** the OAuth
|
|
479
480
|
[on-behalf-of](https://workos.com/blog/oauth-on-behalf-of-ai-agents) model: the
|
|
480
481
|
agent's reach is tied to the consenting user, never the org.
|
|
481
|
-
- **Least privilege, just-in-time
|
|
482
|
+
- **Least privilege, just-in-time:** scoped to the task's entities, not standing
|
|
482
483
|
org-wide access (the over-privilege pattern
|
|
483
484
|
[OWASP's NHI Top 10](https://www.token.security/assets/the-ultimate-non-human-identity-security-guide)
|
|
484
485
|
flags as the dominant agent risk).
|
|
485
|
-
- **Dual-principal attribution
|
|
486
|
+
- **Dual-principal attribution:** record both the executing agent and the
|
|
486
487
|
triggering human.
|
|
487
488
|
|
|
488
489
|
Identity is 1:1 with a human participant; authority is narrowed to the work. That
|
|
489
490
|
split is what lets Ablo keep *one model API for every actor* without ever
|
|
490
491
|
granting an agent standing access to everything its user can see. The agent that
|
|
491
|
-
runs the [
|
|
492
|
-
|
|
493
|
-
it claimed.
|
|
492
|
+
runs the [Coordination](./coordination.md) `claim` loop is, to the scoping layer,
|
|
493
|
+
that same participant — scoped to the row it claimed.
|
|
494
494
|
|
|
495
495
|
## Narrowing to specific entities
|
|
496
496
|
|
|
@@ -530,7 +530,7 @@ an agent pointed at the entities it's working on. You **never hand-write**
|
|
|
530
530
|
|
|
531
531
|
> **`groups.root` is the schema model option, not a client setting.**
|
|
532
532
|
> `groups: { root: 'workspace' }` in `model(...)` declares a scope root
|
|
533
|
-
> ([Half 2](#half-2
|
|
533
|
+
> ([Half 2](#half-2-per-model-scope-row--group)) — it names the group
|
|
534
534
|
> (`workspace:<id>`) that the mechanisms above then subscribe to.
|
|
535
535
|
> There is no `Ablo({ scope })` constructor option. The lifecycle filter on
|
|
536
536
|
> [`list()`](./api.md#model-methods) is a separate axis named **`state`**
|
|
@@ -543,7 +543,7 @@ an agent pointed at the entities it's working on. You **never hand-write**
|
|
|
543
543
|
> can't reach a workspace its capability doesn't already permit, no matter what it
|
|
544
544
|
> passes. Smaller bootstrap, less fan-out, same server-enforced boundary.
|
|
545
545
|
|
|
546
|
-
## How this compares
|
|
546
|
+
## How this compares, and the best practices it follows
|
|
547
547
|
|
|
548
548
|
Ablo's identity model is not novel; it's the convergent answer every serious
|
|
549
549
|
realtime / sync SDK arrived at. Knowing which industry pattern it *is* tells you
|
|
@@ -551,7 +551,7 @@ how to reason about it.
|
|
|
551
551
|
|
|
552
552
|
**Realtime authorization splits into two shapes.** Ablo is firmly in the first:
|
|
553
553
|
|
|
554
|
-
- **Server derives scope from authenticated identity
|
|
554
|
+
- **Server derives scope from authenticated identity:** the server decides what
|
|
555
555
|
a participant may read/write and the client cannot override it. This is Ablo's
|
|
556
556
|
proxy model. It's the same shape as
|
|
557
557
|
[Supabase Realtime's RLS-on-connect](https://supabase.com/docs/guides/realtime/authorization)
|
|
@@ -560,7 +560,7 @@ how to reason about it.
|
|
|
560
560
|
checks the permissions for you" — recommended for production), and
|
|
561
561
|
[ElectricSQL **proxy auth**](https://electric-sql.com/docs/guides/auth) (a
|
|
562
562
|
reverse-proxy sets shape params server-side before forwarding).
|
|
563
|
-
- **Client proposes, server authorizes the exact request
|
|
563
|
+
- **Client proposes, server authorizes the exact request:** the client names
|
|
564
564
|
the room/shape and the server signs off, as in
|
|
565
565
|
[Pusher's channel authorization endpoint](https://pusher.com/docs/channels/server_api/authorizing-users/),
|
|
566
566
|
[ElectricSQL **gatekeeper auth**](https://github.com/electric-sql/electric/blob/main/examples/gatekeeper-auth/README.md),
|
package/docs/index.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Ablo Docs
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Collaboration infrastructure for AI agents: one API for agents, apps, and services to claim, change, and confirm the same rows.
|
|
4
4
|
|
|
5
5
|
Two agents reach for the same row. One claims it, does slow work — an LLM call,
|
|
6
6
|
a fetch, a chain of tools — and commits. The second is neither rejected nor
|
|
@@ -110,12 +110,12 @@ Every surface reaches the same coordinated state. Pick by who is calling.
|
|
|
110
110
|
|
|
111
111
|
| Surface | Use it for |
|
|
112
112
|
|---|---|
|
|
113
|
-
| **SDK
|
|
114
|
-
| **Coordination MCP
|
|
115
|
-
| **`humans()
|
|
116
|
-
| **CLI
|
|
117
|
-
| **REST
|
|
118
|
-
| **Integration-helper MCP
|
|
113
|
+
| **SDK**: `@abloatai/ablo`, `transport: 'http'` | The agents themselves. Stateless, request/response, nothing held open. The main path. |
|
|
114
|
+
| **Coordination MCP**: `@abloatai/mcp` | An agent living inside an MCP host that needs claim and commit as tools. A data plane. |
|
|
115
|
+
| **`humans()`**: with `@abloatai/ablo/react` | The interfaces a person watches agent work arrive in: presence, live queries, a local copy. |
|
|
116
|
+
| **CLI**: `ablo` | Scaffolding, schema push, connecting a database. Terminals and CI. |
|
|
117
|
+
| **REST**: `/api/v1` | Runtimes with no SDK. |
|
|
118
|
+
| **Integration-helper MCP**: hosted `/api/mcp` | Teaching a coding assistant the SDK while you build. Docs, lint, and scaffolds only. |
|
|
119
119
|
|
|
120
120
|
The two MCP servers are not interchangeable. The coordination server changes
|
|
121
121
|
your data; the integration-helper server serves documentation and has no
|
|
@@ -136,6 +136,7 @@ default caller, not a special one.
|
|
|
136
136
|
|
|
137
137
|
## Concepts
|
|
138
138
|
|
|
139
|
+
- [How Ablo Works](./how-it-works.md) — the mental model in one page: you write through Ablo, it lands in your Postgres, the write-ahead log confirms it. **Read this first.**
|
|
139
140
|
- [Coordination](./coordination.md) — `claim`, `claim.state`, and `claim.queue`: who holds a row, and who is waiting.
|
|
140
141
|
- [Concurrency Convention](./concurrency-convention.md) — the governing rule for how concurrent writes resolve.
|
|
141
142
|
- [Guarantees](./guarantees.md) — what a confirmed write, a stale-write rejection, and a claim each promise.
|
|
@@ -144,7 +145,6 @@ default caller, not a special one.
|
|
|
144
145
|
- [Agents](./agents.md) — the stateless participant: wake, read, claim, commit, idle.
|
|
145
146
|
- [Agent Messaging](./agent-messaging.md) — durable handoffs between agents, linked to the claim they discuss.
|
|
146
147
|
- [Identity & Sync Groups](./identity.md) — who is connecting, and which slice of state they see.
|
|
147
|
-
- [Interaction Model](./interaction-model.md) — the schema, claim, update, confirmation loop.
|
|
148
148
|
- [Change Propagation](./groups.md) — how one row's change reaches the actors that depend on it.
|
|
149
149
|
- [Client Behavior](./client-behavior.md) — options, errors, retries, timeouts, and imports.
|
|
150
150
|
|
|
@@ -11,7 +11,7 @@ backend and a database, one model at a time.
|
|
|
11
11
|
|
|
12
12
|
Three things hold no matter which actor is writing:
|
|
13
13
|
|
|
14
|
-
- **One model API for every actor
|
|
14
|
+
- **One model API for every actor:** `ablo.<model>.update(...)` is what
|
|
15
15
|
React components, server actions, background workers, and AI agents
|
|
16
16
|
all call. No separate "agent SDK," no parallel mutation path. The
|
|
17
17
|
attribution comes from the credential, not the call site.
|
|
@@ -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>;
|
|
@@ -351,8 +362,19 @@ server -> ablo.weatherReports.update(...)
|
|
|
351
362
|
Ablo coordinates those writes, fans out confirmed deltas, exposes active claims,
|
|
352
363
|
and lets callers reject stale writes with `readAt`.
|
|
353
364
|
|
|
354
|
-
|
|
355
|
-
|
|
365
|
+
A write that reaches your database some other way still reaches connected
|
|
366
|
+
clients. A `psql` session, a cron job, an admin tool, a legacy endpoint: Ablo
|
|
367
|
+
tails your write-ahead log, so a change it did not make is picked up and fanned
|
|
368
|
+
out like any other, attributed to the data source rather than to an agent.
|
|
369
|
+
|
|
370
|
+
What such a write does not get is the coordination. It never entered the commit
|
|
371
|
+
chokepoint, so no claim was checked, no `readAt` was compared, and no idempotency
|
|
372
|
+
key was honoured. It can land on top of a row an agent is holding. Route anything
|
|
373
|
+
that must respect a claim through `ablo.<model>`.
|
|
374
|
+
|
|
375
|
+
On the [Data Source endpoint fallback](./data-sources.md) there is no replication
|
|
376
|
+
stream to tail, and there the original caveat holds: a direct write stays
|
|
377
|
+
invisible until your app reports it through Data Source events.
|
|
356
378
|
|
|
357
379
|
## 6. Existing API Backend
|
|
358
380
|
|
|
@@ -376,7 +398,7 @@ The migration can be gradual:
|
|
|
376
398
|
|
|
377
399
|
1. Declare schema for one model, such as `reports`.
|
|
378
400
|
2. Keep existing server loads for first paint.
|
|
379
|
-
3. Add `useAblo((ablo) => ablo.weatherReports.local.
|
|
401
|
+
3. Add `useAblo((ablo) => ablo.weatherReports.local.get(id)) ?? serverReport` for live rows.
|
|
380
402
|
4. Add one Data Source endpoint that calls the existing service layer.
|
|
381
403
|
5. Move one mutation button from `fetch('/api/reports/...')` to `ablo.weatherReports.update(...)`.
|
|
382
404
|
6. Add an outbox/events path for writes that still happen outside Ablo.
|
|
@@ -505,9 +527,9 @@ them.
|
|
|
505
527
|
|
|
506
528
|
| Method | Use it for |
|
|
507
529
|
| -------------------------------------- | -------------------------------------------------------------------------------- |
|
|
508
|
-
| `
|
|
530
|
+
| `get({ id })` | Async read of one row from the server (await it). |
|
|
509
531
|
| `list({ where })` | Async read of many rows from the server (await it). |
|
|
510
|
-
| `get(id)`
|
|
532
|
+
| `local.get(id)` | Synchronous local read of one synced row (use in render). |
|
|
511
533
|
| `local.list({ where })` | Synchronous local read of many synced rows. |
|
|
512
534
|
| `local.count({ where })` | Synchronous local count of synced rows. |
|
|
513
535
|
| `create({ data, id? })` | Create through the model client. |
|
|
@@ -517,5 +539,5 @@ them.
|
|
|
517
539
|
| `claim({ id, description?, ttl? })` | Acquire a disposable handle: wait for your turn, re-read, and hold the row. |
|
|
518
540
|
|
|
519
541
|
Keep first integrations on the model methods above. Every mutation and
|
|
520
|
-
server-read verb takes one options object;
|
|
542
|
+
server-read verb takes one options object; the synchronous `local.get(id)` stays
|
|
521
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.
|