@canonry/canonry 7.72.2 → 7.74.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 (131) hide show
  1. package/assets/assets/{AgentSetupPage-D39M9CRK.js → AgentSetupPage-D7YVdPWO.js} +1 -1
  2. package/assets/assets/{AuditHistoryPanel-BS-iFyHX.js → AuditHistoryPanel-yrgfS5B_.js} +1 -1
  3. package/assets/assets/{BacklinksPage-BW2bEGF4.js → BacklinksPage-DJM6z03g.js} +1 -1
  4. package/assets/assets/{HistoryPage-Dh6o2iVd.js → HistoryPage-CNvAVJ1S.js} +1 -1
  5. package/assets/assets/{MeasurementPropertyPage-DL9cbtVz.js → MeasurementPropertyPage-BVVYhOxR.js} +1 -1
  6. package/assets/assets/{ProjectPage-DXh97uDN.js → ProjectPage-CPRD8kR9.js} +1 -1
  7. package/assets/assets/{RunsPage-BM4ETLsm.js → RunsPage-Ck33dcjz.js} +1 -1
  8. package/assets/assets/{SettingsPage-DpJTB2-Q.js → SettingsPage-CS_t4Dai.js} +1 -1
  9. package/assets/assets/{SiteHealthSection-CmlMQ17V.js → SiteHealthSection-BurKnHpn.js} +2 -2
  10. package/assets/assets/ToneBadge-Df8oiKsQ.js +60 -0
  11. package/assets/assets/{TrafficPage-ClH3Dxnm.js → TrafficPage-DAiY698E.js} +1 -1
  12. package/assets/assets/{TrafficSourceDetailPage-DE-NyNGz.js → TrafficSourceDetailPage-clJSX2rr.js} +1 -1
  13. package/assets/assets/{account-context-BwV6ox8O.js → account-context-DDEhuoP2.js} +1 -1
  14. package/assets/assets/{card-DhkoBNyY.js → card-DbR-lBY3.js} +1 -1
  15. package/assets/assets/{extract-error-message-dNDtiSBo.js → extract-error-message-7_Vm2DQF.js} +1 -1
  16. package/assets/assets/{format-helpers-Bt4tx-f3.js → format-helpers-vtahpXTQ.js} +1 -1
  17. package/assets/assets/{index-MbApCekj.js → index-BIhCeXvc.js} +4 -4
  18. package/assets/assets/{query-client-D-zMw63p.js → query-client-TlFXYJ9o.js} +1 -1
  19. package/assets/assets/{react-query-EdiRRB2q.js → react-query-C5Mv8xfD.js} +1 -1
  20. package/assets/assets/{react-sigma_core.esm.min-Bidr1Gxk.js → react-sigma_core.esm.min-DmggJ7YC.js} +1 -1
  21. package/assets/assets/{service-BUi8JgGI.js → service-CLFURPuM.js} +1 -1
  22. package/assets/index.html +7 -7
  23. package/dist/{ads-FFUUMS4J.js → ads-L64W5J35.js} +3 -3
  24. package/dist/{agent-LCD2VG7C.js → agent-CMQ34CFD.js} +7 -7
  25. package/dist/{agent-ask-OBHTCYFR.js → agent-ask-3KEYJSZV.js} +3 -3
  26. package/dist/{agent-conversations-VMZ4NFIX.js → agent-conversations-4KKPOTCP.js} +3 -3
  27. package/dist/{agent-memory-UPFJFNWC.js → agent-memory-VOSRZ57W.js} +3 -3
  28. package/dist/{agent-providers-4GOWLBZK.js → agent-providers-SLXTYMDS.js} +3 -3
  29. package/dist/{agent-transcript-RGXIVZUP.js → agent-transcript-Q6OEZROG.js} +3 -3
  30. package/dist/{analytics-FBK4XV5P.js → analytics-LSF6NYII.js} +3 -3
  31. package/dist/{apply-Y2DVE3YB.js → apply-7O3RES7N.js} +3 -3
  32. package/dist/{backfill-NE55YL6X.js → backfill-7ARSKYDG.js} +9 -9
  33. package/dist/{backlinks-YWHFC7DZ.js → backlinks-4YAC374B.js} +3 -3
  34. package/dist/{bing-7BP3DWPK.js → bing-SHAYDXAA.js} +3 -3
  35. package/dist/{bootstrap-Z7QSKVPL.js → bootstrap-BHLBGQKJ.js} +10 -10
  36. package/dist/{cdp-OL6RENO3.js → cdp-QWJE6SFY.js} +7 -7
  37. package/dist/{chunk-J523Y7X2.js → chunk-2Y6MSB4J.js} +1 -1
  38. package/dist/{chunk-GA2EHZTI.js → chunk-3WPTMLQI.js} +22 -6
  39. package/dist/{chunk-QDN3FL2A.js → chunk-3YIKSZHN.js} +2 -1
  40. package/dist/{chunk-IY4AJ6DN.js → chunk-5PA7SJ2M.js} +5 -5
  41. package/dist/{chunk-JO73L677.js → chunk-6ATKY252.js} +2 -2
  42. package/dist/{chunk-XR6ZVMYK.js → chunk-6W5AR4IW.js} +1 -1
  43. package/dist/{chunk-IZMT4WLZ.js → chunk-6YW24AUN.js} +2 -2
  44. package/dist/{chunk-QKDKVPMR.js → chunk-AJMDETJA.js} +4 -4
  45. package/dist/{chunk-HZSE3WSG.js → chunk-AZQNJX3T.js} +2 -2
  46. package/dist/{chunk-ATT3ZCSS.js → chunk-BICUWFV4.js} +38 -16
  47. package/dist/{chunk-MJYO6UK2.js → chunk-DL3LTSBV.js} +2 -2
  48. package/dist/{chunk-6MYHGP46.js → chunk-DLVGMRM5.js} +1 -1
  49. package/dist/{chunk-TOX6HAQJ.js → chunk-EXGETCJR.js} +3 -3
  50. package/dist/{chunk-BADJCT6U.js → chunk-GWDMWKM6.js} +1 -1
  51. package/dist/{chunk-PPM6Z7CM.js → chunk-HEL6DKDN.js} +1 -1
  52. package/dist/{chunk-NZZWJCKS.js → chunk-HJHFW455.js} +3 -3
  53. package/dist/{chunk-DRGCVTYH.js → chunk-JPM3VC6A.js} +1 -1
  54. package/dist/{chunk-H4HNXCDF.js → chunk-KOWOQ3JZ.js} +2 -2
  55. package/dist/{chunk-UWBJWKDQ.js → chunk-L5GIL45X.js} +2 -2
  56. package/dist/{chunk-P3UGNR2Y.js → chunk-LS47ZAX2.js} +5 -5
  57. package/dist/{chunk-IV6VZ63E.js → chunk-MB3IUJ6Z.js} +18 -14
  58. package/dist/{chunk-3AG3LPZX.js → chunk-MRSNLKSM.js} +1 -1
  59. package/dist/{chunk-JE5QURTR.js → chunk-NXFNXXYM.js} +184 -92
  60. package/dist/{chunk-Q25JQANW.js → chunk-O4D66XPL.js} +17 -2
  61. package/dist/{chunk-O7WOCTBD.js → chunk-PAIYFNNY.js} +71 -3
  62. package/dist/{chunk-JEBY6MS6.js → chunk-QS6RIP37.js} +1 -1
  63. package/dist/{chunk-4LTL5RNY.js → chunk-SCRPC2PQ.js} +4 -4
  64. package/dist/{chunk-YXAKL5HM.js → chunk-SF5JLMLG.js} +6791 -6777
  65. package/dist/{chunk-UZKNS5PL.js → chunk-TXUNPPWD.js} +254 -92
  66. package/dist/{chunk-NZLK3KHQ.js → chunk-WLNYLQTX.js} +6 -6
  67. package/dist/{chunk-7KOAP47B.js → chunk-WLWOGBLJ.js} +363 -248
  68. package/dist/{chunk-CF6BUJRK.js → chunk-WY2C6FFA.js} +8 -8
  69. package/dist/{chunk-BZZDFIY2.js → chunk-XDQJOZKJ.js} +1 -1
  70. package/dist/{chunk-TEDY3MHN.js → chunk-YTSE2EZP.js} +3 -3
  71. package/dist/{chunk-G7SGQUW4.js → chunk-ZMDIARSG.js} +345 -337
  72. package/dist/{citations-RVSC67GE.js → citations-EENLZULG.js} +3 -3
  73. package/dist/{cli-catalog-NEZE5QU5.js → cli-catalog-BXHG544D.js} +3 -3
  74. package/dist/{cli-commands-ARJWPTFW.js → cli-commands-7BG5M2KF.js} +7 -7
  75. package/dist/cli.js +13 -13
  76. package/dist/{competitor-BX3M3OYS.js → competitor-CW5LMXJ6.js} +3 -3
  77. package/dist/{content-PKJMVMHJ.js → content-BX6UGRRU.js} +3 -3
  78. package/dist/{dashboard-SVHZBVM6.js → dashboard-M2TKT2LW.js} +6 -6
  79. package/dist/{demo-GLV22DPY.js → demo-GKSMSKOY.js} +1 -1
  80. package/dist/{demo-server-GPM4C4Q2.js → demo-server-UQO4XKHN.js} +12 -12
  81. package/dist/{discover-JDP44ZHX.js → discover-2EZUZFNR.js} +3 -3
  82. package/dist/{discover-eval-6ORCVYYR.js → discover-eval-IS2GRYVV.js} +3 -3
  83. package/dist/{doctor-TVZRMBSS.js → doctor-DPEIBV3E.js} +3 -3
  84. package/dist/{evidence-FNIAFBBG.js → evidence-UGC4ZWNA.js} +3 -3
  85. package/dist/{export-cmd-HQNF2SSD.js → export-cmd-7OVM23C4.js} +3 -3
  86. package/dist/{ga-PIO5OXMK.js → ga-FUADE4K4.js} +3 -3
  87. package/dist/{gbp-3R5FME43.js → gbp-R4M663R5.js} +3 -3
  88. package/dist/{google-BYZREX2O.js → google-WTOTC5SL.js} +4 -4
  89. package/dist/{google-marketing-SPQ2GW6X.js → google-marketing-IXPMUBXZ.js} +1 -1
  90. package/dist/{health-cmd-HH5RYMSV.js → health-cmd-QKDO6VIT.js} +3 -3
  91. package/dist/{history-Q35NLLMJ.js → history-LEBTYXVW.js} +3 -3
  92. package/dist/index.d.ts +249 -72
  93. package/dist/index.js +25 -25
  94. package/dist/{init-Q33HEJJN.js → init-BKOT4UY2.js} +12 -11
  95. package/dist/{insights-SH73B4PD.js → insights-OBHQ5GHV.js} +3 -3
  96. package/dist/intelligence-service-EJQQNO65.js +16 -0
  97. package/dist/{journeys-SIV7HPW7.js → journeys-PTMCJHOL.js} +3 -3
  98. package/dist/{keys-O656YWSW.js → keys-W2FZNFOR.js} +3 -3
  99. package/dist/{keyword-FZ2N4EW7.js → keyword-Y6WABJPU.js} +3 -3
  100. package/dist/{logs-2NPN7HBI.js → logs-KRSH53GN.js} +3 -3
  101. package/dist/mcp-6LJW6LEJ.js +19 -0
  102. package/dist/mcp.js +20 -14
  103. package/dist/{measurement-plan-JQKVSJRL.js → measurement-plan-RHLKXNYG.js} +5 -5
  104. package/dist/{notify-VTAYBHC6.js → notify-HRK2HQTU.js} +3 -3
  105. package/dist/{organic-evidence-HPXXEPOW.js → organic-evidence-EX2VL5CE.js} +3 -3
  106. package/dist/{overview-FSBNDYZJ.js → overview-254QNXAC.js} +4 -4
  107. package/dist/{project-MAPPXURS.js → project-UOYX7T4J.js} +3 -3
  108. package/dist/{query-R6DLPIPJ.js → query-JMVOMNRS.js} +3 -3
  109. package/dist/{research-FXKOEUQB.js → research-PAPX7KO5.js} +3 -3
  110. package/dist/{results-export-X2PUM63S.js → results-export-3BXIIDP2.js} +3 -3
  111. package/dist/{run-6ENTG4IO.js → run-WQXLV4MS.js} +4 -4
  112. package/dist/{schedule-DMMVRT7P.js → schedule-3WJ5F3BF.js} +3 -3
  113. package/dist/{search-FHI6APT7.js → search-RVGFOZAT.js} +3 -3
  114. package/dist/{serve-SJTQQQQE.js → serve-BPM2P3GY.js} +26 -26
  115. package/dist/{session-VHCKM3AI.js → session-76DT5W6D.js} +13 -13
  116. package/dist/{settings-FMB4QED4.js → settings-MRJ566D6.js} +3 -3
  117. package/dist/{skills-HBIMHSOI.js → skills-F6AXQHTO.js} +2 -2
  118. package/dist/{snapshot-RBA2EL7R.js → snapshot-7GBDFFVB.js} +3 -3
  119. package/dist/{src-BXXGJOQB.js → src-52USCS7O.js} +4 -2
  120. package/dist/{status-ATFDFAVG.js → status-S6CZV2LU.js} +4 -4
  121. package/dist/{technical-aeo-JWQTGCTB.js → technical-aeo-Q73ZF2FD.js} +3 -3
  122. package/dist/{telemetry-6QFGJL2I.js → telemetry-BT4OOPN6.js} +4 -4
  123. package/dist/{tools-4VRHKUBE.js → tools-HYOV77FY.js} +11 -11
  124. package/dist/{traffic-5R7M4CK5.js → traffic-EFLF62IZ.js} +5 -5
  125. package/dist/{users-24TIUYJY.js → users-MYXGNUIZ.js} +3 -3
  126. package/dist/{visibility-stats-YLQPPBJJ.js → visibility-stats-QENASZW6.js} +3 -3
  127. package/dist/{wordpress-7VDPHMUZ.js → wordpress-CYLQHE5L.js} +3 -3
  128. package/package.json +10 -6
  129. package/assets/assets/ToneBadge-CmvL5mFG.js +0 -60
  130. package/dist/intelligence-service-LVRH4RJG.js +0 -16
  131. package/dist/mcp-RHQY3DV6.js +0 -14
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  answerServiceAdapters,
3
3
  answerServiceDescriptors
4
- } from "./chunk-XR6ZVMYK.js";
4
+ } from "./chunk-6W5AR4IW.js";
5
5
  import {
6
6
  AeroLlmUsageFeatures,
7
7
  aeroTurnStatus,
@@ -27,14 +27,14 @@ import {
27
27
  resolveConfiguredAgentProvider,
28
28
  resolveSessionProviderAndModel,
29
29
  setAeroSystemPrompt
30
- } from "./chunk-NZLK3KHQ.js";
30
+ } from "./chunk-WLNYLQTX.js";
31
31
  import {
32
32
  AeroToolProfiles,
33
33
  AeroToolScopes,
34
34
  buildAeroStateTools,
35
35
  isAeroToolProfile,
36
36
  truncateToolResult
37
- } from "./chunk-P3UGNR2Y.js";
37
+ } from "./chunk-LS47ZAX2.js";
38
38
  import {
39
39
  getGoogleAuthConfig,
40
40
  getGoogleConnection,
@@ -53,7 +53,7 @@ import {
53
53
  removeCloudflareTrafficConnection,
54
54
  removeCloudflareTrafficConnectionBySourceId,
55
55
  upsertCloudflareTrafficConnection
56
- } from "./chunk-BZZDFIY2.js";
56
+ } from "./chunk-XDQJOZKJ.js";
57
57
  import {
58
58
  claudeAdapter,
59
59
  embedQueries,
@@ -64,7 +64,7 @@ import {
64
64
  openaiAdapter,
65
65
  openrouterAdapter,
66
66
  perplexityAdapter
67
- } from "./chunk-JO73L677.js";
67
+ } from "./chunk-6ATKY252.js";
68
68
  import {
69
69
  classifyUsageSurface,
70
70
  createApiUsageTelemetry,
@@ -76,7 +76,7 @@ import {
76
76
  startOutcomeTimer,
77
77
  trackFeatureCompleted,
78
78
  trackInstallState
79
- } from "./chunk-4LTL5RNY.js";
79
+ } from "./chunk-SCRPC2PQ.js";
80
80
  import {
81
81
  isLoopbackBindHost
82
82
  } from "./chunk-TSENU4GG.js";
@@ -84,7 +84,7 @@ import {
84
84
  isApiProviderRegistrable,
85
85
  isCdpProviderRegistrable,
86
86
  registeredProviderNames
87
- } from "./chunk-6MYHGP46.js";
87
+ } from "./chunk-DLVGMRM5.js";
88
88
  import {
89
89
  checkLatestVersionForServer,
90
90
  getOrCreateAnonymousId,
@@ -95,17 +95,17 @@ import {
95
95
  recordDashboardEvent,
96
96
  setTelemetryPreference,
97
97
  trackEvent
98
- } from "./chunk-MJYO6UK2.js";
98
+ } from "./chunk-DL3LTSBV.js";
99
99
  import {
100
100
  createCanonryMcpServer
101
- } from "./chunk-IV6VZ63E.js";
101
+ } from "./chunk-MB3IUJ6Z.js";
102
102
  import {
103
103
  CANONRY_MCP_TIERS,
104
104
  CANONRY_MCP_TOOLKIT_NAMES
105
105
  } from "./chunk-NDTU7FGL.js";
106
106
  import {
107
107
  ApiClient
108
- } from "./chunk-ATT3ZCSS.js";
108
+ } from "./chunk-BICUWFV4.js";
109
109
  import {
110
110
  AGENT_PROVIDERS,
111
111
  adapterSupportsBatch,
@@ -136,21 +136,21 @@ import {
136
136
  saveConfigPatch,
137
137
  sentimentInstallReadiness,
138
138
  updateSharedProviderExecutionGate
139
- } from "./chunk-JE5QURTR.js";
139
+ } from "./chunk-NXFNXXYM.js";
140
140
  import {
141
141
  getBundledSkillSnapshots
142
- } from "./chunk-PPM6Z7CM.js";
142
+ } from "./chunk-HEL6DKDN.js";
143
143
  import {
144
144
  computeFactorAverages,
145
145
  deleteSiteCrawlGraphLayout,
146
146
  persistSiteCrawlGraphLayout,
147
147
  prepareSiteCrawlGraphLayout,
148
148
  recordedSharePct
149
- } from "./chunk-IZMT4WLZ.js";
149
+ } from "./chunk-6YW24AUN.js";
150
150
  import {
151
151
  CanonryMcpToolNames,
152
152
  canonryMcpTools
153
- } from "./chunk-GA2EHZTI.js";
153
+ } from "./chunk-3WPTMLQI.js";
154
154
  import {
155
155
  PACKAGE_VERSION
156
156
  } from "./chunk-AOJU5LKX.js";
@@ -159,16 +159,16 @@ import {
159
159
  } from "./chunk-HGMYEDAQ.js";
160
160
  import {
161
161
  IntelligenceService
162
- } from "./chunk-IY4AJ6DN.js";
162
+ } from "./chunk-5PA7SJ2M.js";
163
163
  import {
164
164
  addLogListener,
165
165
  createFastifyLogger,
166
166
  createLogger
167
- } from "./chunk-HZSE3WSG.js";
167
+ } from "./chunk-AZQNJX3T.js";
168
168
  import {
169
169
  computeCitedCompetitorDomains,
170
170
  determineCitationState
171
- } from "./chunk-DRGCVTYH.js";
171
+ } from "./chunk-JPM3VC6A.js";
172
172
  import {
173
173
  BingApiError,
174
174
  CC_CACHE_DIR,
@@ -181,6 +181,7 @@ import {
181
181
  GbpApiError,
182
182
  GoogleApiError,
183
183
  GoogleAuthError,
184
+ MCP_DELEGATED_KEY_MAX_AGE_MS,
184
185
  PLUGIN_DIR,
185
186
  SITE_REACHABILITY_CHECKS,
186
187
  SITE_REACHABILITY_CHECK_ID,
@@ -288,7 +289,7 @@ import {
288
289
  webhookResponseReason,
289
290
  webhookTargetRefusalReason,
290
291
  writeAuditLog
291
- } from "./chunk-7KOAP47B.js";
292
+ } from "./chunk-WLWOGBLJ.js";
292
293
  import {
293
294
  OperationalLogStore,
294
295
  SentimentIdempotencyConflict,
@@ -374,7 +375,7 @@ import {
374
375
  siteLivenessState,
375
376
  trafficSources,
376
377
  usageCounters
377
- } from "./chunk-O7WOCTBD.js";
378
+ } from "./chunk-PAIYFNNY.js";
378
379
  import {
379
380
  AGENT_MEMORY_VALUE_MAX_BYTES,
380
381
  AI_ENGINE_DOMAINS,
@@ -626,7 +627,7 @@ import {
626
627
  winnabilityClassLabel,
627
628
  withRetry,
628
629
  withSkippedProviders
629
- } from "./chunk-YXAKL5HM.js";
630
+ } from "./chunk-SF5JLMLG.js";
630
631
 
631
632
  // src/google-marketing-runtime.ts
632
633
  import crypto2 from "crypto";
@@ -3316,7 +3317,7 @@ import path5 from "path";
3316
3317
  import { isIP as isIP2 } from "net";
3317
3318
  import { fileURLToPath } from "url";
3318
3319
  import { setImmediate as yieldToEventLoop4 } from "timers/promises";
3319
- import { and as and26, eq as eq33, isNotNull as isNotNull5, isNull as isNull7, lte as lte3, max, ne as ne4, or as or4, sql as sql13 } from "drizzle-orm";
3320
+ import { and as and27, eq as eq33, isNotNull as isNotNull5, isNull as isNull8, lte as lte3, max, ne as ne4, or as or4, sql as sql13 } from "drizzle-orm";
3320
3321
 
3321
3322
  // src/seo-runtime.ts
3322
3323
  import { eq, inArray } from "drizzle-orm";
@@ -5809,7 +5810,7 @@ function nonBlank(value) {
5809
5810
  return trimmed ? trimmed : void 0;
5810
5811
  }
5811
5812
  function resolveBuildCommit(env = process.env) {
5812
- const embedded = true ? "7811ac751d0f8a0fecec447c7bc024943d24eeb5" : void 0;
5813
+ const embedded = true ? "4bcaf7411fada38e4780290ce3ebccdea236a5c5" : void 0;
5813
5814
  return nonBlank(embedded) ?? nonBlank(env.CANONRY_COMMIT);
5814
5815
  }
5815
5816
  function resolveInstanceIdentity(env = process.env) {
@@ -15638,8 +15639,7 @@ import { eq as eq27 } from "drizzle-orm";
15638
15639
 
15639
15640
  // src/agent/remote-mcp.ts
15640
15641
  import { Type } from "@earendil-works/pi-ai";
15641
- import { Client } from "@modelcontextprotocol/sdk/client/index.js";
15642
- import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
15642
+ import { Client, StreamableHTTPClientTransport } from "@modelcontextprotocol/client";
15643
15643
  var log24 = createLogger("RemoteMcp");
15644
15644
  var AERO_EXCLUDED_MCP_TOOLS = /* @__PURE__ */ new Set([]);
15645
15645
  async function connectStreamableHttp(server) {
@@ -15650,7 +15650,7 @@ async function connectStreamableHttp(server) {
15650
15650
  });
15651
15651
  const client = new Client(
15652
15652
  { name: "canonry-aero", version: "1.0.0" },
15653
- { capabilities: {} }
15653
+ { capabilities: {}, versionNegotiation: { mode: "auto" } }
15654
15654
  );
15655
15655
  await client.connect(transport);
15656
15656
  return client;
@@ -16971,12 +16971,15 @@ var ViewerAeroSessions = class {
16971
16971
  // src/mcp-http.ts
16972
16972
  import crypto25 from "crypto";
16973
16973
  import { isIP } from "net";
16974
- import { eq as eq29 } from "drizzle-orm";
16975
- import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
16976
- import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
16974
+ import { and as and24, eq as eq29, isNull as isNull6 } from "drizzle-orm";
16975
+ import { NodeStreamableHTTPServerTransport, toNodeHandler, toWebRequest } from "@modelcontextprotocol/node";
16976
+ import { McpServer, createMcpHandler, isInitializeRequest, isLegacyRequest } from "@modelcontextprotocol/server";
16977
16977
  var SESSION_IDLE_MS = 10 * 60 * 1e3;
16978
16978
  var SWEEP_INTERVAL_MS = 60 * 1e3;
16979
- function mintSessionKey(db, scopes, userId) {
16979
+ var MAX_CLEANUP_RETRIES_PER_SWEEP = 8;
16980
+ var MAX_PENDING_KEY_RETIREMENTS = 256;
16981
+ var MAX_CLEANUP_BACKOFF_MS = 10 * 60 * 1e3;
16982
+ function mintSessionKey(db, scopes, userId, oauthGrantId, createdAt) {
16980
16983
  const raw = `cnry_${crypto25.randomBytes(24).toString("hex")}`;
16981
16984
  const id = crypto25.randomUUID();
16982
16985
  db.insert(apiKeys).values({
@@ -16986,18 +16989,10 @@ function mintSessionKey(db, scopes, userId) {
16986
16989
  keyPrefix: raw.slice(0, 9),
16987
16990
  scopes: [...scopes],
16988
16991
  delegatedUserId: userId,
16989
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
16992
+ delegatedOAuthGrantId: oauthGrantId,
16993
+ createdAt: new Date(createdAt).toISOString()
16990
16994
  }).run();
16991
- return { id, raw };
16992
- }
16993
- function revokeSessionKey(db, id) {
16994
- try {
16995
- db.transaction((tx) => {
16996
- tx.delete(dashboardSessions).where(eq29(dashboardSessions.apiKeyId, id)).run();
16997
- tx.update(apiKeys).set({ revokedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq29(apiKeys.id, id)).run();
16998
- });
16999
- } catch {
17000
- }
16995
+ return { id, raw, expiresAt: createdAt + MCP_DELEGATED_KEY_MAX_AGE_MS };
17001
16996
  }
17002
16997
  function mcpTransportPaths() {
17003
16998
  const paths = ["/mcp", "/mcp/readonly"];
@@ -17052,18 +17047,95 @@ function mcpHttpHealth(app, apiPrefix, hosted) {
17052
17047
  }
17053
17048
  function registerMcpHttpRoutes(scope, opts) {
17054
17049
  const sessions = /* @__PURE__ */ new Map();
17050
+ const legacyServers = /* @__PURE__ */ new Set();
17051
+ const modernExchanges = /* @__PURE__ */ new Set();
17055
17052
  const now = opts.now ?? (() => Date.now());
17053
+ const pendingKeyRetirements = /* @__PURE__ */ new Map();
17054
+ let shuttingDown = false;
17055
+ function withoutLockWait(work) {
17056
+ const timeout = opts.db.$client.pragma("busy_timeout", { simple: true });
17057
+ try {
17058
+ opts.db.$client.pragma("busy_timeout = 0");
17059
+ return work();
17060
+ } finally {
17061
+ opts.db.$client.pragma(`busy_timeout = ${timeout}`);
17062
+ }
17063
+ }
17064
+ function attemptKeyRetirement(id, remove) {
17065
+ try {
17066
+ withoutLockWait(() => {
17067
+ opts.db.transaction((tx) => {
17068
+ tx.delete(dashboardSessions).where(eq29(dashboardSessions.apiKeyId, id)).run();
17069
+ tx.update(apiKeys).set({ revokedAt: new Date(now()).toISOString() }).where(and24(eq29(apiKeys.id, id), isNull6(apiKeys.revokedAt))).run();
17070
+ });
17071
+ if (remove) {
17072
+ try {
17073
+ opts.db.delete(apiKeys).where(eq29(apiKeys.id, id)).run();
17074
+ } catch (error) {
17075
+ scope.log.warn({ err: error, keyId: id }, "Retained revoked MCP request key after deletion failed");
17076
+ }
17077
+ }
17078
+ });
17079
+ return true;
17080
+ } catch (error) {
17081
+ scope.log.error({ err: error, keyId: id }, "Failed to retire MCP key; cleanup remains bounded by its expiry");
17082
+ return false;
17083
+ }
17084
+ }
17085
+ function retireSessionKey(key, remove = false) {
17086
+ const queued = pendingKeyRetirements.get(key.id);
17087
+ if (queued) {
17088
+ queued.remove ||= remove;
17089
+ return;
17090
+ }
17091
+ if (attemptKeyRetirement(key.id, remove)) return;
17092
+ if (pendingKeyRetirements.size >= MAX_PENDING_KEY_RETIREMENTS) {
17093
+ const oldest = pendingKeyRetirements.keys().next().value;
17094
+ if (oldest !== void 0) pendingKeyRetirements.delete(oldest);
17095
+ scope.log.warn("MCP cleanup queue is full; oldest credential remains bounded by its expiry");
17096
+ }
17097
+ pendingKeyRetirements.set(key.id, { remove, expiresAt: key.expiresAt, nextAttemptAt: now() + SWEEP_INTERVAL_MS, attempts: 1 });
17098
+ }
17099
+ function retryKeyRetirements() {
17100
+ let attempted = 0;
17101
+ for (const [id, queued] of pendingKeyRetirements) {
17102
+ if (queued.expiresAt <= now()) {
17103
+ pendingKeyRetirements.delete(id);
17104
+ continue;
17105
+ }
17106
+ if (queued.nextAttemptAt > now()) continue;
17107
+ if (attempted++ >= MAX_CLEANUP_RETRIES_PER_SWEEP) break;
17108
+ if (attemptKeyRetirement(id, queued.remove)) pendingKeyRetirements.delete(id);
17109
+ else {
17110
+ queued.nextAttemptAt = now() + Math.min(SWEEP_INTERVAL_MS * 2 ** queued.attempts, MAX_CLEANUP_BACKOFF_MS);
17111
+ queued.attempts++;
17112
+ }
17113
+ }
17114
+ }
17056
17115
  async function dropSession(id) {
17057
- const session = sessions.get(id);
17058
- if (!session) return;
17059
- sessions.delete(id);
17060
- await session.close().catch(() => {
17061
- });
17116
+ await sessions.get(id)?.close().catch((error) => scope.log.error({ err: error }, "Failed to close legacy MCP session"));
17062
17117
  }
17063
17118
  const sweep = setInterval(() => {
17064
- const cutoff = now() - SESSION_IDLE_MS;
17065
- for (const [id, session] of sessions) {
17066
- if (session.lastSeenAt < cutoff) void dropSession(id);
17119
+ try {
17120
+ retryKeyRetirements();
17121
+ withoutLockWait(() => {
17122
+ const cutoff = now() - SESSION_IDLE_MS;
17123
+ for (const session of legacyServers) {
17124
+ const key = session.sessionKeyId ? opts.db.select({ revokedAt: apiKeys.revokedAt }).from(apiKeys).where(eq29(apiKeys.id, session.sessionKeyId)).get() : void 0;
17125
+ if (session.lastSeenAt < cutoff || session.expiresAt !== void 0 && session.expiresAt <= now() || session.sessionKeyId && (!key || key.revokedAt)) {
17126
+ void session.close().catch((error) => scope.log.error({ err: error }, "Failed to close expired MCP session"));
17127
+ }
17128
+ }
17129
+ for (const exchange of modernExchanges) {
17130
+ if (!exchange.sessionKey) continue;
17131
+ const key = opts.db.select({ revokedAt: apiKeys.revokedAt }).from(apiKeys).where(eq29(apiKeys.id, exchange.sessionKey.id)).get();
17132
+ if (!key || key.revokedAt || exchange.sessionKey.expiresAt <= now()) {
17133
+ void exchange.close().catch((error) => scope.log.error({ err: error }, "Failed to close expired MCP request"));
17134
+ }
17135
+ }
17136
+ });
17137
+ } catch (error) {
17138
+ scope.log.error({ err: error }, "MCP lifecycle sweep failed; will retry on the next interval");
17067
17139
  }
17068
17140
  }, SWEEP_INTERVAL_MS);
17069
17141
  sweep.unref();
@@ -17077,9 +17149,15 @@ function registerMcpHttpRoutes(scope, opts) {
17077
17149
  return payload;
17078
17150
  });
17079
17151
  }
17080
- scope.addHook("onClose", async () => {
17152
+ scope.addHook("preClose", async () => {
17153
+ shuttingDown = true;
17081
17154
  clearInterval(sweep);
17082
- await Promise.all([...sessions.keys()].map((id) => dropSession(id)));
17155
+ await Promise.all([...legacyServers].map((session) => session.close().catch((error) => scope.log.error({ err: error }, "Failed to close MCP session during shutdown"))));
17156
+ await Promise.all([...modernExchanges].map((exchange) => exchange.close().catch((error) => scope.log.error({ err: error }, "Failed to close MCP request during shutdown"))));
17157
+ retryKeyRetirements();
17158
+ if (pendingKeyRetirements.size > 0) {
17159
+ scope.log.warn({ count: pendingKeyRetirements.size }, "MCP credential cleanup remains pending at shutdown; credentials remain bounded by expiry");
17160
+ }
17083
17161
  });
17084
17162
  function callerBearer(request) {
17085
17163
  const header = request.headers.authorization;
@@ -17090,53 +17168,74 @@ function registerMcpHttpRoutes(scope, opts) {
17090
17168
  if (trimmed.slice(0, prefix.length).toLowerCase() !== prefix) return null;
17091
17169
  return trimmed.slice(prefix.length).trim() || null;
17092
17170
  }
17093
- async function openSession(request, authorizationId, segment) {
17094
- const bearer = callerBearer(request);
17095
- if (!bearer) return null;
17171
+ function createAuthorizedServer(request, segment, bearer, signal, modern = false, needsApiCredential = true) {
17172
+ if (shuttingDown) throw new Error("MCP server is shutting down");
17096
17173
  const scopes = request.principal?.scopes ?? request.apiKey?.scopes ?? [];
17097
17174
  let sessionKey = null;
17098
- if (!request.apiKey && request.principal?.kind === "user") {
17099
- sessionKey = mintSessionKey(opts.db, scopes, request.principal.id);
17175
+ if (needsApiCredential && !request.apiKey && request.principal?.kind === "user") {
17176
+ if (!request.principal.oauthGrantId) throw forbidden("MCP OAuth credentials require an active authorization grant.");
17177
+ sessionKey = mintSessionKey(opts.db, segment.readOnly ? ["read"] : scopes, request.principal.id, request.principal.oauthGrantId, now());
17100
17178
  }
17101
17179
  const client = new ApiClient(opts.selfApiUrl, sessionKey?.raw ?? bearer, {
17102
17180
  skipProbe: true,
17181
+ storedOnly: segment.readOnly,
17182
+ signal,
17103
17183
  clientName: `canonry-mcp/${PACKAGE_VERSION}`,
17104
17184
  surface: "mcp-http",
17105
17185
  // Correlation only: this is never accepted as caller identity or authority.
17106
- actorSession: crypto25.randomUUID()
17186
+ ...modern ? {} : { actorSession: crypto25.randomUUID() }
17107
17187
  });
17108
17188
  let server;
17109
17189
  try {
17110
17190
  server = createCanonryMcpServer({
17111
- scope: segment.readOnly || isReadOnlyKey(scopes) ? "read-only" : "all",
17191
+ scope: segment.readOnly ? "stored-only" : isReadOnlyKey(scopes) ? "read-only" : "all",
17112
17192
  credentialScopes: scopes,
17113
17193
  operator: request.operatorAccess === true,
17194
+ connectionProfile: Boolean(opts.issuer && !request.principal?.projectId && (request.principal?.kind === "user" || request.principal?.delegatedUser) && scopes.some((scope2) => scope2 === "read" || scope2 === "*")),
17114
17195
  tiers: segment.tiers,
17115
17196
  clientFactory: () => client,
17116
17197
  updateAvailable: opts.getUpdateAvailable
17117
17198
  });
17199
+ server.server.registerCapabilities({ tools: { listChanged: false }, resources: { listChanged: false, subscribe: false } });
17118
17200
  } catch (error) {
17119
- if (sessionKey) revokeSessionKey(opts.db, sessionKey.id);
17201
+ if (sessionKey) retireSessionKey(sessionKey, modern);
17120
17202
  throw error;
17121
17203
  }
17122
- const transport = new StreamableHTTPServerTransport({
17204
+ return { server, sessionKey };
17205
+ }
17206
+ async function openSession(request, authorizationId, segment) {
17207
+ const bearer = callerBearer(request);
17208
+ if (!bearer) return null;
17209
+ const { server, sessionKey } = createAuthorizedServer(request, segment, bearer);
17210
+ const transport = new NodeStreamableHTTPServerTransport({
17123
17211
  sessionIdGenerator: () => crypto25.randomUUID(),
17124
17212
  onsessioninitialized: (id) => {
17125
- sessions.set(id, { transport, close, authorizationId, segmentId: segment.id, lastSeenAt: now() });
17213
+ if (!shuttingDown && !closing) sessions.set(id, session);
17126
17214
  }
17127
17215
  });
17128
- async function close() {
17129
- if (sessionKey) revokeSessionKey(opts.db, sessionKey.id);
17130
- try {
17131
- await transport.close();
17132
- } finally {
17133
- await server.close();
17134
- }
17216
+ let closing;
17217
+ const session = { transport, close, authorizationId, segmentId: segment.id, sessionKeyId: sessionKey?.id, expiresAt: sessionKey?.expiresAt, lastSeenAt: now() };
17218
+ legacyServers.add(session);
17219
+ function close() {
17220
+ if (closing) return closing;
17221
+ closing = Promise.resolve().then(async () => {
17222
+ if (sessionKey) retireSessionKey(sessionKey);
17223
+ const id = transport.sessionId;
17224
+ if (id) sessions.delete(id);
17225
+ try {
17226
+ await transport.close();
17227
+ } finally {
17228
+ try {
17229
+ await server.close();
17230
+ } finally {
17231
+ legacyServers.delete(session);
17232
+ }
17233
+ }
17234
+ });
17235
+ return closing;
17135
17236
  }
17136
17237
  transport.onclose = () => {
17137
- const id = transport.sessionId;
17138
- if (id) sessions.delete(id);
17139
- if (sessionKey) revokeSessionKey(opts.db, sessionKey.id);
17238
+ void close().catch((error) => scope.log.error({ err: error }, "Failed to close legacy MCP transport"));
17140
17239
  };
17141
17240
  try {
17142
17241
  await server.connect(transport);
@@ -17145,15 +17244,68 @@ function registerMcpHttpRoutes(scope, opts) {
17145
17244
  });
17146
17245
  throw error;
17147
17246
  }
17148
- return { transport, close, authorizationId, segmentId: segment.id, lastSeenAt: now() };
17247
+ return session;
17248
+ }
17249
+ async function handleModern(request, reply, segment, bearer) {
17250
+ const abort = new AbortController();
17251
+ let closing;
17252
+ const exchange = {
17253
+ close: () => {
17254
+ if (closing) return closing;
17255
+ abort.abort();
17256
+ if (exchange.sessionKey) retireSessionKey(exchange.sessionKey, true);
17257
+ modernExchanges.delete(exchange);
17258
+ closing = handler.close().catch((error) => {
17259
+ scope.log.error({ err: error }, "Failed to close MCP request transport");
17260
+ throw error;
17261
+ });
17262
+ return closing;
17263
+ }
17264
+ };
17265
+ const reportError = (error) => scope.log.error({ err: error }, "Modern MCP request failed");
17266
+ const handler = createMcpHandler(({ requestInfo }) => {
17267
+ if (shuttingDown) throw new Error("MCP server is shutting down");
17268
+ const body = request.body;
17269
+ const method = body && typeof body === "object" ? body.method : void 0;
17270
+ if (method === "subscriptions/listen") {
17271
+ return new McpServer({ name: "canonry", version: PACKAGE_VERSION });
17272
+ }
17273
+ const signal = requestInfo ? AbortSignal.any([abort.signal, requestInfo.signal]) : abort.signal;
17274
+ const opened = createAuthorizedServer(request, segment, bearer, signal, true, method === "tools/call");
17275
+ exchange.sessionKey = opened.sessionKey ?? void 0;
17276
+ return opened.server;
17277
+ }, { legacy: "reject", onerror: reportError });
17278
+ modernExchanges.add(exchange);
17279
+ try {
17280
+ await toNodeHandler(handler, { onerror: reportError })(request.raw, reply.raw, request.body);
17281
+ } finally {
17282
+ await exchange.close();
17283
+ }
17149
17284
  }
17150
17285
  async function handle(request, reply, segment) {
17286
+ if (shuttingDown) {
17287
+ await reply.status(503).send({ jsonrpc: "2.0", id: null, error: { code: -32e3, message: "MCP server is shutting down." } });
17288
+ return;
17289
+ }
17151
17290
  const principal = request.principal ?? request.apiKey;
17152
17291
  const bearer = callerBearer(request);
17153
17292
  if (!principal || !bearer) {
17154
17293
  await reply.status(401).send({ error: { code: "AUTH_REQUIRED", message: "Authentication required." } });
17155
17294
  return;
17156
17295
  }
17296
+ if (segment.readOnly && !request.apiKey && request.principal?.kind === "user" && !principal.scopes.some((scope2) => scope2 === "read" || scope2 === "*")) {
17297
+ throw forbidden('The hosted read-only MCP endpoint requires the "read" scope.');
17298
+ }
17299
+ const webRequest = await toWebRequest(request.raw, request.body);
17300
+ const legacy = await isLegacyRequest(webRequest, request.body);
17301
+ if (shuttingDown) {
17302
+ await reply.status(503).send({ jsonrpc: "2.0", id: null, error: { code: -32e3, message: "MCP server is shutting down." } });
17303
+ return;
17304
+ }
17305
+ if (!legacy) {
17306
+ await handleModern(request, reply, segment, bearer);
17307
+ return;
17308
+ }
17157
17309
  const authorizationId = hashApiKey(JSON.stringify({
17158
17310
  bearerHash: hashApiKey(bearer),
17159
17311
  kind: request.principal?.kind ?? "api-key",
@@ -17165,6 +17317,11 @@ function registerMcpHttpRoutes(scope, opts) {
17165
17317
  const sessionId = request.headers["mcp-session-id"];
17166
17318
  const existing = typeof sessionId === "string" ? sessions.get(sessionId) : void 0;
17167
17319
  if (existing) {
17320
+ if (existing.expiresAt !== void 0 && existing.expiresAt <= now() && typeof sessionId === "string") {
17321
+ await dropSession(sessionId);
17322
+ await reply.status(404).send({ error: { code: "NOT_FOUND", message: "Unknown MCP session." } });
17323
+ return;
17324
+ }
17168
17325
  if (existing.authorizationId !== authorizationId || existing.segmentId !== segment.id) {
17169
17326
  await reply.status(404).send({ error: { code: "NOT_FOUND", message: "Unknown MCP session." } });
17170
17327
  return;
@@ -17186,6 +17343,11 @@ function registerMcpHttpRoutes(scope, opts) {
17186
17343
  await reply.status(401).send({ error: { code: "AUTH_REQUIRED", message: "Authentication required." } });
17187
17344
  return;
17188
17345
  }
17346
+ if (shuttingDown) {
17347
+ await opened.close();
17348
+ await reply.status(503).send({ jsonrpc: "2.0", id: null, error: { code: -32e3, message: "MCP server is shutting down." } });
17349
+ return;
17350
+ }
17189
17351
  let handled = false;
17190
17352
  try {
17191
17353
  await opened.transport.handleRequest(request.raw, reply.raw, request.body);
@@ -18401,7 +18563,7 @@ function clipText(value, length) {
18401
18563
  }
18402
18564
 
18403
18565
  // src/research-runner.ts
18404
- import { and as and24, eq as eq31, inArray as inArray13, sql as sql11 } from "drizzle-orm";
18566
+ import { and as and25, eq as eq31, inArray as inArray13, sql as sql11 } from "drizzle-orm";
18405
18567
  var unfinishedResearchQueryStatuses = [ResearchQueryStatuses.queued, ResearchQueryStatuses.running];
18406
18568
  function finalResearchRunStatus(completed, failed, waiting) {
18407
18569
  if (waiting > 0) return ResearchRunStatuses["waiting-upstream"];
@@ -18409,13 +18571,13 @@ function finalResearchRunStatus(completed, failed, waiting) {
18409
18571
  return completed > 0 ? ResearchRunStatuses.partial : ResearchRunStatuses.failed;
18410
18572
  }
18411
18573
  async function executeResearchRun(db, registry, runId, projectId, options = {}) {
18412
- const run = db.select().from(researchRuns).where(and24(eq31(researchRuns.id, runId), eq31(researchRuns.projectId, projectId))).get();
18574
+ const run = db.select().from(researchRuns).where(and25(eq31(researchRuns.id, runId), eq31(researchRuns.projectId, projectId))).get();
18413
18575
  if (!run || run.status !== ResearchRunStatuses.queued) return;
18414
18576
  const project = db.select().from(projects).where(eq31(projects.id, projectId)).get();
18415
18577
  if (!project) return;
18416
18578
  const provider = registry.get(run.provider);
18417
18579
  const now = (/* @__PURE__ */ new Date()).toISOString();
18418
- const claim = db.update(researchRuns).set({ status: ResearchRunStatuses.running, startedAt: now }).where(and24(eq31(researchRuns.id, runId), eq31(researchRuns.status, ResearchRunStatuses.queued))).run();
18580
+ const claim = db.update(researchRuns).set({ status: ResearchRunStatuses.running, startedAt: now }).where(and25(eq31(researchRuns.id, runId), eq31(researchRuns.status, ResearchRunStatuses.queued))).run();
18419
18581
  if (claim.changes !== 1) return;
18420
18582
  const elapsed = startOutcomeTimer();
18421
18583
  const requested = currentOutcomeOrigin();
@@ -18460,7 +18622,7 @@ async function executeResearchRun(db, registry, runId, projectId, options = {})
18460
18622
  await mapWithConcurrency(rows, Math.max(1, provider.config.quotaPolicy.maxConcurrency), async (row) => {
18461
18623
  try {
18462
18624
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
18463
- db.update(researchRunQueries).set({ status: ResearchQueryStatuses.running, startedAt }).where(and24(eq31(researchRunQueries.id, row.id), eq31(researchRunQueries.status, ResearchQueryStatuses.queued))).run();
18625
+ db.update(researchRunQueries).set({ status: ResearchQueryStatuses.running, startedAt }).where(and25(eq31(researchRunQueries.id, row.id), eq31(researchRunQueries.status, ResearchQueryStatuses.queued))).run();
18464
18626
  const raw = await gate.run(async () => {
18465
18627
  dispatched++;
18466
18628
  return provider.adapter.executeTrackedQuery({ query: row.queryText, canonicalDomains: domains, competitorDomains, ...run.location ? { location: run.location } : {} }, config);
@@ -18491,7 +18653,7 @@ async function executeResearchRun(db, registry, runId, projectId, options = {})
18491
18653
  rawResponse: raw.rawResponse,
18492
18654
  answerEvidence: raw.evidence ?? null,
18493
18655
  finishedAt: (/* @__PURE__ */ new Date()).toISOString()
18494
- }).where(and24(eq31(researchRunQueries.id, row.id), eq31(researchRunQueries.status, ResearchQueryStatuses.running))).run();
18656
+ }).where(and25(eq31(researchRunQueries.id, row.id), eq31(researchRunQueries.status, ResearchQueryStatuses.running))).run();
18495
18657
  if (completed.changes === 1) incrementResearchProgress(db, runId, "completedQueries");
18496
18658
  } catch (error) {
18497
18659
  if (!(error instanceof IncompleteAnswerError && error.resumable)) queryFailure ??= error;
@@ -18546,7 +18708,7 @@ function incrementResearchProgress(db, runId, column) {
18546
18708
  }
18547
18709
  function markResearchQueryFailed(db, runId, queryId, error) {
18548
18710
  const message = describeError(error);
18549
- const unfinished = and24(eq31(researchRunQueries.id, queryId), inArray13(researchRunQueries.status, unfinishedResearchQueryStatuses));
18711
+ const unfinished = and25(eq31(researchRunQueries.id, queryId), inArray13(researchRunQueries.status, unfinishedResearchQueryStatuses));
18550
18712
  if (error instanceof IncompleteAnswerError && error.resumable) {
18551
18713
  db.update(researchRunQueries).set({
18552
18714
  status: ResearchQueryStatuses["waiting-upstream"],
@@ -18562,7 +18724,7 @@ function markResearchQueryFailed(db, runId, queryId, error) {
18562
18724
  if (failed.changes === 1) incrementResearchProgress(db, runId, "failedQueries");
18563
18725
  }
18564
18726
  function markUnfinishedResearchQueriesFailed(db, runId, error) {
18565
- db.update(researchRunQueries).set({ status: ResearchQueryStatuses.failed, error, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(and24(eq31(researchRunQueries.researchRunId, runId), inArray13(researchRunQueries.status, unfinishedResearchQueryStatuses))).run();
18727
+ db.update(researchRunQueries).set({ status: ResearchQueryStatuses.failed, error, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(and25(eq31(researchRunQueries.researchRunId, runId), inArray13(researchRunQueries.status, unfinishedResearchQueryStatuses))).run();
18566
18728
  }
18567
18729
  function finalizeResearchRun(db, runId, fatalError) {
18568
18730
  const rows = db.select({ status: researchRunQueries.status }).from(researchRunQueries).where(eq31(researchRunQueries.researchRunId, runId)).all();
@@ -18582,7 +18744,7 @@ function finalizeResearchRun(db, runId, fatalError) {
18582
18744
  }
18583
18745
 
18584
18746
  // src/google-marketing-sync.ts
18585
- import { and as and25, eq as eq32, isNull as isNull6, sql as sql12 } from "drizzle-orm";
18747
+ import { and as and26, eq as eq32, isNull as isNull7, sql as sql12 } from "drizzle-orm";
18586
18748
  var RUNTIME_FAILURE_REASONS = {
18587
18749
  INVALID_INPUT: OutcomeReasonCodes.VALIDATION,
18588
18750
  NOT_CONFIGURED: OutcomeReasonCodes.NOT_CONNECTED,
@@ -18619,7 +18781,7 @@ function assertSnapshotContext(metadata, expected) {
18619
18781
  }
18620
18782
  }
18621
18783
  function requireSyncRun(db, runId, projectId, kind) {
18622
- const run = db.select().from(runs).where(and25(
18784
+ const run = db.select().from(runs).where(and26(
18623
18785
  eq32(runs.id, runId),
18624
18786
  eq32(runs.projectId, projectId),
18625
18787
  eq32(runs.kind, kind)
@@ -18638,7 +18800,7 @@ function markFailed(db, runId, projectId, kind, expectedStatus, error) {
18638
18800
  status: RunStatuses.failed,
18639
18801
  error: serializeRunError({ message: safeErrorMessage(error) }),
18640
18802
  finishedAt: (/* @__PURE__ */ new Date()).toISOString()
18641
- }).where(and25(
18803
+ }).where(and26(
18642
18804
  eq32(runs.id, runId),
18643
18805
  eq32(runs.projectId, projectId),
18644
18806
  eq32(runs.kind, kind),
@@ -18646,7 +18808,7 @@ function markFailed(db, runId, projectId, kind, expectedStatus, error) {
18646
18808
  )).run();
18647
18809
  }
18648
18810
  function claimSyncRun(db, runId, projectId, kind, startedAt) {
18649
- const claim = db.update(runs).set({ status: RunStatuses.running, startedAt, error: null }).where(and25(
18811
+ const claim = db.update(runs).set({ status: RunStatuses.running, startedAt, error: null }).where(and26(
18650
18812
  eq32(runs.id, runId),
18651
18813
  eq32(runs.projectId, projectId),
18652
18814
  eq32(runs.kind, kind),
@@ -18714,7 +18876,7 @@ async function executeGoogleAdsMarketingSync(db, runtime, runId, projectId) {
18714
18876
  status: RunStatuses.completed,
18715
18877
  finishedAt,
18716
18878
  error: null
18717
- }).where(and25(
18879
+ }).where(and26(
18718
18880
  eq32(runs.id, runId),
18719
18881
  eq32(runs.projectId, projectId),
18720
18882
  eq32(runs.kind, RunKinds["google-ads-sync"]),
@@ -18738,11 +18900,11 @@ async function executeGoogleAdsMarketingSync(db, runtime, runId, projectId) {
18738
18900
  lastMetricsSnapshotAt: result.metrics?.metadata.capturedAt ?? connection.lastMetricsSnapshotAt,
18739
18901
  lastMetricsSnapshotId: result.metrics?.metadata.id ?? connection.lastMetricsSnapshotId,
18740
18902
  updatedAt: finishedAt
18741
- }).where(and25(
18903
+ }).where(and26(
18742
18904
  eq32(googleAdsConnections.id, connection.id),
18743
18905
  eq32(googleAdsConnections.projectId, projectId),
18744
18906
  eq32(googleAdsConnections.selectedCustomerId, connection.selectedCustomerId),
18745
- connection.selectedLoginCustomerId === null ? isNull6(googleAdsConnections.selectedLoginCustomerId) : eq32(googleAdsConnections.selectedLoginCustomerId, connection.selectedLoginCustomerId),
18907
+ connection.selectedLoginCustomerId === null ? isNull7(googleAdsConnections.selectedLoginCustomerId) : eq32(googleAdsConnections.selectedLoginCustomerId, connection.selectedLoginCustomerId),
18746
18908
  eq32(googleAdsConnections.selectionGeneration, connection.selectionGeneration)
18747
18909
  )).run();
18748
18910
  if (connectionClaim.changes !== 1) {
@@ -18823,7 +18985,7 @@ async function executeGtmMarketingSync(db, runtime, runId, projectId) {
18823
18985
  status: RunStatuses.completed,
18824
18986
  finishedAt,
18825
18987
  error: null
18826
- }).where(and25(
18988
+ }).where(and26(
18827
18989
  eq32(runs.id, runId),
18828
18990
  eq32(runs.projectId, projectId),
18829
18991
  eq32(runs.kind, RunKinds["gtm-sync"]),
@@ -18848,12 +19010,12 @@ async function executeGtmMarketingSync(db, runtime, runId, projectId) {
18848
19010
  lastSnapshotAt: snapshot.metadata.capturedAt,
18849
19011
  lastSnapshotId: snapshot.metadata.id,
18850
19012
  updatedAt: finishedAt
18851
- }).where(and25(
19013
+ }).where(and26(
18852
19014
  eq32(gtmConnections.id, connection.id),
18853
19015
  eq32(gtmConnections.projectId, projectId),
18854
19016
  eq32(gtmConnections.selectedAccountId, connection.selectedAccountId),
18855
19017
  eq32(gtmConnections.selectedContainerId, connection.selectedContainerId),
18856
- connection.selectedWorkspaceId === null ? isNull6(gtmConnections.selectedWorkspaceId) : eq32(gtmConnections.selectedWorkspaceId, connection.selectedWorkspaceId),
19018
+ connection.selectedWorkspaceId === null ? isNull7(gtmConnections.selectedWorkspaceId) : eq32(gtmConnections.selectedWorkspaceId, connection.selectedWorkspaceId),
18857
19019
  eq32(gtmConnections.selectionGeneration, connection.selectionGeneration)
18858
19020
  )).run();
18859
19021
  if (connectionClaim.changes !== 1) {
@@ -20262,13 +20424,13 @@ async function createServer(opts) {
20262
20424
  const currentPasswordFingerprint = () => hashSessionToken(opts.config.dashboardPasswordHash ?? "");
20263
20425
  opts.db.delete(dashboardSessions).where(or4(
20264
20426
  eq33(dashboardSessions.apiKeyHash, ""),
20265
- and26(
20427
+ and27(
20266
20428
  isNotNull5(dashboardSessions.passwordFingerprint),
20267
20429
  ne4(dashboardSessions.passwordFingerprint, currentPasswordFingerprint())
20268
20430
  )
20269
20431
  )).run();
20270
20432
  if (!opts.config.dashboardPasswordHash) {
20271
- const lastPasswordEventAt = (action) => opts.db.select({ at: max(auditLog.createdAt) }).from(auditLog).where(and26(eq33(auditLog.entityType, "dashboard-password"), eq33(auditLog.action, action))).get()?.at ?? null;
20433
+ const lastPasswordEventAt = (action) => opts.db.select({ at: max(auditLog.createdAt) }).from(auditLog).where(and27(eq33(auditLog.entityType, "dashboard-password"), eq33(auditLog.action, action))).get()?.at ?? null;
20272
20434
  const createdAt = lastPasswordEventAt("dashboard-password.created");
20273
20435
  const clearedAt = lastPasswordEventAt("dashboard-password.cleared");
20274
20436
  if (createdAt !== null && (clearedAt === null || clearedAt < createdAt)) {
@@ -20302,7 +20464,7 @@ async function createServer(opts) {
20302
20464
  currentApiKeyHash: apiKeys.keyHash,
20303
20465
  passwordFingerprint: dashboardSessions.passwordFingerprint,
20304
20466
  expiresAt: dashboardSessions.expiresAt
20305
- }).from(dashboardSessions).innerJoin(apiKeys, eq33(apiKeys.id, dashboardSessions.apiKeyId)).where(and26(eq33(dashboardSessions.tokenHash, tokenHash), isNull7(apiKeys.revokedAt))).get();
20467
+ }).from(dashboardSessions).innerJoin(apiKeys, eq33(apiKeys.id, dashboardSessions.apiKeyId)).where(and27(eq33(dashboardSessions.tokenHash, tokenHash), isNull8(apiKeys.revokedAt))).get();
20306
20468
  if (!session) return null;
20307
20469
  const expiresAt = Date.parse(session.expiresAt);
20308
20470
  if (!Number.isFinite(expiresAt) || expiresAt <= Date.now() || session.apiKeyHash !== session.currentApiKeyHash || session.passwordFingerprint !== null && session.passwordFingerprint !== currentPasswordFingerprint()) {
@@ -20581,7 +20743,7 @@ async function createServer(opts) {
20581
20743
  opts.db.update(projects).set({ answerFieldsRecompute: null }).where(eq33(projects.id, projectId)).run();
20582
20744
  if (recomputeDebts.get(projectId) === debt) recomputeDebts.delete(projectId);
20583
20745
  } else if (debt.latestFull === 0) {
20584
- opts.db.update(projects).set({ answerFieldsRecompute: "competitors" }).where(and26(eq33(projects.id, projectId), eq33(projects.answerFieldsRecompute, "full"))).run();
20746
+ opts.db.update(projects).set({ answerFieldsRecompute: "competitors" }).where(and27(eq33(projects.id, projectId), eq33(projects.answerFieldsRecompute, "full"))).run();
20585
20747
  }
20586
20748
  } catch (err) {
20587
20749
  app.log.error({ err, projectId }, "could not clear the owed answer field recompute");