@canonry/canonry 4.118.0 → 4.124.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.
Files changed (34) hide show
  1. package/README.md +9 -0
  2. package/assets/agent-workspace/skills/canonry/SKILL.md +3 -0
  3. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +135 -28
  4. package/assets/assets/{AuditHistoryPanel-Yh7VEe4P.js → AuditHistoryPanel-fPBADGsr.js} +1 -1
  5. package/assets/assets/{BacklinksPage-BmO77kMs.js → BacklinksPage-DKer7s8M.js} +1 -1
  6. package/assets/assets/ChartPrimitives-0DFDFhCZ.js +1 -0
  7. package/assets/assets/{HistoryPage-DkLV_iRc.js → HistoryPage-BTkTlXg0.js} +1 -1
  8. package/assets/assets/ProjectPage-BzqDCkp2.js +7 -0
  9. package/assets/assets/{RunRow-BDH9agxQ.js → RunRow-DyIeH4_d.js} +1 -1
  10. package/assets/assets/{RunsPage-D9YsEeT3.js → RunsPage-KVu_37ds.js} +1 -1
  11. package/assets/assets/{SettingsPage-o5as3Ixw.js → SettingsPage-CCLXSBpq.js} +1 -1
  12. package/assets/assets/{TrafficPage-BCK203aQ.js → TrafficPage-BH7a0sS2.js} +1 -1
  13. package/assets/assets/{TrafficSourceDetailPage-DsVMAZvS.js → TrafficSourceDetailPage-kIlf0kCe.js} +1 -1
  14. package/assets/assets/{arrow-left-B7ZR6VC9.js → arrow-left-jhivZs4g.js} +1 -1
  15. package/assets/assets/{extract-error-message-DeYDUpew.js → extract-error-message-XU_cbcLD.js} +1 -1
  16. package/assets/assets/index-CqFSTLK5.js +210 -0
  17. package/assets/assets/index-jymsMBpZ.css +1 -0
  18. package/assets/assets/{trash-2-xxHTqWbF.js → trash-2-CcmHLt5u.js} +1 -1
  19. package/assets/assets/vendor-recharts-DSxTZhaH.js +36 -0
  20. package/assets/index.html +3 -3
  21. package/dist/{chunk-HGNY5GL4.js → chunk-4D2XUMMV.js} +273 -37
  22. package/dist/{chunk-CMZIPFNQ.js → chunk-BGLBWFMV.js} +5034 -736
  23. package/dist/{chunk-KEZWLP4O.js → chunk-SLCGTOKT.js} +3293 -2337
  24. package/dist/{chunk-75UBTCKS.js → chunk-WGGB7QBD.js} +321 -6
  25. package/dist/cli.js +561 -36
  26. package/dist/index.js +4 -4
  27. package/dist/{intelligence-service-FWPXMAVK.js → intelligence-service-SACHNNCT.js} +2 -2
  28. package/dist/mcp.js +4 -4
  29. package/package.json +8 -8
  30. package/assets/assets/ChartPrimitives-H0ICVpw_.js +0 -1
  31. package/assets/assets/ProjectPage-R3odr_eA.js +0 -7
  32. package/assets/assets/index-Bot6YiD6.css +0 -1
  33. package/assets/assets/index-QCFQv0xh.js +0 -210
  34. package/assets/assets/vendor-recharts-C9EZkgbP.js +0 -36
package/assets/index.html CHANGED
@@ -12,12 +12,12 @@
12
12
  <link rel="icon" type="image/png" sizes="32x32" href="./favicon-32.png" />
13
13
  <link rel="apple-touch-icon" href="./apple-touch-icon.png" />
14
14
  <title>Canonry</title>
15
- <script type="module" crossorigin src="./assets/index-QCFQv0xh.js"></script>
15
+ <script type="module" crossorigin src="./assets/index-CqFSTLK5.js"></script>
16
16
  <link rel="modulepreload" crossorigin href="./assets/vendor-tanstack-Be8JdV5M.js">
17
17
  <link rel="modulepreload" crossorigin href="./assets/vendor-radix-CQ77EO2y.js">
18
- <link rel="modulepreload" crossorigin href="./assets/vendor-recharts-C9EZkgbP.js">
18
+ <link rel="modulepreload" crossorigin href="./assets/vendor-recharts-DSxTZhaH.js">
19
19
  <link rel="modulepreload" crossorigin href="./assets/vendor-markdown-6dwjPOKK.js">
20
- <link rel="stylesheet" crossorigin href="./assets/index-Bot6YiD6.css">
20
+ <link rel="stylesheet" crossorigin href="./assets/index-jymsMBpZ.css">
21
21
  </head>
22
22
  <body>
23
23
  <div id="root"></div>
@@ -10,7 +10,7 @@ import {
10
10
  loadConfig,
11
11
  loadConfigRaw,
12
12
  saveConfigPatch
13
- } from "./chunk-75UBTCKS.js";
13
+ } from "./chunk-WGGB7QBD.js";
14
14
  import {
15
15
  CC_CACHE_DIR,
16
16
  DUCKDB_SPEC,
@@ -115,12 +115,14 @@ import {
115
115
  siteAuditPages,
116
116
  siteAuditSnapshots,
117
117
  usageCounters
118
- } from "./chunk-CMZIPFNQ.js";
118
+ } from "./chunk-BGLBWFMV.js";
119
119
  import {
120
120
  AGENT_MEMORY_VALUE_MAX_BYTES,
121
121
  AGENT_PROVIDER_IDS,
122
122
  AI_ENGINE_DOMAINS,
123
123
  AI_ENGINE_SELF_DOMAINS,
124
+ AdsAdGroupBillingEventTypes,
125
+ AdsCampaignBiddingTypes,
124
126
  AgentProviderIds,
125
127
  BacklinkSources,
126
128
  CcReleaseSyncStatuses,
@@ -142,6 +144,10 @@ import {
142
144
  TrafficSourceTypes,
143
145
  VERTEX_AI_SEARCH_PROXY_DOMAIN,
144
146
  actionConfidenceLabel,
147
+ adsAccountDtoSchema,
148
+ adsConversionEventSettingListResponseSchema,
149
+ adsConversionPixelListResponseSchema,
150
+ adsGeoSearchResponseSchema,
145
151
  agentBusy,
146
152
  agentMemoryDeleteRequestSchema,
147
153
  agentMemoryUpsertRequestSchema,
@@ -166,6 +172,7 @@ import {
166
172
  isGhostTelemetryEvent,
167
173
  isRetryableHttpError,
168
174
  normalizeIdTokens,
175
+ normalizeServedModel,
169
176
  normalizeUrlPath,
170
177
  notFound,
171
178
  parseOriginList,
@@ -178,7 +185,7 @@ import {
178
185
  validationError,
179
186
  winnabilityClassLabel,
180
187
  withRetry
181
- } from "./chunk-KEZWLP4O.js";
188
+ } from "./chunk-SLCGTOKT.js";
182
189
 
183
190
  // src/telemetry.ts
184
191
  import crypto from "crypto";
@@ -594,6 +601,7 @@ async function executeTrackedQuery(input) {
594
601
  provider: "gemini",
595
602
  rawResponse,
596
603
  model,
604
+ servedModel: extractServedModel(rawResponse),
597
605
  groundingSources: parsed.groundingSources,
598
606
  searchQueries: parsed.searchQueries
599
607
  };
@@ -619,6 +627,9 @@ function normalizeResult(raw) {
619
627
  function hasParsedResponseContent(rawResponse) {
620
628
  return Array.isArray(rawResponse.candidates) && rawResponse.candidates.length > 0;
621
629
  }
630
+ function extractServedModel(rawResponse) {
631
+ return normalizeServedModel(rawResponse.modelVersion);
632
+ }
622
633
  function reparseStoredResult(rawResponse) {
623
634
  const groundingSources = extractGroundingMetadataFromRaw(rawResponse);
624
635
  const searchQueries = extractSearchQueriesFromRaw(rawResponse);
@@ -767,7 +778,11 @@ function responseToRecord(response) {
767
778
  }));
768
779
  return {
769
780
  candidates: candidates ?? [],
770
- usageMetadata: response.usageMetadata ?? null
781
+ usageMetadata: response.usageMetadata ?? null,
782
+ // Gemini reports the identity it actually served here — the only place it
783
+ // appears. Preserved so `servedModel` can be read back off a stored snapshot.
784
+ modelVersion: response.modelVersion ?? null,
785
+ responseId: response.responseId ?? null
771
786
  };
772
787
  } catch {
773
788
  return { error: "failed to serialize response" };
@@ -884,6 +899,7 @@ var geminiAdapter = {
884
899
  provider: "gemini",
885
900
  rawResponse: raw.rawResponse,
886
901
  model: raw.model,
902
+ servedModel: raw.servedModel,
887
903
  groundingSources: raw.groundingSources,
888
904
  searchQueries: raw.searchQueries
889
905
  };
@@ -1006,6 +1022,7 @@ async function executeTrackedQuery2(input) {
1006
1022
  provider: "openai",
1007
1023
  rawResponse,
1008
1024
  model,
1025
+ servedModel: extractServedModel2(rawResponse),
1009
1026
  groundingSources: parsed.groundingSources,
1010
1027
  searchQueries: parsed.searchQueries
1011
1028
  };
@@ -1031,6 +1048,9 @@ function normalizeResult2(raw) {
1031
1048
  function hasParsedResponseContent2(rawResponse) {
1032
1049
  return Array.isArray(rawResponse.output) && rawResponse.output.length > 0;
1033
1050
  }
1051
+ function extractServedModel2(rawResponse) {
1052
+ return normalizeServedModel(rawResponse.model);
1053
+ }
1034
1054
  function reparseStoredResult2(rawResponse) {
1035
1055
  const groundingSources = extractGroundingSourcesFromRaw(rawResponse);
1036
1056
  const searchQueries = extractSearchQueriesFromRaw2(rawResponse);
@@ -1228,6 +1248,7 @@ var openaiAdapter = {
1228
1248
  provider: "openai",
1229
1249
  rawResponse: raw.rawResponse,
1230
1250
  model: raw.model,
1251
+ servedModel: raw.servedModel,
1231
1252
  groundingSources: raw.groundingSources,
1232
1253
  searchQueries: raw.searchQueries
1233
1254
  };
@@ -1365,6 +1386,7 @@ async function executeTrackedQuery3(input) {
1365
1386
  provider: "claude",
1366
1387
  rawResponse,
1367
1388
  model,
1389
+ servedModel: extractServedModel3(rawResponse),
1368
1390
  groundingSources: parsed.groundingSources,
1369
1391
  searchQueries: parsed.searchQueries
1370
1392
  };
@@ -1390,6 +1412,9 @@ function normalizeResult3(raw) {
1390
1412
  function hasParsedResponseContent3(rawResponse) {
1391
1413
  return Array.isArray(rawResponse.content) && rawResponse.content.length > 0;
1392
1414
  }
1415
+ function extractServedModel3(rawResponse) {
1416
+ return normalizeServedModel(rawResponse.model);
1417
+ }
1393
1418
  function reparseStoredResult3(rawResponse) {
1394
1419
  const groundingSources = extractGroundingSourcesFromRaw2(rawResponse);
1395
1420
  const searchQueries = extractSearchQueriesFromRaw3(rawResponse);
@@ -1589,6 +1614,7 @@ var claudeAdapter = {
1589
1614
  provider: "claude",
1590
1615
  rawResponse: raw.rawResponse,
1591
1616
  model: raw.model,
1617
+ servedModel: raw.servedModel,
1592
1618
  groundingSources: raw.groundingSources,
1593
1619
  searchQueries: raw.searchQueries
1594
1620
  };
@@ -1704,10 +1730,12 @@ async function executeTrackedQuery4(input) {
1704
1730
  ]
1705
1731
  })
1706
1732
  );
1733
+ const rawResponse = responseToRecord4(response);
1707
1734
  return {
1708
1735
  provider: "local",
1709
- rawResponse: responseToRecord4(response),
1736
+ rawResponse,
1710
1737
  model,
1738
+ servedModel: extractServedModel4(rawResponse),
1711
1739
  groundingSources: [],
1712
1740
  searchQueries: []
1713
1741
  };
@@ -1771,6 +1799,9 @@ function extractDomainMentions(text) {
1771
1799
  }
1772
1800
  return [...domains];
1773
1801
  }
1802
+ function extractServedModel4(rawResponse) {
1803
+ return normalizeServedModel(rawResponse.model);
1804
+ }
1774
1805
  function responseToRecord4(response) {
1775
1806
  try {
1776
1807
  return JSON.parse(JSON.stringify(response));
@@ -1830,6 +1861,7 @@ var localAdapter = {
1830
1861
  provider: "local",
1831
1862
  rawResponse: raw.rawResponse,
1832
1863
  model: raw.model,
1864
+ servedModel: raw.servedModel,
1833
1865
  groundingSources: raw.groundingSources,
1834
1866
  searchQueries: raw.searchQueries
1835
1867
  };
@@ -2505,6 +2537,7 @@ async function executeTrackedQuery5(input) {
2505
2537
  provider: "perplexity",
2506
2538
  rawResponse,
2507
2539
  model,
2540
+ servedModel: extractServedModel5(rawResponse),
2508
2541
  groundingSources: parsed.groundingSources,
2509
2542
  searchQueries: parsed.searchQueries
2510
2543
  };
@@ -2535,6 +2568,9 @@ function hasParsedResponseContent4(rawResponse) {
2535
2568
  if (!nestedResponse) return false;
2536
2569
  return Array.isArray(nestedResponse.choices) && nestedResponse.choices.length > 0 || Array.isArray(nestedResponse.search_results) && nestedResponse.search_results.length > 0 || Array.isArray(nestedResponse.citations) && nestedResponse.citations.length > 0;
2537
2570
  }
2571
+ function extractServedModel5(rawResponse) {
2572
+ return normalizeServedModel(rawResponse.model);
2573
+ }
2538
2574
  function reparseStoredResult4(rawResponse) {
2539
2575
  const groundingSources = extractGroundingSources(rawResponse);
2540
2576
  return {
@@ -2725,6 +2761,7 @@ var perplexityAdapter = {
2725
2761
  provider: "perplexity",
2726
2762
  rawResponse: raw.rawResponse,
2727
2763
  model: raw.model,
2764
+ servedModel: raw.servedModel,
2728
2765
  groundingSources: raw.groundingSources,
2729
2766
  searchQueries: raw.searchQueries
2730
2767
  };
@@ -3303,7 +3340,10 @@ var JobRunner = class {
3303
3340
  }
3304
3341
  }
3305
3342
  const projectProviders = providerOverride ?? project.providers;
3306
- activeProviders = this.registry.getForProject(projectProviders);
3343
+ activeProviders = this.registry.getForProject(projectProviders).map((entry) => {
3344
+ const model = project.providerModels[entry.adapter.name];
3345
+ return model === void 0 ? entry : { ...entry, config: { ...entry.config, model } };
3346
+ });
3307
3347
  if (activeProviders.length === 0) {
3308
3348
  throw new Error("No providers configured. Add at least one provider API key.");
3309
3349
  }
@@ -3406,6 +3446,7 @@ var JobRunner = class {
3406
3446
  queryText: q.query,
3407
3447
  provider: providerName,
3408
3448
  model: raw.model,
3449
+ servedModel: raw.servedModel ?? null,
3409
3450
  citationState,
3410
3451
  answerMentioned,
3411
3452
  answerText: normalized.answerText,
@@ -3416,6 +3457,7 @@ var JobRunner = class {
3416
3457
  screenshotPath: screenshotRelPath,
3417
3458
  rawResponse: JSON.stringify({
3418
3459
  model: raw.model,
3460
+ servedModel: raw.servedModel ?? null,
3419
3461
  groundingSources: normalized.groundingSources,
3420
3462
  searchQueries: normalized.searchQueries,
3421
3463
  apiResponse: raw.rawResponse
@@ -3430,6 +3472,7 @@ var JobRunner = class {
3430
3472
  queryText: q.query,
3431
3473
  provider: providerName,
3432
3474
  model: raw.model,
3475
+ servedModel: raw.servedModel ?? null,
3433
3476
  citationState,
3434
3477
  answerMentioned,
3435
3478
  answerText: normalized.answerText,
@@ -3439,6 +3482,7 @@ var JobRunner = class {
3439
3482
  location: runLocation?.label ?? null,
3440
3483
  rawResponse: JSON.stringify({
3441
3484
  model: raw.model,
3485
+ servedModel: raw.servedModel ?? null,
3442
3486
  groundingSources: normalized.groundingSources,
3443
3487
  searchQueries: normalized.searchQueries,
3444
3488
  apiResponse: raw.rawResponse
@@ -4245,9 +4289,8 @@ var OpenAiAdsWriteStatuses = {
4245
4289
  active: "active",
4246
4290
  paused: "paused"
4247
4291
  };
4248
- var OpenAiAdsBillingEventTypes = {
4249
- impression: "impression"
4250
- };
4292
+ var OpenAiAdsBiddingTypes = AdsCampaignBiddingTypes;
4293
+ var OpenAiAdsBillingEventTypes = AdsAdGroupBillingEventTypes;
4251
4294
  var OpenAiAdsCreativeTypes = {
4252
4295
  chatCard: "chat_card"
4253
4296
  };
@@ -4284,6 +4327,8 @@ var MAX_BID_MICROS = 1e8;
4284
4327
  var MIN_AD_TITLE_LENGTH = 3;
4285
4328
  var MAX_AD_TITLE_LENGTH = 50;
4286
4329
  var MAX_AD_BODY_LENGTH = 100;
4330
+ var MIN_GEO_SEARCH_LIMIT = 1;
4331
+ var MAX_GEO_SEARCH_LIMIT = 500;
4287
4332
  function validateApiKey(apiKey) {
4288
4333
  if (!apiKey || typeof apiKey !== "string" || apiKey.trim().length === 0) {
4289
4334
  throw new OpenAiAdsApiError("API key is required and must be a non-empty string", 400);
@@ -4294,6 +4339,15 @@ function validateId(value, label) {
4294
4339
  throw new OpenAiAdsApiError(`${label} is required and must be a non-empty string`, 400);
4295
4340
  }
4296
4341
  }
4342
+ function validateGeoSearchLimit(limit) {
4343
+ if (limit === void 0) return;
4344
+ if (!Number.isInteger(limit) || limit < MIN_GEO_SEARCH_LIMIT || limit > MAX_GEO_SEARCH_LIMIT) {
4345
+ throw new OpenAiAdsApiError(
4346
+ `Geo search limit must be an integer between ${MIN_GEO_SEARCH_LIMIT} and ${MAX_GEO_SEARCH_LIMIT}`,
4347
+ 400
4348
+ );
4349
+ }
4350
+ }
4297
4351
  function isRecord(value) {
4298
4352
  return typeof value === "object" && value !== null && !Array.isArray(value);
4299
4353
  }
@@ -4370,10 +4424,34 @@ function validateCampaignTargeting(value) {
4370
4424
  validateId(target.id, "Campaign location id");
4371
4425
  }
4372
4426
  }
4427
+ function validateCampaignBidding(request) {
4428
+ const biddingType = request.bidding_type;
4429
+ if (biddingType !== void 0 && biddingType !== OpenAiAdsBiddingTypes.impressions && biddingType !== OpenAiAdsBiddingTypes.clicks) {
4430
+ throw new OpenAiAdsApiError("Campaign bidding_type must be impressions or clicks", 400);
4431
+ }
4432
+ const conversionIds = request.conversion_event_setting_ids;
4433
+ if (conversionIds !== void 0) {
4434
+ if (!Array.isArray(conversionIds)) {
4435
+ throw new OpenAiAdsApiError("Campaign conversion_event_setting_ids must be an array of unique IDs", 400);
4436
+ }
4437
+ for (const conversionId of conversionIds) {
4438
+ validateId(conversionId, "Campaign conversion event setting id");
4439
+ }
4440
+ if (new Set(conversionIds).size !== conversionIds.length) {
4441
+ throw new OpenAiAdsApiError("Campaign conversion_event_setting_ids must contain unique IDs", 400);
4442
+ }
4443
+ }
4444
+ if (biddingType === OpenAiAdsBiddingTypes.clicks && (!Array.isArray(conversionIds) || conversionIds.length === 0)) {
4445
+ throw new OpenAiAdsApiError(
4446
+ "Click campaigns require at least one conversion_event_setting_id",
4447
+ 400
4448
+ );
4449
+ }
4450
+ }
4373
4451
  function validateBiddingConfig(value) {
4374
4452
  validateRequestObject(value, "Ad group bidding_config");
4375
- if (value.billing_event_type !== OpenAiAdsBillingEventTypes.impression) {
4376
- throw new OpenAiAdsApiError("Ad group billing_event_type must be impression", 400);
4453
+ if (value.billing_event_type !== OpenAiAdsBillingEventTypes.impression && value.billing_event_type !== OpenAiAdsBillingEventTypes.click) {
4454
+ throw new OpenAiAdsApiError("Ad group billing_event_type must be impression or click", 400);
4377
4455
  }
4378
4456
  const maxBid = value.max_bid_micros;
4379
4457
  if (!Number.isInteger(maxBid) || maxBid < MIN_BID_MICROS || maxBid > MAX_BID_MICROS) {
@@ -4426,6 +4504,7 @@ function validateCreateCampaignRequest(request) {
4426
4504
  validateCampaignBudget(request.budget);
4427
4505
  validateCampaignTimestamp(request.start_time, "Campaign start_time");
4428
4506
  validateCampaignTimestamp(request.end_time, "Campaign end_time");
4507
+ validateCampaignBidding(request);
4429
4508
  validateCampaignTargeting(request.targeting);
4430
4509
  }
4431
4510
  function validateUpdateCampaignRequest(request) {
@@ -4526,7 +4605,11 @@ async function fetchAllPages(apiKey, path10, queryPairs) {
4526
4605
  after = response.last_id;
4527
4606
  }
4528
4607
  adsClientLog("error", "pagination.cap-reached", { path: path10, pages: OPENAI_ADS_MAX_PAGES, items: items.length });
4529
- return items;
4608
+ throw new OpenAiAdsApiError(
4609
+ `OpenAI Ads API collection remained incomplete after the ${OPENAI_ADS_MAX_PAGES}-page safety cap`,
4610
+ 502,
4611
+ "pagination_limit_exceeded"
4612
+ );
4530
4613
  }
4531
4614
  function insightsPairs(opts) {
4532
4615
  return (opts?.fields ?? []).map((field) => `fields[]=${encodeURIComponent(field)}`);
@@ -4535,6 +4618,22 @@ async function getAdAccount(apiKey) {
4535
4618
  validateApiKey(apiKey);
4536
4619
  return adsFetch(apiKey, "ad_account");
4537
4620
  }
4621
+ async function searchGeoLocations(apiKey, query, limit) {
4622
+ validateApiKey(apiKey);
4623
+ validateId(query, "Geo search query");
4624
+ validateGeoSearchLimit(limit);
4625
+ const queryPairs = [`q=${encodeURIComponent(query)}`];
4626
+ if (limit !== void 0) queryPairs.push(`limit=${limit}`);
4627
+ return adsFetch(apiKey, "geo_lookup/search", queryPairs);
4628
+ }
4629
+ async function listConversionPixels(apiKey) {
4630
+ validateApiKey(apiKey);
4631
+ return fetchAllPages(apiKey, "conversions/pixels", []);
4632
+ }
4633
+ async function listConversionEventSettings(apiKey) {
4634
+ validateApiKey(apiKey);
4635
+ return fetchAllPages(apiKey, "conversions/event_settings", []);
4636
+ }
4538
4637
  async function listCampaigns(apiKey) {
4539
4638
  validateApiKey(apiKey);
4540
4639
  return fetchAllPages(apiKey, "campaigns", []);
@@ -4557,6 +4656,11 @@ async function updateCampaign(apiKey, campaignId, request) {
4557
4656
  validateUpdateCampaignRequest(outbound);
4558
4657
  return adsFetch(apiKey, `campaigns/${encodeURIComponent(campaignId)}`, [], "POST", outbound);
4559
4658
  }
4659
+ async function activateCampaign(apiKey, campaignId) {
4660
+ validateApiKey(apiKey);
4661
+ validateId(campaignId, "Campaign id");
4662
+ return adsFetch(apiKey, `campaigns/${encodeURIComponent(campaignId)}/activate`, [], "POST");
4663
+ }
4560
4664
  async function pauseCampaign(apiKey, campaignId) {
4561
4665
  validateApiKey(apiKey);
4562
4666
  validateId(campaignId, "Campaign id");
@@ -4585,6 +4689,11 @@ async function updateAdGroup(apiKey, adGroupId, request) {
4585
4689
  validateUpdateAdGroupRequest(outbound);
4586
4690
  return adsFetch(apiKey, `ad_groups/${encodeURIComponent(adGroupId)}`, [], "POST", outbound);
4587
4691
  }
4692
+ async function activateAdGroup(apiKey, adGroupId) {
4693
+ validateApiKey(apiKey);
4694
+ validateId(adGroupId, "Ad group id");
4695
+ return adsFetch(apiKey, `ad_groups/${encodeURIComponent(adGroupId)}/activate`, [], "POST");
4696
+ }
4588
4697
  async function pauseAdGroup(apiKey, adGroupId) {
4589
4698
  validateApiKey(apiKey);
4590
4699
  validateId(adGroupId, "Ad group id");
@@ -4613,6 +4722,11 @@ async function updateAd(apiKey, adId, request) {
4613
4722
  validateUpdateAdRequest(outbound);
4614
4723
  return adsFetch(apiKey, `ads/${encodeURIComponent(adId)}`, [], "POST", outbound);
4615
4724
  }
4725
+ async function activateAd(apiKey, adId) {
4726
+ validateApiKey(apiKey);
4727
+ validateId(adId, "Ad id");
4728
+ return adsFetch(apiKey, `ads/${encodeURIComponent(adId)}/activate`, [], "POST");
4729
+ }
4616
4730
  async function pauseAd(apiKey, adId) {
4617
4731
  validateApiKey(apiKey);
4618
4732
  validateId(adId, "Ad id");
@@ -4762,6 +4876,7 @@ async function executeAdsSync(db, runId, projectId, opts) {
4762
4876
  biddingType: campaign.bidding_type,
4763
4877
  dailySpendLimitMicros: campaign.budget?.daily_spend_limit_micros ?? null,
4764
4878
  lifetimeSpendLimitMicros: campaign.budget?.lifetime_spend_limit_micros ?? null,
4879
+ conversionEventSettingIds: campaign.conversion_event_setting_ids ?? [],
4765
4880
  targeting: campaign.targeting,
4766
4881
  upstreamCreatedAt: campaign.created_at,
4767
4882
  upstreamUpdatedAt: campaign.updated_at,
@@ -4824,6 +4939,9 @@ async function executeAdsSync(db, runId, projectId, opts) {
4824
4939
  currencyCode: account.currency_code,
4825
4940
  timezone: account.timezone,
4826
4941
  status: account.status,
4942
+ reviewStatus: account.review?.status ?? null,
4943
+ integrityReviewStatus: account.account_integrity_review?.review?.status ?? null,
4944
+ integrityDecision: account.account_integrity_review?.details?.decision ?? null,
4827
4945
  conversionTrackingConfigured,
4828
4946
  lastSyncedAt: insertNow,
4829
4947
  updatedAt: insertNow
@@ -6783,7 +6901,7 @@ function readStoredGroundingSources(rawResponse) {
6783
6901
  return result;
6784
6902
  }
6785
6903
  async function backfillInsightsCommand(project, opts) {
6786
- const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-FWPXMAVK.js");
6904
+ const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-SACHNNCT.js");
6787
6905
  const config = loadConfig();
6788
6906
  const db = createClient(config.database);
6789
6907
  migrate(db);
@@ -8870,14 +8988,22 @@ var AERO_ADS_OPERATOR_MCP_TOOL_NAMES = /* @__PURE__ */ new Set([
8870
8988
  CanonryMcpToolNames.canonry_memory_forget,
8871
8989
  CanonryMcpToolNames.canonry_run_trigger,
8872
8990
  CanonryMcpToolNames.canonry_ads_status,
8991
+ CanonryMcpToolNames.canonry_ads_account,
8992
+ CanonryMcpToolNames.canonry_ads_geo_search,
8993
+ CanonryMcpToolNames.canonry_ads_conversion_pixels,
8994
+ CanonryMcpToolNames.canonry_ads_conversion_event_settings,
8873
8995
  CanonryMcpToolNames.canonry_ads_campaigns,
8874
8996
  CanonryMcpToolNames.canonry_ads_insights,
8875
8997
  CanonryMcpToolNames.canonry_ads_summary,
8998
+ CanonryMcpToolNames.canonry_ads_operations_unresolved,
8876
8999
  CanonryMcpToolNames.canonry_ads_operation_get,
9000
+ CanonryMcpToolNames.canonry_ads_operation_reconcile,
9001
+ CanonryMcpToolNames.canonry_ads_operation_resume_activation,
8877
9002
  CanonryMcpToolNames.canonry_ads_image_upload,
8878
9003
  CanonryMcpToolNames.canonry_ads_campaign_create,
8879
9004
  CanonryMcpToolNames.canonry_ads_campaign_update,
8880
9005
  CanonryMcpToolNames.canonry_ads_campaign_pause,
9006
+ CanonryMcpToolNames.canonry_ads_campaign_activate_tree,
8881
9007
  CanonryMcpToolNames.canonry_ads_ad_group_create,
8882
9008
  CanonryMcpToolNames.canonry_ads_ad_group_update,
8883
9009
  CanonryMcpToolNames.canonry_ads_ad_group_pause,
@@ -11652,38 +11778,130 @@ async function createServer(opts) {
11652
11778
  return removed;
11653
11779
  }
11654
11780
  };
11781
+ const normalizeAdsAccount = (account) => adsAccountDtoSchema.parse({
11782
+ id: account.id,
11783
+ name: account.name,
11784
+ status: account.status,
11785
+ currencyCode: account.currency_code ?? null,
11786
+ timezone: account.timezone ?? null,
11787
+ url: account.url ?? null,
11788
+ reviewStatus: account.review?.status ?? null,
11789
+ integrityReviewStatus: account.account_integrity_review?.review?.status ?? null,
11790
+ integrityDecision: account.account_integrity_review?.details?.decision ?? null
11791
+ });
11655
11792
  const verifyAdsAccount = async (apiKey) => {
11656
- const account = await getAdAccount(apiKey);
11793
+ const account = normalizeAdsAccount(await getAdAccount(apiKey));
11657
11794
  return {
11658
11795
  id: account.id,
11659
11796
  name: account.name,
11660
11797
  status: account.status,
11661
- currencyCode: account.currency_code ?? null,
11662
- timezone: account.timezone ?? null
11798
+ currencyCode: account.currencyCode,
11799
+ timezone: account.timezone,
11800
+ reviewStatus: account.reviewStatus,
11801
+ integrityReviewStatus: account.integrityReviewStatus,
11802
+ integrityDecision: account.integrityDecision
11663
11803
  };
11664
11804
  };
11805
+ const adsReader = {
11806
+ getAccount: async (apiKey) => normalizeAdsAccount(await getAdAccount(apiKey)),
11807
+ searchGeo: async (apiKey, input) => {
11808
+ const response = await searchGeoLocations(apiKey, input.q, input.limit);
11809
+ return adsGeoSearchResponseSchema.parse({
11810
+ count: response.count,
11811
+ query: response.query,
11812
+ results: response.results.map((location) => ({
11813
+ id: location.id,
11814
+ type: location.type,
11815
+ canonicalName: location.canonical_name,
11816
+ countryCode: location.country_code,
11817
+ name: location.name,
11818
+ regionCode: location.region_code
11819
+ }))
11820
+ });
11821
+ },
11822
+ listConversionPixels: async (apiKey) => {
11823
+ const pixels = await listConversionPixels(apiKey);
11824
+ return adsConversionPixelListResponseSchema.parse({
11825
+ pixels: pixels.map((pixel) => ({
11826
+ id: pixel.id,
11827
+ clientType: pixel.client_type,
11828
+ name: pixel.name,
11829
+ pixelId: pixel.pixel_id
11830
+ }))
11831
+ });
11832
+ },
11833
+ listConversionEventSettings: async (apiKey) => {
11834
+ const eventSettings = await listConversionEventSettings(apiKey);
11835
+ return adsConversionEventSettingListResponseSchema.parse({
11836
+ eventSettings: eventSettings.map((eventSetting) => ({
11837
+ id: eventSetting.id,
11838
+ name: eventSetting.name,
11839
+ eventType: eventSetting.event_type,
11840
+ customEventName: eventSetting.custom_event_name,
11841
+ attributionWindowDays: eventSetting.attribution_window_days,
11842
+ adAccountId: eventSetting.ad_account_id,
11843
+ sourceIds: eventSetting.source_ids,
11844
+ sources: eventSetting.sources,
11845
+ archived: eventSetting.archived,
11846
+ version: eventSetting.version
11847
+ }))
11848
+ });
11849
+ }
11850
+ };
11665
11851
  const adsEntityResult = (entity) => ({
11666
11852
  id: entity.id,
11853
+ name: entity.name,
11667
11854
  status: entity.status,
11668
11855
  updatedAt: entity.updated_at,
11669
- reviewStatus: entity.review_status ?? null
11856
+ reviewStatus: entity.review_status ?? null,
11857
+ creative: typeof entity.creative?.title === "string" && typeof entity.creative.body === "string" && typeof entity.creative.target_url === "string" && typeof entity.creative.file_id === "string" ? {
11858
+ title: entity.creative.title,
11859
+ body: entity.creative.body,
11860
+ targetUrl: entity.creative.target_url,
11861
+ fileId: entity.creative.file_id
11862
+ } : null
11863
+ });
11864
+ const adsCampaignEntityResult = (entity) => ({
11865
+ ...adsEntityResult(entity),
11866
+ description: entity.description,
11867
+ startTime: entity.start_time,
11868
+ endTime: entity.end_time,
11869
+ lifetimeSpendLimitMicros: entity.budget?.lifetime_spend_limit_micros ?? null,
11870
+ locationIds: entity.targeting?.locations?.include?.map((location) => location.id) ?? [],
11871
+ biddingType: entity.bidding_type,
11872
+ conversionEventSettingIds: entity.conversion_event_setting_ids
11873
+ });
11874
+ const adsAdGroupEntityResult = (entity, campaignId) => ({
11875
+ ...adsEntityResult(entity),
11876
+ description: entity.description,
11877
+ campaignId: campaignId ?? null,
11878
+ contextHints: entity.context_hints,
11879
+ maxBidMicros: entity.bidding_config?.max_bid_micros ?? null,
11880
+ billingEventType: entity.bidding_config?.billing_event_type ?? null
11881
+ });
11882
+ const adsAdEntityResult = (entity, adGroupId) => ({
11883
+ ...adsEntityResult(entity),
11884
+ adGroupId: adGroupId ?? null
11670
11885
  });
11671
11886
  const adsOperator = {
11672
11887
  uploadImage: async (apiKey, imageUrl) => {
11673
11888
  const result = await uploadImageFromUrl(apiKey, imageUrl);
11674
11889
  return { fileId: result.file_id };
11675
11890
  },
11676
- getCampaign: async (apiKey, id) => adsEntityResult(await getCampaign(apiKey, id)),
11677
- createCampaign: async (apiKey, input) => adsEntityResult(await createCampaign(apiKey, {
11891
+ getCampaign: async (apiKey, id) => adsCampaignEntityResult(await getCampaign(apiKey, id)),
11892
+ listCampaigns: async (apiKey) => (await listCampaigns(apiKey)).map(adsCampaignEntityResult),
11893
+ createCampaign: async (apiKey, input) => adsCampaignEntityResult(await createCampaign(apiKey, {
11678
11894
  name: input.name,
11679
11895
  description: input.description,
11680
11896
  start_time: input.startTime,
11681
11897
  end_time: input.endTime,
11682
11898
  status: OpenAiAdsWriteStatuses.paused,
11683
11899
  budget: { lifetime_spend_limit_micros: input.lifetimeSpendLimitMicros },
11900
+ bidding_type: input.biddingType,
11901
+ conversion_event_setting_ids: input.conversionEventSettingIds,
11684
11902
  targeting: { locations: { include: input.locationIds.map((id) => ({ id })) } }
11685
11903
  })),
11686
- updateCampaign: async (apiKey, id, input) => adsEntityResult(await updateCampaign(apiKey, id, {
11904
+ updateCampaign: async (apiKey, id, input) => adsCampaignEntityResult(await updateCampaign(apiKey, id, {
11687
11905
  name: input.name,
11688
11906
  description: input.description,
11689
11907
  start_time: input.startTime,
@@ -11691,31 +11909,44 @@ async function createServer(opts) {
11691
11909
  budget: input.lifetimeSpendLimitMicros === void 0 ? void 0 : { lifetime_spend_limit_micros: input.lifetimeSpendLimitMicros },
11692
11910
  targeting: input.locationIds === void 0 ? void 0 : { locations: { include: input.locationIds.map((locationId) => ({ id: locationId })) } }
11693
11911
  })),
11694
- pauseCampaign: async (apiKey, id) => adsEntityResult(await pauseCampaign(apiKey, id)),
11695
- getAdGroup: async (apiKey, id) => adsEntityResult(await getAdGroup(apiKey, id)),
11696
- createAdGroup: async (apiKey, input) => adsEntityResult(await createAdGroup(apiKey, {
11912
+ activateCampaign: async (apiKey, id) => adsCampaignEntityResult(await activateCampaign(apiKey, id)),
11913
+ pauseCampaign: async (apiKey, id) => adsCampaignEntityResult(await pauseCampaign(apiKey, id)),
11914
+ getAdGroup: async (apiKey, id) => adsAdGroupEntityResult(await getAdGroup(apiKey, id)),
11915
+ listAdGroups: async (apiKey, campaignId) => (await listAdGroups(apiKey, campaignId)).map((entity) => adsAdGroupEntityResult(entity, campaignId)),
11916
+ createAdGroup: async (apiKey, input) => adsAdGroupEntityResult(await createAdGroup(apiKey, {
11697
11917
  campaign_id: input.campaignId,
11698
11918
  name: input.name,
11699
11919
  description: input.description,
11700
11920
  context_hints: input.contextHints,
11701
11921
  status: OpenAiAdsWriteStatuses.paused,
11702
11922
  bidding_config: {
11703
- billing_event_type: OpenAiAdsBillingEventTypes.impression,
11923
+ billing_event_type: input.billingEventType,
11704
11924
  max_bid_micros: input.maxBidMicros
11705
11925
  }
11706
11926
  })),
11707
- updateAdGroup: async (apiKey, id, input) => adsEntityResult(await updateAdGroup(apiKey, id, {
11708
- name: input.name,
11709
- description: input.description,
11710
- context_hints: input.contextHints,
11711
- bidding_config: input.maxBidMicros === void 0 ? void 0 : {
11712
- billing_event_type: OpenAiAdsBillingEventTypes.impression,
11713
- max_bid_micros: input.maxBidMicros
11927
+ updateAdGroup: async (apiKey, id, input) => {
11928
+ let biddingConfig;
11929
+ if (input.maxBidMicros !== void 0) {
11930
+ if (input.billingEventType === void 0) {
11931
+ throw new Error("Ad group max-bid updates require the current billing event type");
11932
+ }
11933
+ biddingConfig = {
11934
+ billing_event_type: input.billingEventType,
11935
+ max_bid_micros: input.maxBidMicros
11936
+ };
11714
11937
  }
11715
- })),
11716
- pauseAdGroup: async (apiKey, id) => adsEntityResult(await pauseAdGroup(apiKey, id)),
11717
- getAd: async (apiKey, id) => adsEntityResult(await getAd(apiKey, id)),
11718
- createAd: async (apiKey, input) => adsEntityResult(await createAd(apiKey, {
11938
+ return adsAdGroupEntityResult(await updateAdGroup(apiKey, id, {
11939
+ name: input.name,
11940
+ description: input.description,
11941
+ context_hints: input.contextHints,
11942
+ bidding_config: biddingConfig
11943
+ }));
11944
+ },
11945
+ activateAdGroup: async (apiKey, id) => adsAdGroupEntityResult(await activateAdGroup(apiKey, id)),
11946
+ pauseAdGroup: async (apiKey, id) => adsAdGroupEntityResult(await pauseAdGroup(apiKey, id)),
11947
+ getAd: async (apiKey, id) => adsAdEntityResult(await getAd(apiKey, id)),
11948
+ listAds: async (apiKey, adGroupId) => (await listAds(apiKey, adGroupId)).map((entity) => adsAdEntityResult(entity, adGroupId)),
11949
+ createAd: async (apiKey, input) => adsAdEntityResult(await createAd(apiKey, {
11719
11950
  ad_group_id: input.adGroupId,
11720
11951
  name: input.name,
11721
11952
  status: OpenAiAdsWriteStatuses.paused,
@@ -11727,7 +11958,7 @@ async function createServer(opts) {
11727
11958
  file_id: input.creative.fileId
11728
11959
  }
11729
11960
  })),
11730
- updateAd: async (apiKey, id, input) => adsEntityResult(await updateAd(apiKey, id, {
11961
+ updateAd: async (apiKey, id, input) => adsAdEntityResult(await updateAd(apiKey, id, {
11731
11962
  name: input.name,
11732
11963
  creative: input.creative ? {
11733
11964
  type: OpenAiAdsCreativeTypes.chatCard,
@@ -11737,7 +11968,8 @@ async function createServer(opts) {
11737
11968
  file_id: input.creative.fileId
11738
11969
  } : void 0
11739
11970
  })),
11740
- pauseAd: async (apiKey, id) => adsEntityResult(await pauseAd(apiKey, id))
11971
+ activateAd: async (apiKey, id) => adsAdEntityResult(await activateAd(apiKey, id)),
11972
+ pauseAd: async (apiKey, id) => adsAdEntityResult(await pauseAd(apiKey, id))
11741
11973
  };
11742
11974
  const scheduler = new Scheduler(opts.db, {
11743
11975
  onRunCreated: (runId, projectId, providers2, location) => {
@@ -12331,6 +12563,7 @@ async function createServer(opts) {
12331
12563
  },
12332
12564
  adsCredentialStore,
12333
12565
  verifyAdsAccount,
12566
+ adsReader,
12334
12567
  adsOperator,
12335
12568
  onAdsSyncRequested: (runId, projectId) => {
12336
12569
  runAdsSync(runId, projectId);
@@ -12475,6 +12708,9 @@ async function createServer(opts) {
12475
12708
  name: a.name,
12476
12709
  displayName: a.displayName,
12477
12710
  mode: a.mode,
12711
+ modelConfigurable: a.mode === "api",
12712
+ defaultModel: a.modelRegistry.defaultModel,
12713
+ knownModels: a.modelRegistry.knownModels,
12478
12714
  modelValidationPattern: a.modelRegistry.validationPattern,
12479
12715
  modelValidationHint: a.modelRegistry.validationHint
12480
12716
  })),