import { createDerivedAccountStore } from '@adcp/sdk/server';
const accounts = createDerivedAccountStore<AudioStackAccountMeta>({
toAccount: (ctx) => ({
id: 'audiostack',
name: 'AudioStack',
status: 'active',
ctx_metadata: {}, // tokens stay on ctx.authInfo, not here
}),
});
const accounts = createDerivedAccountStore<{ tenantId: string }>({
toAccount: (ctx) => {
const cred = ctx?.authInfo?.credential;
const tenantId = cred?.kind === 'oauth' ? cred.client_id
: cred?.kind === 'api_key' ? cred.key_id
: 'public';
return {
id: 'criteo',
name: `Criteo (${tenantId})`,
status: 'active',
ctx_metadata: { tenantId },
};
},
});
write path (rare for Shape D, but possible):
const accounts: AccountStore<MyMeta> = {
...createDerivedAccountStore({ toAccount }),
upsert: async (refs, ctx) => mySync(refs, ctx),
};
Buyer-supplied account_id is refused at the framework boundary.
Since adcp-client#1468, the framework throws AdcpError('INVALID_REQUEST', { field: 'account.account_id' }) BEFORE reaching this resolver when a
'derived'-mode platform receives an inline account_id — same shape
as 'implicit''s long-standing refusal (#1364), with a single-tenant
message instead of the sync_accounts-first guidance. The factory
itself ignores any account_id that does reach it (defensive belt +
braces), but adopters can rely on the framework refusal as the
canonical surface. Hand-rolled 'derived' stores get the same
enforcement automatically.
Build an
AccountStore<TCtxMeta>for single-tenant agents whose tenant is derived from the auth principal alone (noaccount_idon the wire).The factory:
resolution: 'derived'.AdcpError('AUTH_REQUIRED')whenctx.authInfocarries no credential (skip withskipAuthCheck: true). The check accepts the discriminatedcredentialshape (preferred) AND the deprecatedtoken/clientIdfields populated by pre-#1269 authenticators — fail-closed only when none of the three are present.toAccount(ctx)and returns the result. Buyer-suppliedAccountReferenceis ignored — single-tenant by definition.list/upsert— single-tenant adapters have nothing to enumerate or write. Adopters who want either compose via spread.When NOT to use this. If two different auth principals should resolve to two different
account.idvalues, you want Shape B (createOAuthPassthroughResolver) orcreateTenantStore— NOT this factory. Misusing Shape D in a multi-tenant deployment routes every buyer to the same singleton id and breaks tenant isolation silently (no error, no log). The factory is single-tenant by design —toAccount(ctx)should return the sameidfor every principal that calls it.