@adcp/sdk 6.7.0 → 6.8.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/bin/adcp.js +15 -3
- package/dist/lib/adapters/derived-account-store.d.ts +152 -0
- package/dist/lib/adapters/derived-account-store.d.ts.map +1 -0
- package/dist/lib/adapters/derived-account-store.js +135 -0
- package/dist/lib/adapters/derived-account-store.js.map +1 -0
- package/dist/lib/adapters/implicit-account-store.d.ts +3 -1
- package/dist/lib/adapters/implicit-account-store.d.ts.map +1 -1
- package/dist/lib/adapters/implicit-account-store.js +3 -1
- package/dist/lib/adapters/implicit-account-store.js.map +1 -1
- package/dist/lib/adapters/index.d.ts +1 -0
- package/dist/lib/adapters/index.d.ts.map +1 -1
- package/dist/lib/adapters/index.js +7 -1
- package/dist/lib/adapters/index.js.map +1 -1
- package/dist/lib/adapters/oauth-passthrough-resolver.d.ts +3 -1
- package/dist/lib/adapters/oauth-passthrough-resolver.d.ts.map +1 -1
- package/dist/lib/adapters/oauth-passthrough-resolver.js +3 -1
- package/dist/lib/adapters/oauth-passthrough-resolver.js.map +1 -1
- package/dist/lib/adapters/roster-account-store.d.ts +85 -24
- package/dist/lib/adapters/roster-account-store.d.ts.map +1 -1
- package/dist/lib/adapters/roster-account-store.js +52 -30
- package/dist/lib/adapters/roster-account-store.js.map +1 -1
- package/dist/lib/index.d.ts +3 -3
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +13 -8
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/mock-server/creative-ad-server/seed-data.d.ts +81 -0
- package/dist/lib/mock-server/creative-ad-server/seed-data.d.ts.map +1 -0
- package/dist/lib/mock-server/creative-ad-server/seed-data.js +200 -0
- package/dist/lib/mock-server/creative-ad-server/seed-data.js.map +1 -0
- package/dist/lib/mock-server/creative-ad-server/server.d.ts +39 -0
- package/dist/lib/mock-server/creative-ad-server/server.d.ts.map +1 -0
- package/dist/lib/mock-server/creative-ad-server/server.js +618 -0
- package/dist/lib/mock-server/creative-ad-server/server.js.map +1 -0
- package/dist/lib/mock-server/index.d.ts.map +1 -1
- package/dist/lib/mock-server/index.js +180 -24
- package/dist/lib/mock-server/index.js.map +1 -1
- package/dist/lib/mock-server/sales-non-guaranteed/seed-data.d.ts +66 -0
- package/dist/lib/mock-server/sales-non-guaranteed/seed-data.d.ts.map +1 -0
- package/dist/lib/mock-server/sales-non-guaranteed/seed-data.js +193 -0
- package/dist/lib/mock-server/sales-non-guaranteed/seed-data.js.map +1 -0
- package/dist/lib/mock-server/sales-non-guaranteed/server.d.ts +33 -0
- package/dist/lib/mock-server/sales-non-guaranteed/server.d.ts.map +1 -0
- package/dist/lib/mock-server/sales-non-guaranteed/server.js +782 -0
- package/dist/lib/mock-server/sales-non-guaranteed/server.js.map +1 -0
- package/dist/lib/mock-server/sponsored-intelligence/seed-data.d.ts +50 -0
- package/dist/lib/mock-server/sponsored-intelligence/seed-data.d.ts.map +1 -0
- package/dist/lib/mock-server/sponsored-intelligence/seed-data.js +133 -0
- package/dist/lib/mock-server/sponsored-intelligence/seed-data.js.map +1 -0
- package/dist/lib/mock-server/sponsored-intelligence/server.d.ts +13 -0
- package/dist/lib/mock-server/sponsored-intelligence/server.d.ts.map +1 -0
- package/dist/lib/mock-server/sponsored-intelligence/server.js +609 -0
- package/dist/lib/mock-server/sponsored-intelligence/server.js.map +1 -0
- package/dist/lib/protocols/mcp.d.ts.map +1 -1
- package/dist/lib/protocols/mcp.js +1 -41
- package/dist/lib/protocols/mcp.js.map +1 -1
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/account-mode.d.ts +113 -0
- package/dist/lib/server/account-mode.d.ts.map +1 -0
- package/dist/lib/server/account-mode.js +125 -0
- package/dist/lib/server/account-mode.js.map +1 -0
- package/dist/lib/server/adcp-server.js +41 -0
- package/dist/lib/server/adcp-server.js.map +1 -1
- package/dist/lib/server/auth.d.ts +35 -0
- package/dist/lib/server/auth.d.ts.map +1 -1
- package/dist/lib/server/auth.js.map +1 -1
- package/dist/lib/server/create-adcp-server.d.ts +26 -9
- package/dist/lib/server/create-adcp-server.d.ts.map +1 -1
- package/dist/lib/server/create-adcp-server.js +46 -20
- package/dist/lib/server/create-adcp-server.js.map +1 -1
- package/dist/lib/server/ctx-metadata/store.d.ts +1 -1
- package/dist/lib/server/ctx-metadata/store.d.ts.map +1 -1
- package/dist/lib/server/ctx-metadata/store.js +1 -0
- package/dist/lib/server/ctx-metadata/store.js.map +1 -1
- package/dist/lib/server/decisioning/account.d.ts +5 -0
- package/dist/lib/server/decisioning/account.d.ts.map +1 -1
- package/dist/lib/server/decisioning/account.js.map +1 -1
- package/dist/lib/server/decisioning/buyer-agent.d.ts +37 -4
- package/dist/lib/server/decisioning/buyer-agent.d.ts.map +1 -1
- package/dist/lib/server/decisioning/buyer-agent.js +12 -2
- package/dist/lib/server/decisioning/buyer-agent.js.map +1 -1
- package/dist/lib/server/decisioning/compose.d.ts +33 -2
- package/dist/lib/server/decisioning/compose.d.ts.map +1 -1
- package/dist/lib/server/decisioning/compose.js +13 -46
- package/dist/lib/server/decisioning/compose.js.map +1 -1
- package/dist/lib/server/decisioning/index.d.ts +2 -1
- package/dist/lib/server/decisioning/index.d.ts.map +1 -1
- package/dist/lib/server/decisioning/index.js +2 -1
- package/dist/lib/server/decisioning/index.js.map +1 -1
- package/dist/lib/server/decisioning/platform-helpers.d.ts +18 -0
- package/dist/lib/server/decisioning/platform-helpers.d.ts.map +1 -1
- package/dist/lib/server/decisioning/platform-helpers.js +20 -0
- package/dist/lib/server/decisioning/platform-helpers.js.map +1 -1
- package/dist/lib/server/decisioning/platform.d.ts +19 -21
- package/dist/lib/server/decisioning/platform.d.ts.map +1 -1
- package/dist/lib/server/decisioning/platform.js.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.d.ts.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.js +334 -44
- package/dist/lib/server/decisioning/runtime/from-platform.js.map +1 -1
- package/dist/lib/server/decisioning/runtime/observed-modes.d.ts +40 -0
- package/dist/lib/server/decisioning/runtime/observed-modes.d.ts.map +1 -0
- package/dist/lib/server/decisioning/runtime/observed-modes.js +82 -0
- package/dist/lib/server/decisioning/runtime/observed-modes.js.map +1 -0
- package/dist/lib/server/decisioning/runtime/protocol-for-tool.js +2 -2
- package/dist/lib/server/decisioning/runtime/protocol-for-tool.js.map +1 -1
- package/dist/lib/server/decisioning/runtime/validate-platform.d.ts.map +1 -1
- package/dist/lib/server/decisioning/runtime/validate-platform.js +9 -1
- package/dist/lib/server/decisioning/runtime/validate-platform.js.map +1 -1
- package/dist/lib/server/decisioning/specialisms/sponsored-intelligence.d.ts +125 -0
- package/dist/lib/server/decisioning/specialisms/sponsored-intelligence.d.ts.map +1 -0
- package/dist/lib/server/decisioning/specialisms/sponsored-intelligence.js +52 -0
- package/dist/lib/server/decisioning/specialisms/sponsored-intelligence.js.map +1 -0
- package/dist/lib/server/decisioning/tenant-registry.d.ts +16 -0
- package/dist/lib/server/decisioning/tenant-registry.d.ts.map +1 -1
- package/dist/lib/server/decisioning/tenant-registry.js.map +1 -1
- package/dist/lib/server/index.d.ts +4 -1
- package/dist/lib/server/index.d.ts.map +1 -1
- package/dist/lib/server/index.js +9 -3
- package/dist/lib/server/index.js.map +1 -1
- package/dist/lib/server/serve.js +20 -2
- package/dist/lib/server/serve.js.map +1 -1
- package/dist/lib/server/test-controller.d.ts +3 -0
- package/dist/lib/server/test-controller.d.ts.map +1 -1
- package/dist/lib/server/test-controller.js +23 -20
- package/dist/lib/server/test-controller.js.map +1 -1
- package/dist/lib/testing/comply-controller.d.ts +23 -2
- package/dist/lib/testing/comply-controller.d.ts.map +1 -1
- package/dist/lib/testing/comply-controller.js +19 -2
- package/dist/lib/testing/comply-controller.js.map +1 -1
- package/dist/lib/testing/index.d.ts +1 -1
- package/dist/lib/testing/index.d.ts.map +1 -1
- package/dist/lib/testing/index.js.map +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/validations.js +36 -54
- package/dist/lib/testing/storyboard/validations.js.map +1 -1
- package/dist/lib/testing/test-controller.d.ts +10 -4
- package/dist/lib/testing/test-controller.d.ts.map +1 -1
- package/dist/lib/testing/test-controller.js +9 -3
- package/dist/lib/testing/test-controller.js.map +1 -1
- package/dist/lib/types/index.d.ts +3 -2
- package/dist/lib/types/index.d.ts.map +1 -1
- package/dist/lib/types/index.js +3 -0
- package/dist/lib/types/index.js.map +1 -1
- package/dist/lib/utils/glob.d.ts +4 -2
- package/dist/lib/utils/glob.d.ts.map +1 -1
- package/dist/lib/utils/glob.js +4 -2
- package/dist/lib/utils/glob.js.map +1 -1
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/docs/llms.txt +2 -2
- package/examples/README.md +29 -13
- package/examples/hello-cluster.ts +62 -23
- package/examples/hello_creative_adapter_ad_server.ts +790 -0
- package/examples/hello_seller_adapter_guaranteed.ts +80 -22
- package/examples/hello_seller_adapter_non_guaranteed.ts +1020 -0
- package/examples/hello_si_adapter_brand.ts +572 -0
- package/package.json +3 -2
- package/skills/build-creative-agent/SKILL.md +103 -183
- package/skills/build-generative-seller-agent/SKILL.md +15 -9
- package/skills/build-governance-agent/SKILL.md +20 -11
- package/skills/build-retail-media-agent/SKILL.md +10 -8
- package/skills/build-seller-agent/SKILL.md +15 -13
- package/skills/build-seller-agent/specialisms/sales-non-guaranteed.md +9 -31
- package/skills/build-si-agent/SKILL.md +251 -196
- package/skills/build-signals-agent/SKILL.md +2 -0
- package/skills/call-adcp-agent/SKILL.md +7 -1
- package/skills/call-adcp-agent.previous/SKILL.md +0 -261
|
@@ -89,6 +89,10 @@ const DEFAULT_FRAMEWORK_LOGGER = {
|
|
|
89
89
|
const status_changes_1 = require("../status-changes");
|
|
90
90
|
const comply_controller_1 = require("../../../testing/comply-controller");
|
|
91
91
|
const seed_merge_1 = require("../../../testing/seed-merge");
|
|
92
|
+
const adcp_server_1 = require("../../adcp-server");
|
|
93
|
+
const account_mode_1 = require("../../account-mode");
|
|
94
|
+
const test_controller_1 = require("../../test-controller");
|
|
95
|
+
const observed_modes_1 = require("./observed-modes");
|
|
92
96
|
const normalize_errors_1 = require("../../normalize-errors");
|
|
93
97
|
/**
|
|
94
98
|
* Apply `normalizeErrors` to a sync_creatives row's optional `errors`
|
|
@@ -105,44 +109,63 @@ function normalizeRowErrors(row) {
|
|
|
105
109
|
return { ...row, errors: (0, normalize_errors_1.normalizeErrors)(row.errors) };
|
|
106
110
|
}
|
|
107
111
|
/**
|
|
108
|
-
* Enforce the documented `
|
|
109
|
-
*
|
|
110
|
-
* `
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
* `
|
|
112
|
+
* Enforce the documented inline-`account_id` refusal for resolution modes
|
|
113
|
+
* that declare the field meaningless on the wire — `'implicit'` (since
|
|
114
|
+
* #1364) and `'derived'` (since adcp-client#1468). Both modes share the
|
|
115
|
+
* same wire contract: the buyer does not pass `account_id` inline; the
|
|
116
|
+
* framework derives the tenant from the authenticated principal (after a
|
|
117
|
+
* `sync_accounts` step for `'implicit'`; directly for `'derived'`).
|
|
114
118
|
*
|
|
115
119
|
* Throws `AdcpError('INVALID_REQUEST')` before reaching the adopter's
|
|
116
120
|
* `accounts.resolve`, so each adopter doesn't reimplement the same
|
|
117
121
|
* `if (ref?.account_id) return null` branch and the wire response is
|
|
118
|
-
* consistent across
|
|
119
|
-
*
|
|
120
|
-
*
|
|
122
|
+
* consistent across both modes. The brand+operator union arm is
|
|
123
|
+
* permitted — only `account_id`-shaped references are refused.
|
|
124
|
+
*
|
|
125
|
+
* Mode-specific message and suggestion: `'implicit'` adopters get the
|
|
126
|
+
* `sync_accounts`-first guidance; `'derived'` adopters get the single-
|
|
127
|
+
* tenant explanation (no `sync_accounts` step exists in derived mode —
|
|
128
|
+
* the auth principal alone identifies the tenant).
|
|
129
|
+
*
|
|
130
|
+
* Documented at `AccountStore.resolution` in `account.ts`.
|
|
121
131
|
*/
|
|
122
|
-
function
|
|
123
|
-
if (resolution !== 'implicit')
|
|
132
|
+
function refuseInlineAccountIdWhenForbidden(resolution, ref) {
|
|
133
|
+
if (resolution !== 'implicit' && resolution !== 'derived')
|
|
124
134
|
return;
|
|
125
135
|
if ((0, account_1.refAccountId)(ref) === undefined)
|
|
126
136
|
return;
|
|
137
|
+
if (resolution === 'implicit') {
|
|
138
|
+
throw new async_outcome_1.AdcpError('INVALID_REQUEST', {
|
|
139
|
+
message: 'This platform resolves accounts from the authenticated principal — call sync_accounts first; do not pass account.account_id inline.',
|
|
140
|
+
field: 'account.account_id',
|
|
141
|
+
suggestion: 'Call sync_accounts to associate accounts with your principal, then omit account_id on subsequent calls.',
|
|
142
|
+
});
|
|
143
|
+
}
|
|
127
144
|
throw new async_outcome_1.AdcpError('INVALID_REQUEST', {
|
|
128
|
-
message: 'This
|
|
145
|
+
message: 'This single-tenant agent identifies the tenant from the authenticated principal alone — do not pass account.account_id inline; the field is meaningless on the wire for derived-resolution agents.',
|
|
129
146
|
field: 'account.account_id',
|
|
130
|
-
suggestion: '
|
|
147
|
+
suggestion: 'Omit the account field; the framework derives the tenant from your authenticated credential.',
|
|
131
148
|
});
|
|
132
149
|
}
|
|
133
150
|
/**
|
|
134
151
|
* Dev-mode warning when a multi-id read tool returns fewer rows than
|
|
135
152
|
* the buyer requested — the canonical signal that the platform is
|
|
136
|
-
* silently truncating to
|
|
137
|
-
* #1399
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
* on every miss would
|
|
153
|
+
* silently truncating to `<id_field>[0]` (closes #1342, follow-up
|
|
154
|
+
* #1399, extended in #1410 to additional read-by-id surfaces). Catches
|
|
155
|
+
* the bug class where adopters write the recommended pattern wrong on
|
|
156
|
+
* first pass; quiet in production where legitimate misses (deleted,
|
|
157
|
+
* archived, cross-account) are routine and warning on every miss would
|
|
158
|
+
* be noise.
|
|
141
159
|
*
|
|
142
160
|
* Suppressible via `ADCP_SUPPRESS_MULTI_ID_WARN=1` for adopters whose
|
|
143
161
|
* legitimate-miss rate is high (deleted-account-rich datasets, etc.).
|
|
162
|
+
*
|
|
163
|
+
* Sync / upsert surfaces (`syncCreatives`, `syncCatalogs`, `syncPlans`)
|
|
164
|
+
* are intentionally out of scope — those have a different shape where
|
|
165
|
+
* pass-through is the obvious pattern and "did all rows roundtrip?"
|
|
166
|
+
* isn't a clean question.
|
|
144
167
|
*/
|
|
145
|
-
function warnIfTruncatedMultiIdResponse(toolName, requestedIds, responseArray, logger) {
|
|
168
|
+
function warnIfTruncatedMultiIdResponse(toolName, idFieldName, requestedIds, responseArray, logger) {
|
|
146
169
|
if (process.env.NODE_ENV === 'production')
|
|
147
170
|
return;
|
|
148
171
|
if (process.env.ADCP_SUPPRESS_MULTI_ID_WARN === '1')
|
|
@@ -152,10 +175,10 @@ function warnIfTruncatedMultiIdResponse(toolName, requestedIds, responseArray, l
|
|
|
152
175
|
const returned = Array.isArray(responseArray) ? responseArray.length : 0;
|
|
153
176
|
if (returned >= requestedIds.length)
|
|
154
177
|
return;
|
|
155
|
-
// Empty
|
|
178
|
+
// Empty `<id_field>` is filtered above as paginated-mode (no truncation
|
|
156
179
|
// possible without a request to compare against).
|
|
157
|
-
logger.warn(`[adcp/sdk] ${toolName}: platform returned ${returned} row${returned === 1 ? '' : 's'} for ${requestedIds.length} requested
|
|
158
|
-
`the platform may be silently truncating to
|
|
180
|
+
logger.warn(`[adcp/sdk] ${toolName}: platform returned ${returned} row${returned === 1 ? '' : 's'} for ${requestedIds.length} requested ${idFieldName} — ` +
|
|
181
|
+
`the platform may be silently truncating to ${idFieldName}[0]. ` +
|
|
159
182
|
`See https://github.com/adcontextprotocol/adcp-client/issues/1342 for the multi-id pass-through contract. ` +
|
|
160
183
|
`Suppress with ADCP_SUPPRESS_MULTI_ID_WARN=1 if legitimate misses (deleted / cross-account) are routine.`);
|
|
161
184
|
}
|
|
@@ -515,14 +538,15 @@ function createAdcpServerFromPlatform(platform, opts) {
|
|
|
515
538
|
let resolved = false;
|
|
516
539
|
let resolvedAccountId;
|
|
517
540
|
try {
|
|
518
|
-
// Enforce the JSDoc contract documented at
|
|
519
|
-
//
|
|
520
|
-
//
|
|
521
|
-
//
|
|
522
|
-
//
|
|
523
|
-
//
|
|
524
|
-
// the `{ account_id }` arm is refused. Closes adcp-client#1364
|
|
525
|
-
|
|
541
|
+
// Enforce the JSDoc contract documented at `AccountStore.resolution`:
|
|
542
|
+
// implicit-mode and derived-mode platforms refuse inline `account_id`
|
|
543
|
+
// references. Implicit: buyers call sync_accounts first, then the
|
|
544
|
+
// framework resolves from the auth principal. Derived: single-tenant;
|
|
545
|
+
// the principal alone identifies the tenant. The brand+operator union
|
|
546
|
+
// arm is permitted (implicit's sync_accounts onboarding flow); only
|
|
547
|
+
// the `{ account_id }` arm is refused. Closes adcp-client#1364
|
|
548
|
+
// (implicit) and adcp-client#1468 (derived).
|
|
549
|
+
refuseInlineAccountIdWhenForbidden(platform.accounts.resolution, ref);
|
|
526
550
|
const account = await platform.accounts.resolve(ref, toResolveCtx(ctx, ctx.toolName));
|
|
527
551
|
resolved = account != null;
|
|
528
552
|
resolvedAccountId = account?.id;
|
|
@@ -600,6 +624,7 @@ function createAdcpServerFromPlatform(platform, opts) {
|
|
|
600
624
|
}, ctxFor), 'creative', mergeOpts),
|
|
601
625
|
eventTracking: mergeHandlers(opts.eventTracking, buildEventTrackingHandlers(platform, ctxFor), 'eventTracking', mergeOpts),
|
|
602
626
|
signals: mergeHandlers(opts.signals, buildSignalsHandlers(platform, ctxFor, effectiveCtxMetadata, fwLogger), 'signals', mergeOpts),
|
|
627
|
+
sponsoredIntelligence: mergeHandlers(opts.sponsoredIntelligence, buildSponsoredIntelligenceHandlers(platform, ctxFor, effectiveCtxMetadata, fwLogger), 'sponsoredIntelligence', mergeOpts),
|
|
603
628
|
governance: mergeHandlers(opts.governance, buildGovernanceHandlers(platform, ctxFor), 'governance', mergeOpts),
|
|
604
629
|
accounts: mergeHandlers(opts.accounts, buildAccountHandlers(platform, ctxFor), 'accounts', mergeOpts),
|
|
605
630
|
brandRights: mergeHandlers(opts.brandRights, buildBrandRightsHandlers(platform, ctxFor, effectiveCtxMetadata, fwLogger), 'brandRights', mergeOpts),
|
|
@@ -611,10 +636,13 @@ function createAdcpServerFromPlatform(platform, opts) {
|
|
|
611
636
|
const server = (0, create_adcp_server_1.createAdcpServer)(config);
|
|
612
637
|
// Wire `comply_test_controller` if the adopter supplied adapters.
|
|
613
638
|
// `createComplyController` builds the tool definition + handler + raw
|
|
614
|
-
// dispatch
|
|
615
|
-
//
|
|
616
|
-
//
|
|
617
|
-
//
|
|
639
|
+
// dispatch. The framework registers the tool itself (bypassing
|
|
640
|
+
// `controller.register(server)`) so the sandbox-authority gate can
|
|
641
|
+
// resolve the calling account through `platform.accounts.resolve`
|
|
642
|
+
// BEFORE dispatching — under no circumstances should the controller
|
|
643
|
+
// operate on a `live`-mode account, regardless of what the caller
|
|
644
|
+
// claims on the wire. See `docs/proposals/lifecycle-state-and-sandbox-authority.md`
|
|
645
|
+
// for the full three-mode design and #1435 phase 2.
|
|
618
646
|
if (opts.complyTest != null) {
|
|
619
647
|
let complyConfig = opts.complyTest;
|
|
620
648
|
if (autoSeedStore != null) {
|
|
@@ -682,7 +710,131 @@ function createAdcpServerFromPlatform(platform, opts) {
|
|
|
682
710
|
complyConfig = { ...opts.complyTest, seed: autoSeed };
|
|
683
711
|
}
|
|
684
712
|
const controller = (0, comply_controller_1.createComplyController)(complyConfig);
|
|
685
|
-
|
|
713
|
+
// Manual registration with framework-side sandbox-authority gate. See
|
|
714
|
+
// top-of-block rationale and `docs/proposals/lifecycle-state-and-sandbox-authority.md`.
|
|
715
|
+
//
|
|
716
|
+
// Trust boundary: the gate consults the *resolved* account from
|
|
717
|
+
// `platform.accounts.resolve`, NOT a buyer-supplied claim like
|
|
718
|
+
// `account.sandbox === true` on the wire. The resolver is the only
|
|
719
|
+
// thing that names the account's mode; the gate refuses dispatch when
|
|
720
|
+
// mode is `live` (or the resolver fails to produce an account, modulo
|
|
721
|
+
// the env / context fallbacks below).
|
|
722
|
+
//
|
|
723
|
+
// Fallback paths (deprecated):
|
|
724
|
+
// - `context.sandbox === true` admits when no account resolved. Useful
|
|
725
|
+
// during the migration window for adopters whose wire shape carries
|
|
726
|
+
// sandbox routing in `context` but whose resolver isn't yet returning
|
|
727
|
+
// `mode: 'sandbox'`.
|
|
728
|
+
// - `process.env.ADCP_SANDBOX === '1'` admits unconditionally — the
|
|
729
|
+
// historical pattern. KEPT for back-compat so existing test platforms
|
|
730
|
+
// don't break on upgrade. Fails closed if the same process has ever
|
|
731
|
+
// resolved an explicit `mode: 'live'` account from the resolver: that
|
|
732
|
+
// pairing is a misconfiguration (env var should be unset on prod) and
|
|
733
|
+
// leaving it open re-exposes the live principal we just gated against.
|
|
734
|
+
//
|
|
735
|
+
// `list_scenarios` is exempt — it's the discovery probe used by buyer
|
|
736
|
+
// tooling to distinguish "controller wired but locked" from "controller
|
|
737
|
+
// missing entirely". Read-only and reveals nothing beyond which scenarios
|
|
738
|
+
// the adopter advertised in capabilities.
|
|
739
|
+
const mcp = (0, adcp_server_1.getSdkServer)(server);
|
|
740
|
+
if (mcp == null) {
|
|
741
|
+
// Non-MCP server — fall back to the controller's own registration so
|
|
742
|
+
// adopters wiring a custom transport keep the v5 behavior. The gate is
|
|
743
|
+
// an MCP-side concern; A2A and other transports are wired separately.
|
|
744
|
+
controller.register(server);
|
|
745
|
+
}
|
|
746
|
+
else {
|
|
747
|
+
// Permit a top-level `account` field on the wire so the gate can read
|
|
748
|
+
// the buyer's account ref. The canonical AdCP shape strips it (account
|
|
749
|
+
// routes through `context.account`), but adopters' storyboard fixtures
|
|
750
|
+
// commonly send it at the top level — `TOOL_INPUT_SHAPE`'s JSDoc in
|
|
751
|
+
// `src/lib/server/test-controller.ts` documents this extension as the
|
|
752
|
+
// supported escape hatch.
|
|
753
|
+
//
|
|
754
|
+
// Schema is the full canonical `AccountReference` per
|
|
755
|
+
// `schemas/cache/3.0.5/core/account-ref.json`: oneOf
|
|
756
|
+
// { account_id } — explicit accounts
|
|
757
|
+
// { brand, operator, sandbox? } — implicit accounts
|
|
758
|
+
// Modeled here as a single object with all four fields optional so
|
|
759
|
+
// either arm passes; resolvers narrow on the shape at dispatch.
|
|
760
|
+
// `brand` content is `unknown()` because the inner `brand-ref.json`
|
|
761
|
+
// shape is itself a oneOf and resolvers (not the gate) validate it.
|
|
762
|
+
// Top-level `.strict()` still blocks unknown keys at the framework
|
|
763
|
+
// boundary so a buyer can't stuff `__proto__` / arbitrary payloads
|
|
764
|
+
// into adopters' resolvers.
|
|
765
|
+
const gatedInputSchema = {
|
|
766
|
+
...controller.toolDefinition.inputSchema,
|
|
767
|
+
account: zod_1.z
|
|
768
|
+
.object({
|
|
769
|
+
account_id: zod_1.z.string().min(1).optional(),
|
|
770
|
+
brand: zod_1.z.unknown().optional(),
|
|
771
|
+
operator: zod_1.z.string().optional(),
|
|
772
|
+
sandbox: zod_1.z.boolean().optional(),
|
|
773
|
+
})
|
|
774
|
+
.strict()
|
|
775
|
+
.optional(),
|
|
776
|
+
};
|
|
777
|
+
mcp.registerTool(controller.toolDefinition.name, {
|
|
778
|
+
description: controller.toolDefinition.description,
|
|
779
|
+
inputSchema: gatedInputSchema,
|
|
780
|
+
}, (async (input, extra) => {
|
|
781
|
+
// Probe exempt — capability discovery, no state mutation.
|
|
782
|
+
if (input.scenario === 'list_scenarios') {
|
|
783
|
+
return controller.handle(input);
|
|
784
|
+
}
|
|
785
|
+
// Read account ref from top-level (extended shape) or from
|
|
786
|
+
// `context.account` (canonical AdCP routing). First non-null wins.
|
|
787
|
+
const refFromTop = input.account;
|
|
788
|
+
const refFromContext = input.context?.account;
|
|
789
|
+
const accountRef = refFromTop ?? refFromContext;
|
|
790
|
+
let resolvedAccount = null;
|
|
791
|
+
try {
|
|
792
|
+
resolvedAccount = await platform.accounts.resolve(accountRef, {
|
|
793
|
+
...(extra?.authInfo !== undefined && { authInfo: extra.authInfo }),
|
|
794
|
+
toolName: 'comply_test_controller',
|
|
795
|
+
});
|
|
796
|
+
}
|
|
797
|
+
catch {
|
|
798
|
+
// Resolver failures fall through to the wire-ref / env fallbacks.
|
|
799
|
+
// Treat as "no account resolved" — fail-closed by default unless a
|
|
800
|
+
// fallback admits.
|
|
801
|
+
}
|
|
802
|
+
// Record the resolved account's explicit mode (if any). Used by the
|
|
803
|
+
// env-fallback fail-closed guard below.
|
|
804
|
+
(0, observed_modes_1.recordResolvedAccountMode)(resolvedAccount);
|
|
805
|
+
const accountIsSandbox = resolvedAccount != null && (0, account_mode_1.isSandboxOrMockAccount)(resolvedAccount);
|
|
806
|
+
// Spec-defined fallback for the unresolved-account path: read
|
|
807
|
+
// `sandbox: true` off the wire `AccountReference` (per
|
|
808
|
+
// `core/account-ref.json`). Only consulted when the resolver
|
|
809
|
+
// returned `null` — if the resolver names the account, the
|
|
810
|
+
// resolver wins. The buyer's wire claim never overrides a
|
|
811
|
+
// resolved live account.
|
|
812
|
+
const refSandbox = accountRef?.sandbox === true;
|
|
813
|
+
const envSandbox = process.env.ADCP_SANDBOX === '1';
|
|
814
|
+
const wouldAdmitOnlyViaEnv = envSandbox && !accountIsSandbox && !(resolvedAccount == null && refSandbox);
|
|
815
|
+
// Fail-closed guard on the env fallback. If the env var is the only
|
|
816
|
+
// signal that would admit AND this process has ever resolved an
|
|
817
|
+
// explicit `mode: 'live'` account from the resolver, the env is
|
|
818
|
+
// misconfigured. Refuse loudly so operators notice, instead of
|
|
819
|
+
// silently downgrading the gate for live principals.
|
|
820
|
+
if (wouldAdmitOnlyViaEnv && (0, observed_modes_1.hasObservedLiveMode)()) {
|
|
821
|
+
throw new Error('comply_test_controller: ADCP_SANDBOX=1 is set but this process has resolved at least one ' +
|
|
822
|
+
'live-mode account from platform.accounts.resolve. Remove ADCP_SANDBOX from your prod ' +
|
|
823
|
+
'environment; gate the controller via mode: "sandbox" on resolved sandbox accounts instead. ' +
|
|
824
|
+
'See docs/proposals/lifecycle-state-and-sandbox-authority.md.');
|
|
825
|
+
}
|
|
826
|
+
const allowed = accountIsSandbox || (resolvedAccount == null && refSandbox) || envSandbox;
|
|
827
|
+
if (!allowed) {
|
|
828
|
+
return (0, test_controller_1.toMcpResponse)({
|
|
829
|
+
success: false,
|
|
830
|
+
error: 'FORBIDDEN',
|
|
831
|
+
error_detail: 'comply_test_controller requires a sandbox or mock account; ' +
|
|
832
|
+
'resolved account is in live mode (or no account resolved).',
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
return controller.handle(input);
|
|
836
|
+
}));
|
|
837
|
+
}
|
|
686
838
|
}
|
|
687
839
|
return Object.assign(server, {
|
|
688
840
|
getTaskState: async (taskId, expectedAccountId) => {
|
|
@@ -822,7 +974,7 @@ function buildTasksGetTool(platform, taskRegistry, agentRegistry, logger) {
|
|
|
822
974
|
};
|
|
823
975
|
let resolvedAccountId;
|
|
824
976
|
if (ref) {
|
|
825
|
-
|
|
977
|
+
refuseInlineAccountIdWhenForbidden(platform.accounts.resolution, ref);
|
|
826
978
|
try {
|
|
827
979
|
const resolved = await platform.accounts.resolve(ref, resolveCtx);
|
|
828
980
|
if (resolved)
|
|
@@ -1907,6 +2059,10 @@ exports.ENTITY_TO_RESOURCE_KIND = {
|
|
|
1907
2059
|
collection_list: 'collection_list',
|
|
1908
2060
|
account: 'account',
|
|
1909
2061
|
product: 'product',
|
|
2062
|
+
// SI session lifecycle, hydrated on `si_send_message` /
|
|
2063
|
+
// `si_terminate_session` from the entry the framework auto-stores after
|
|
2064
|
+
// `si_initiate_session`. See `buildSponsoredIntelligenceHandlers`.
|
|
2065
|
+
si_session: 'si_session',
|
|
1910
2066
|
};
|
|
1911
2067
|
/**
|
|
1912
2068
|
* `x-entity` values the SDK does NOT hydrate today — graceful skip rather
|
|
@@ -1928,8 +2084,7 @@ exports.INTENTIONALLY_UNHYDRATED_ENTITIES = new Set([
|
|
|
1928
2084
|
'governance_plan', // No SDK ResourceKind yet; campaign-governance follow-up.
|
|
1929
2085
|
'governance_check', // Transient request envelope; not stored.
|
|
1930
2086
|
'event_source', // No SDK ResourceKind yet.
|
|
1931
|
-
'
|
|
1932
|
-
'offering', // SI offering catalog; not stored as ctx-metadata.
|
|
2087
|
+
'offering', // SI offering catalog; correlation handled by brand-side `offering_token`, not framework hydration.
|
|
1933
2088
|
'rights_holder_brand', // Read-through `get_brand_identity`; not separately stored.
|
|
1934
2089
|
'advertiser_brand', // Same as above.
|
|
1935
2090
|
]);
|
|
@@ -2354,7 +2509,7 @@ function buildMediaBuyHandlers(platform, taskRegistry, taskWebhookEmit, observab
|
|
|
2354
2509
|
const reqCtx = ctxFor(ctx);
|
|
2355
2510
|
return projectSync(async () => {
|
|
2356
2511
|
const result = await sales.getMediaBuyDelivery(params, reqCtx);
|
|
2357
|
-
warnIfTruncatedMultiIdResponse('getMediaBuyDelivery', params.media_buy_ids, result?.media_buy_deliveries, logger);
|
|
2512
|
+
warnIfTruncatedMultiIdResponse('getMediaBuyDelivery', 'media_buy_ids', params.media_buy_ids, result?.media_buy_deliveries, logger);
|
|
2358
2513
|
return result;
|
|
2359
2514
|
}, actuals => actuals);
|
|
2360
2515
|
},
|
|
@@ -2377,7 +2532,7 @@ function buildMediaBuyHandlers(platform, taskRegistry, taskWebhookEmit, observab
|
|
|
2377
2532
|
const reqCtx = ctxFor(ctx);
|
|
2378
2533
|
return projectSync(async () => {
|
|
2379
2534
|
const result = await sales.getMediaBuys(params, reqCtx);
|
|
2380
|
-
warnIfTruncatedMultiIdResponse('getMediaBuys', params.media_buy_ids, result?.media_buys, logger);
|
|
2535
|
+
warnIfTruncatedMultiIdResponse('getMediaBuys', 'media_buy_ids', params.media_buy_ids, result?.media_buys, logger);
|
|
2381
2536
|
await autoStoreResources(ctxMetadataStore, reqCtx.account?.id, 'media_buy', result?.media_buys, 'media_buy_id', logger);
|
|
2382
2537
|
await backfillTargetingOverlay(mediaBuyStore, reqCtx.account?.id, result, logger);
|
|
2383
2538
|
return result;
|
|
@@ -2408,7 +2563,11 @@ function buildMediaBuyHandlers(platform, taskRegistry, taskWebhookEmit, observab
|
|
|
2408
2563
|
...(sales.listCreatives && {
|
|
2409
2564
|
listCreatives: async (params, ctx) => {
|
|
2410
2565
|
const reqCtx = ctxFor(ctx);
|
|
2411
|
-
return projectSync(
|
|
2566
|
+
return projectSync(async () => {
|
|
2567
|
+
const result = await sales.listCreatives(params, reqCtx);
|
|
2568
|
+
warnIfTruncatedMultiIdResponse('listCreatives', 'creative_ids', params.creative_ids, result?.creatives, logger);
|
|
2569
|
+
return result;
|
|
2570
|
+
}, r => r);
|
|
2412
2571
|
},
|
|
2413
2572
|
}),
|
|
2414
2573
|
};
|
|
@@ -2511,7 +2670,11 @@ function buildCreativeHandlers(platform, taskRegistry, taskWebhookEmit, observab
|
|
|
2511
2670
|
});
|
|
2512
2671
|
}
|
|
2513
2672
|
const reqCtx = ctxFor(ctx);
|
|
2514
|
-
return projectSync(
|
|
2673
|
+
return projectSync(async () => {
|
|
2674
|
+
const result = await creative.listCreatives(params, reqCtx);
|
|
2675
|
+
warnIfTruncatedMultiIdResponse('listCreatives', 'creative_ids', params.creative_ids, result?.creatives, logger);
|
|
2676
|
+
return result;
|
|
2677
|
+
}, r => r);
|
|
2515
2678
|
},
|
|
2516
2679
|
getCreativeDelivery: async (params, ctx) => {
|
|
2517
2680
|
if (!('getCreativeDelivery' in creative)) {
|
|
@@ -2520,7 +2683,11 @@ function buildCreativeHandlers(platform, taskRegistry, taskWebhookEmit, observab
|
|
|
2520
2683
|
});
|
|
2521
2684
|
}
|
|
2522
2685
|
const reqCtx = ctxFor(ctx);
|
|
2523
|
-
return projectSync(
|
|
2686
|
+
return projectSync(async () => {
|
|
2687
|
+
const result = await creative.getCreativeDelivery(params, reqCtx);
|
|
2688
|
+
warnIfTruncatedMultiIdResponse('getCreativeDelivery', 'creative_ids', params.creative_ids, result?.creative_deliveries, logger);
|
|
2689
|
+
return result;
|
|
2690
|
+
}, r => r);
|
|
2524
2691
|
},
|
|
2525
2692
|
};
|
|
2526
2693
|
}
|
|
@@ -2571,6 +2738,10 @@ function buildSignalsHandlers(platform, ctxFor, ctxMetadataStore, logger) {
|
|
|
2571
2738
|
const reqCtx = ctxFor(ctx);
|
|
2572
2739
|
return projectSync(async () => {
|
|
2573
2740
|
const result = await signals.getSignals(params, reqCtx);
|
|
2741
|
+
// signal_ids is `SignalID[]` (`{source, data_provider_domain, id}`
|
|
2742
|
+
// objects), not bare strings — but the helper's truncation-detection
|
|
2743
|
+
// is purely length-based, so the object element type is irrelevant.
|
|
2744
|
+
warnIfTruncatedMultiIdResponse('getSignals', 'signal_ids', params.signal_ids, result?.signals, logger);
|
|
2574
2745
|
// Auto-store signals so subsequent activate_signal can hydrate
|
|
2575
2746
|
// `req.signal` from the publisher's prior catalog entry.
|
|
2576
2747
|
await autoStoreResources(ctxMetadataStore, reqCtx.account?.id, 'signal', result?.signals, 'signal_agent_segment_id', logger);
|
|
@@ -2590,6 +2761,125 @@ function buildSignalsHandlers(platform, ctxFor, ctxMetadataStore, logger) {
|
|
|
2590
2761
|
},
|
|
2591
2762
|
};
|
|
2592
2763
|
}
|
|
2764
|
+
/**
|
|
2765
|
+
* Adapt `SponsoredIntelligencePlatform` (v6 protocol-keyed shape) onto the v5
|
|
2766
|
+
* `SponsoredIntelligenceHandlers` handler-bag the dispatcher consumes.
|
|
2767
|
+
*
|
|
2768
|
+
* Auto-store on `initiateSession`: stash a session record keyed by
|
|
2769
|
+
* `session_id` under `ResourceKind: 'si_session'`. The framework's
|
|
2770
|
+
* schema-driven hydrator (TOOL_ENTITY_FIELDS for `si_send_message` /
|
|
2771
|
+
* `si_terminate_session`) attaches that record at `params.session` on
|
|
2772
|
+
* subsequent calls so the platform reads transcript context from
|
|
2773
|
+
* `req.session` without manual `ctx.store.get`.
|
|
2774
|
+
*
|
|
2775
|
+
* Hydrate on `sendMessage` / `terminateSession`: schema-driven
|
|
2776
|
+
* `hydrateForTool` reads `params.session_id`, looks up the stored
|
|
2777
|
+
* session, and attaches at `params.session`.
|
|
2778
|
+
*/
|
|
2779
|
+
function buildSponsoredIntelligenceHandlers(platform, ctxFor, ctxMetadataStore, logger) {
|
|
2780
|
+
const si = platform.sponsoredIntelligence;
|
|
2781
|
+
if (!si)
|
|
2782
|
+
return undefined;
|
|
2783
|
+
return {
|
|
2784
|
+
getOffering: async (params, ctx) => {
|
|
2785
|
+
const reqCtx = ctxFor(ctx);
|
|
2786
|
+
return projectSync(() => si.getOffering(params, reqCtx), r => r);
|
|
2787
|
+
},
|
|
2788
|
+
initiateSession: async (params, ctx) => {
|
|
2789
|
+
const reqCtx = ctxFor(ctx);
|
|
2790
|
+
return projectSync(async () => {
|
|
2791
|
+
const result = await si.initiateSession(params, reqCtx);
|
|
2792
|
+
// Auto-store the session record so subsequent `sendMessage` /
|
|
2793
|
+
// `terminateSession` calls hydrate `req.session` without a
|
|
2794
|
+
// manual lookup. Stored payload preserves the bits the brand
|
|
2795
|
+
// engine needs to resume context across turns: identity
|
|
2796
|
+
// consent, offering scoping, negotiated capabilities,
|
|
2797
|
+
// placement provenance, and any media-buy linkage. The full
|
|
2798
|
+
// request intent is stored alongside so brand handlers can
|
|
2799
|
+
// re-read what the user originally asked for. Production
|
|
2800
|
+
// brand engines that own transcript state in their own
|
|
2801
|
+
// backend can ignore this and read from their own store —
|
|
2802
|
+
// see SponsoredIntelligencePlatform JSDoc.
|
|
2803
|
+
if (ctxMetadataStore && reqCtx.account?.id) {
|
|
2804
|
+
const sessionId = result?.session_id;
|
|
2805
|
+
if (typeof sessionId === 'string' && sessionId.length > 0) {
|
|
2806
|
+
const reqRec = params;
|
|
2807
|
+
const resRec = result;
|
|
2808
|
+
const sessionRecord = {
|
|
2809
|
+
session_id: sessionId,
|
|
2810
|
+
// Request-side scoping the brand engine needs across turns.
|
|
2811
|
+
intent: reqRec.intent,
|
|
2812
|
+
offering_id: reqRec.offering_id,
|
|
2813
|
+
offering_token: reqRec.offering_token,
|
|
2814
|
+
placement: reqRec.placement,
|
|
2815
|
+
media_buy_id: reqRec.media_buy_id,
|
|
2816
|
+
identity: reqRec.identity,
|
|
2817
|
+
supported_capabilities: reqRec.supported_capabilities,
|
|
2818
|
+
// Response-side state.
|
|
2819
|
+
negotiated_capabilities: resRec.negotiated_capabilities,
|
|
2820
|
+
session_status: resRec.session_status,
|
|
2821
|
+
session_ttl_seconds: resRec.session_ttl_seconds,
|
|
2822
|
+
};
|
|
2823
|
+
try {
|
|
2824
|
+
await ctxMetadataStore.setResource(reqCtx.account.id, 'si_session', sessionId, sessionRecord);
|
|
2825
|
+
}
|
|
2826
|
+
catch (err) {
|
|
2827
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2828
|
+
logger.warn(`[adcp/decisioning] auto-store si_session ${sessionId} failed: ${msg}`);
|
|
2829
|
+
}
|
|
2830
|
+
}
|
|
2831
|
+
}
|
|
2832
|
+
return result;
|
|
2833
|
+
}, r => r);
|
|
2834
|
+
},
|
|
2835
|
+
sendMessage: async (params, ctx) => {
|
|
2836
|
+
const reqCtx = ctxFor(ctx);
|
|
2837
|
+
// Auto-hydrate `req.session` from the stored si_session record. The
|
|
2838
|
+
// schema-driven hydrator reads `params.session_id` and attaches the
|
|
2839
|
+
// stored session at `params.session` (via the `_id` → strip
|
|
2840
|
+
// convention; `session_id` → `session`).
|
|
2841
|
+
await hydrateForTool(ctxMetadataStore, reqCtx.account?.id, 'si_send_message', params, logger);
|
|
2842
|
+
return projectSync(() => si.sendMessage(params, reqCtx), r => r);
|
|
2843
|
+
},
|
|
2844
|
+
terminateSession: async (params, ctx) => {
|
|
2845
|
+
const reqCtx = ctxFor(ctx);
|
|
2846
|
+
await hydrateForTool(ctxMetadataStore, reqCtx.account?.id, 'si_terminate_session', params, logger);
|
|
2847
|
+
return projectSync(async () => {
|
|
2848
|
+
const result = await si.terminateSession(params, reqCtx);
|
|
2849
|
+
// Persist `acp_handoff` and terminal `session_status` onto the
|
|
2850
|
+
// stored session record so a re-terminate replay (which is
|
|
2851
|
+
// naturally idempotent on `session_id` per the spec) hydrates
|
|
2852
|
+
// the same handoff payload via `req.session` without the
|
|
2853
|
+
// adopter having to remember to write through. Honors the
|
|
2854
|
+
// platform interface JSDoc contract:
|
|
2855
|
+
// "Re-terminating a closed session MUST return the same
|
|
2856
|
+
// payload, including any acp_handoff block."
|
|
2857
|
+
if (ctxMetadataStore && reqCtx.account?.id) {
|
|
2858
|
+
const sessionId = params?.session_id;
|
|
2859
|
+
if (typeof sessionId === 'string' && sessionId.length > 0) {
|
|
2860
|
+
const resRec = result;
|
|
2861
|
+
try {
|
|
2862
|
+
const existing = await ctxMetadataStore.getEntry(reqCtx.account.id, 'si_session', sessionId);
|
|
2863
|
+
const merged = {
|
|
2864
|
+
...(existing?.resource ?? { session_id: sessionId }),
|
|
2865
|
+
session_status: resRec.session_status,
|
|
2866
|
+
acp_handoff: resRec.acp_handoff,
|
|
2867
|
+
follow_up: resRec.follow_up,
|
|
2868
|
+
terminated: resRec.terminated,
|
|
2869
|
+
};
|
|
2870
|
+
await ctxMetadataStore.setResource(reqCtx.account.id, 'si_session', sessionId, merged);
|
|
2871
|
+
}
|
|
2872
|
+
catch (err) {
|
|
2873
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2874
|
+
logger.warn(`[adcp/decisioning] auto-store si_session ${sessionId} on terminate failed: ${msg}`);
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
}
|
|
2878
|
+
return result;
|
|
2879
|
+
}, r => r);
|
|
2880
|
+
},
|
|
2881
|
+
};
|
|
2882
|
+
}
|
|
2593
2883
|
function buildBrandRightsHandlers(platform, ctxFor, ctxMetadataStore, logger) {
|
|
2594
2884
|
const br = platform.brandRights;
|
|
2595
2885
|
if (!br)
|
|
@@ -2807,7 +3097,7 @@ function buildAccountHandlers(platform, ctxFor) {
|
|
|
2807
3097
|
// tokens / upstream IDs off `ctx.account.ctx_metadata` without
|
|
2808
3098
|
// having to re-resolve from `params.account`.
|
|
2809
3099
|
const resolveCtx = toResolveCtx(ctx, 'get_account_financials');
|
|
2810
|
-
|
|
3100
|
+
refuseInlineAccountIdWhenForbidden(accounts.resolution, params.account);
|
|
2811
3101
|
const resolved = await accounts.resolve(params.account, resolveCtx);
|
|
2812
3102
|
if (!resolved) {
|
|
2813
3103
|
throw new async_outcome_1.AdcpError('ACCOUNT_NOT_FOUND', {
|