MCPcopy Create free account
hub / github.com/adcontextprotocol/adcp / handleCreateMediaBuy

Function handleCreateMediaBuy

server/src/training-agent/task-handlers.ts:1280–1737  ·  view source on GitHub ↗
(args: ToolArgs, ctx: TrainingContext)

Source from the content-addressed store, hash-verified

1278}
1279
1280export async function handleCreateMediaBuy(args: ToolArgs, ctx: TrainingContext) {
1281 const req = args as unknown as CreateMediaBuyRequest & ToolArgs;
1282 let session = await getSession(sessionKeyFromArgs(req, ctx.mode, ctx.userId, ctx.moduleId));
1283
1284 // Consume any single-shot directive registered by
1285 // comply_test_controller.force_create_media_buy_arm. Runs before all other
1286 // gates so the storyboard's wire-shape probe is not confounded by governance
1287 // or account-status checks; the directive is sandbox-only and the runner
1288 // explicitly opted into this response shape. Cleared after read — a second
1289 // create_media_buy from the same session resumes default behavior.
1290 // Idempotency_key replay is unaffected: the SDK's request-idempotency cache
1291 // wraps this handler, so a replayed request returns the cached submitted
1292 // response without re-evaluating the (now-empty) directive slot.
1293 const directive = session.complyExtensions.forcedCreateMediaBuyArm;
1294 if (
1295 directive
1296 && directive.arm === 'submitted'
1297 && typeof directive.taskId === 'string'
1298 && directive.taskId.length > 0
1299 && directive.taskId.length <= 128
1300 ) {
1301 session.complyExtensions.forcedCreateMediaBuyArm = undefined;
1302 const responseMessage =
1303 typeof directive.message === 'string' && directive.message.length <= 2000
1304 ? directive.message
1305 : undefined;
1306 return {
1307 status: 'submitted',
1308 task_id: directive.taskId,
1309 ...(responseMessage && { message: responseMessage }),
1310 };
1311 }
1312
1313 // Enforce account status gates set by comply_test_controller
1314 const accountId = (req as unknown as Record<string, unknown>).account as { account_id?: string } | undefined;
1315 if (accountId?.account_id) {
1316 const acctStatus = getAccountStatus(session, accountId.account_id);
1317 if (acctStatus && acctStatus !== 'active') {
1318 const BLOCKED_STATUSES: Record<string, string> = {
1319 suspended: 'Account is suspended — contact the seller to resolve.',
1320 payment_required: 'Account requires payment before new media buys can be created.',
1321 closed: 'Account is closed and cannot create new media buys.',
1322 rejected: 'Account was rejected and cannot create media buys.',
1323 };
1324 return {
1325 errors: [{ code: 'ACCOUNT_STATUS_BLOCKED', message: BLOCKED_STATUSES[acctStatus] || `Account status "${acctStatus}" does not permit new media buys.` }] as TaskError[],
1326 };
1327 }
1328 }
1329
1330 // Enforce governance: if governance plans exist, validate the buy budget.
1331 // Deny-on-any-plan: without a governance_context there is no way to know
1332 // which plan the buy targets, so a conservative deny teaches buyers to call
1333 // check_governance first.
1334 const rawGovCtx = (req as unknown as Record<string, unknown>).governance_context;
1335 const govCtx = typeof rawGovCtx === 'string' && rawGovCtx ? rawGovCtx : undefined;
1336 if (govCtx) {
1337 // Find the latest check for this governance_context (Map iterates in insertion order)

Callers

nothing calls this directly

Calls 14

getSessionFunction · 0.85
sessionKeyFromArgsFunction · 0.85
getAccountStatusFunction · 0.85
governanceErrorDetailsFunction · 0.85
getCatalogFunction · 0.85
overlaySeededProductsFunction · 0.85
getProposalsFunction · 0.85
proposalLifecycleFunction · 0.85
validateTargetingFunction · 0.85
deriveStatusFunction · 0.85
validActionsForStatusFunction · 0.85

Tested by

no test coverage detected