Skip to main content

Reactor contribution points

Every service the SDK lets a host replace is a Datalayer Reactor contribution point. Python and TypeScript define the same points, with the same ids and contracts (members spelled snake_case in Python, camelCase in TypeScript), listed in protocol/reactor-points.json. Each SDK's tests hold it to that file, so a point added to one SDK and not the other fails CI. One point is for Python hosts only, and the file says so (typescript: null): pap.approvalSurface gates the live-session actions, which only the Python SDK registers.

Resolution​

  • A lower order wins. The SDK's own defaults contribute at order 100, so any host contribution with a lower order replaces them.
  • Two contributions at the winning order are ambiguous and fail closed: the SDK refuses rather than pick one by load order.
  • A point with no default and no contribution fails closed when the SDK needs it (no active … contribution). pap.company.api and pap.company.mcp are optional: with none, the company publishes no API or MCP server. So are pap.telemetry (with none, nothing is emitted) and pap.approvalSurface (with none, destructive live-session actions are refused).
  • pap.company.operationExecutor resolves per action: the lowest-order executor whose action names the operation's performs it; two at that order leave the operation in_progress instead of performing it twice.
  • Contributions follow their plugin: disabling or unregistering a plugin removes what it contributed, and the SDK reads each point when it needs it, never caching a provider.

Personal agent​

PointPythonTypeScriptResponsibilitySDK defaultTenant-scoped
pap.discoveryTransportDISCOVERY_TRANSPORTSDiscoveryTransportsCredential-free, bounded public metadata transporthttpx / fetch
pap.urlSafetyPolicyURL_SAFETY_POLICIESUrlSafetyPoliciesHTTPS, DNS, IP, and redirect policy for metadata URLspublic
pap.clockCLOCKSClocksTime for every expiring protocol statesystem
pap.randomSourceRANDOM_SOURCESRandomSourcesCryptographically secure randomnesssystem
pap.signerSIGNERSSignersThe agent's client and Session assertions (keys stay in custody)— (host contributes)
pap.tokenStoreTOKEN_STORESTokenStoresAccount Tokens behind opaque references— (host contributes)
pap.pairwiseUserIdStorePAIRWISE_USER_ID_STORESPairwiseUserIdStoresStable pairwise user IDs by tenant, local user, and issuer— (host contributes)yes
pap.authorizationStateStoreAUTHORIZATION_STATE_STORESAuthorizationStateStoresExpiring one-use sign-in state (put, take)— (host contributes)
pap.cacheCACHESCachesBounded cache of public metadatamemory
pap.browserHandoffBROWSER_HANDOFFSBrowserHandoffsOpens a sign-in page on a trusted surface— (host contributes)
pap.browserFormPostBROWSER_FORM_POSTSBrowserFormPostsPosts a browser-session assertion from the user's browser— (host contributes)
pap.dpopProofProviderDPOP_PROOF_PROVIDERSDpopProofProvidersFresh DPoP proofs, private keys kept in custody— (host contributes)yes
pap.accountTokenLockACCOUNT_TOKEN_LOCKSAccountTokenLocksSerializes Account Token rotationin-process
pap.protectedInputPROTECTED_INPUTSProtectedInputsCollects credentials and codes outside the model— (host contributes)
pap.retryPolicyRETRY_POLICIESRetryPoliciesWhen a refused request is retrieddefault
pap.rateLimitBudgetRATE_LIMIT_BUDGETSRateLimitBudgetsPer-company request budget shared across tasksin-process
pap.standingPermissionStoreSTANDING_PERMISSION_STORESStandingPermissionStoresReserves, commits, and releases standing permissions— (host contributes)
pap.telemetryTELEMETRY_SINKSTelemetrySinksReceives each finished protocol stage, redacted by construction— (feature off)
pap.approvalSurfaceAPPROVAL_SURFACES— (Python hosts)The user's approval of a destructive live-session action— (refused without)

The discovery transport follows each language's HTTP idiom: Python's gets one approved URL within its MetadataFetch bounds and connects only to the addresses the URL safety policy approved; TypeScript's is a fetch, each URL approved by the policy first.

Company​

PointPythonTypeScriptResponsibilitySDK defaultTenant-scoped
pap.company.storeCOMPANY_STORESCompanyStoresEvery record a company keeps (versioned JSON)memory
pap.company.accountsCOMPANY_ACCOUNTSCompanyAccountsCredentials and second factors for Mediated Sign-In— (host contributes)
pap.company.agentCOMPANY_AGENTSCompanyAgentsThe company agent answering conversations— (host contributes)
pap.company.operationExecutorOPERATION_EXECUTORSOperationExecutorsPerforms a confirmed operation, by action— (host contributes)
pap.company.clientPolicyCLIENT_POLICIESClientPoliciesAllows or blocks personal agents by client_idallow
pap.company.clientResolverCLIENT_RESOLVERSClientResolversFetches an agent's metadata and JWKShttps
pap.company.replayCacheREPLAY_CACHESReplayCachesRemembers assertion and proof IDs oncestore
pap.company.apiCOMPANY_APISCompanyApisThe company's OpenAPI API— (feature off)
pap.company.mcpCOMPANY_MCPCompanyMcpsThe company's MCP server— (feature off)

Tenants​

Two contracts are tenant-scoped: pap.pairwiseUserIdStore receives the tenant_id (tenantId) with each request, and pap.dpopProofProvider receives it in each DpopProofRequest. The SDK always passes the tenant of the call it is making, so a host keeps one tenant's pairwise IDs and proof keys apart from another's; the tests check that one tenant's ID is never returned for another and that each proof is asked for the request's own tenant. A company is one tenant of its own points: a host serving several companies builds one company (and one Reactor) for each.