@ainyc/canonry 4.186.8 → 4.186.9

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 (27) hide show
  1. package/assets/assets/{AuditHistoryPanel-9q5v4zHj.js → AuditHistoryPanel-aXPOAK7n.js} +1 -1
  2. package/assets/assets/{BacklinksPage-C2QVWfQf.js → BacklinksPage-Bim-7fNk.js} +1 -1
  3. package/assets/assets/{HistoryPage-oLCQA7-T.js → HistoryPage-B8RyIjRC.js} +1 -1
  4. package/assets/assets/{MeasurementPropertyPage-DUYt5ceA.js → MeasurementPropertyPage-BlxY3qHW.js} +1 -1
  5. package/assets/assets/ProjectPage-BMZrEjBE.js +9 -0
  6. package/assets/assets/{RunRow-kaFXI_VP.js → RunRow-B28gp-6g.js} +1 -1
  7. package/assets/assets/{RunsPage-CQizRJxf.js → RunsPage-BXm6DVdt.js} +1 -1
  8. package/assets/assets/{SettingsPage-BHh4Y7Qg.js → SettingsPage-CJX7c5HH.js} +1 -1
  9. package/assets/assets/{SiteHealthSection-DjmPK0Tg.js → SiteHealthSection-Ys8sVumO.js} +3 -3
  10. package/assets/assets/{TrafficPage-B3N0r5gW.js → TrafficPage-wPdIYn8H.js} +1 -1
  11. package/assets/assets/{TrafficSourceDetailPage-C27GkPF4.js → TrafficSourceDetailPage-C813qZsA.js} +1 -1
  12. package/assets/assets/{extract-error-message-DlmIEdy_.js → extract-error-message-C3RpUAm_.js} +1 -1
  13. package/assets/assets/{index-DVa1lkiE.js → index-Dr_-IUJz.js} +23 -23
  14. package/assets/assets/{react-sigma_core.esm.min-BBOzBU_n.js → react-sigma_core.esm.min-LmiGNvR6.js} +1 -1
  15. package/assets/index.html +1 -1
  16. package/dist/{chunk-7CG3QE4N.js → chunk-EMSDPSQM.js} +130 -68
  17. package/dist/{chunk-CFJHRXZ3.js → chunk-LNBX7RTN.js} +556 -536
  18. package/dist/{chunk-YYNL73BT.js → chunk-O26JKJXV.js} +27 -4
  19. package/dist/{chunk-42P3CXIH.js → chunk-U3M2VALC.js} +20 -3
  20. package/dist/{chunk-7TYSFZEG.js → chunk-XD2ZD2BP.js} +2 -2
  21. package/dist/cli.js +5 -5
  22. package/dist/index.d.ts +8 -0
  23. package/dist/index.js +4 -4
  24. package/dist/{intelligence-service-KBHLNDGO.js → intelligence-service-UZGA24EF.js} +2 -2
  25. package/dist/mcp.js +3 -3
  26. package/package.json +9 -9
  27. package/assets/assets/ProjectPage-Dsao_AiN.js +0 -9
@@ -1,4 +1,4 @@
1
- import{g as Bt,t as S,r as C}from"./vendor-tanstack-CYBjbqVr.js";import{e as Ht,G as Wt}from"./SiteHealthSection-DjmPK0Tg.js";import"./index-DVa1lkiE.js";import"./vendor-radix-BpmvLvpp.js";import"./vendor-recharts-FA0uySU7.js";import"./vendor-lucide-BovNTc41.js";import"./vendor-markdown-9tTeP8v7.js";import"./vendor-yaml-GWJz5H2G.js";import"./ChartPrimitives-DLEICrrL.js";function Vt(r,n){if(typeof r!="object"||!r)return r;var i=r[Symbol.toPrimitive];if(i!==void 0){var e=i.call(r,n);if(typeof e!="object")return e;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(r)}function ae(r){var n=Vt(r,"string");return typeof n=="symbol"?n:n+""}function A(r,n){if(!(r instanceof n))throw new TypeError("Cannot call a class as a function")}function He(r,n){for(var i=0;i<n.length;i++){var e=n[i];e.enumerable=e.enumerable||!1,e.configurable=!0,"value"in e&&(e.writable=!0),Object.defineProperty(r,ae(e.key),e)}}function L(r,n,i){return n&&He(r.prototype,n),i&&He(r,i),Object.defineProperty(r,"prototype",{writable:!1}),r}function Q(r){return Q=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(n){return n.__proto__||Object.getPrototypeOf(n)},Q(r)}function yt(){try{var r=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){}))}catch{}return(yt=function(){return!!r})()}function jt(r){if(r===void 0)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return r}function Zt(r,n){if(n&&(typeof n=="object"||typeof n=="function"))return n;if(n!==void 0)throw new TypeError("Derived constructors may only return object or undefined");return jt(r)}function I(r,n,i){return n=Q(n),Zt(r,yt()?Reflect.construct(n,i||[],Q(r).constructor):n.apply(r,i))}function xe(r,n){return xe=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(i,e){return i.__proto__=e,i},xe(r,n)}function G(r,n){if(typeof n!="function"&&n!==null)throw new TypeError("Super expression must either be null or a function");r.prototype=Object.create(n&&n.prototype,{constructor:{value:r,writable:!0,configurable:!0}}),Object.defineProperty(r,"prototype",{writable:!1}),n&&xe(r,n)}function qt(r){if(Array.isArray(r))return r}function Yt(r,n){var i=r==null?null:typeof Symbol<"u"&&r[Symbol.iterator]||r["@@iterator"];if(i!=null){var e,t,a,o,s=[],l=!0,h=!1;try{if(a=(i=i.call(r)).next,n===0){if(Object(i)!==i)return;l=!1}else for(;!(l=(e=a.call(i)).done)&&(s.push(e.value),s.length!==n);l=!0);}catch(u){h=!0,t=u}finally{try{if(!l&&i.return!=null&&(o=i.return(),Object(o)!==o))return}finally{if(h)throw t}}return s}}function Se(r,n){(n==null||n>r.length)&&(n=r.length);for(var i=0,e=Array(n);i<n;i++)e[i]=r[i];return e}function Tt(r,n){if(r){if(typeof r=="string")return Se(r,n);var i={}.toString.call(r).slice(8,-1);return i==="Object"&&r.constructor&&(i=r.constructor.name),i==="Map"||i==="Set"?Array.from(r):i==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i)?Se(r,n):void 0}}function Xt(){throw new TypeError(`Invalid attempt to destructure non-iterable instance.
1
+ import{g as Bt,t as S,r as C}from"./vendor-tanstack-CYBjbqVr.js";import{e as Ht,G as Wt}from"./SiteHealthSection-Ys8sVumO.js";import"./index-Dr_-IUJz.js";import"./vendor-radix-BpmvLvpp.js";import"./vendor-recharts-FA0uySU7.js";import"./vendor-lucide-BovNTc41.js";import"./vendor-markdown-9tTeP8v7.js";import"./vendor-yaml-GWJz5H2G.js";import"./ChartPrimitives-DLEICrrL.js";function Vt(r,n){if(typeof r!="object"||!r)return r;var i=r[Symbol.toPrimitive];if(i!==void 0){var e=i.call(r,n);if(typeof e!="object")return e;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(r)}function ae(r){var n=Vt(r,"string");return typeof n=="symbol"?n:n+""}function A(r,n){if(!(r instanceof n))throw new TypeError("Cannot call a class as a function")}function He(r,n){for(var i=0;i<n.length;i++){var e=n[i];e.enumerable=e.enumerable||!1,e.configurable=!0,"value"in e&&(e.writable=!0),Object.defineProperty(r,ae(e.key),e)}}function L(r,n,i){return n&&He(r.prototype,n),i&&He(r,i),Object.defineProperty(r,"prototype",{writable:!1}),r}function Q(r){return Q=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(n){return n.__proto__||Object.getPrototypeOf(n)},Q(r)}function yt(){try{var r=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){}))}catch{}return(yt=function(){return!!r})()}function jt(r){if(r===void 0)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return r}function Zt(r,n){if(n&&(typeof n=="object"||typeof n=="function"))return n;if(n!==void 0)throw new TypeError("Derived constructors may only return object or undefined");return jt(r)}function I(r,n,i){return n=Q(n),Zt(r,yt()?Reflect.construct(n,i||[],Q(r).constructor):n.apply(r,i))}function xe(r,n){return xe=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(i,e){return i.__proto__=e,i},xe(r,n)}function G(r,n){if(typeof n!="function"&&n!==null)throw new TypeError("Super expression must either be null or a function");r.prototype=Object.create(n&&n.prototype,{constructor:{value:r,writable:!0,configurable:!0}}),Object.defineProperty(r,"prototype",{writable:!1}),n&&xe(r,n)}function qt(r){if(Array.isArray(r))return r}function Yt(r,n){var i=r==null?null:typeof Symbol<"u"&&r[Symbol.iterator]||r["@@iterator"];if(i!=null){var e,t,a,o,s=[],l=!0,h=!1;try{if(a=(i=i.call(r)).next,n===0){if(Object(i)!==i)return;l=!1}else for(;!(l=(e=a.call(i)).done)&&(s.push(e.value),s.length!==n);l=!0);}catch(u){h=!0,t=u}finally{try{if(!l&&i.return!=null&&(o=i.return(),Object(o)!==o))return}finally{if(h)throw t}}return s}}function Se(r,n){(n==null||n>r.length)&&(n=r.length);for(var i=0,e=Array(n);i<n;i++)e[i]=r[i];return e}function Tt(r,n){if(r){if(typeof r=="string")return Se(r,n);var i={}.toString.call(r).slice(8,-1);return i==="Object"&&r.constructor&&(i=r.constructor.name),i==="Map"||i==="Set"?Array.from(r):i==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(i)?Se(r,n):void 0}}function Xt(){throw new TypeError(`Invalid attempt to destructure non-iterable instance.
2
2
  In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function ee(r,n){return qt(r)||Yt(r,n)||Tt(r,n)||Xt()}var pe={black:"#000000",silver:"#C0C0C0",gray:"#808080",grey:"#808080",white:"#FFFFFF",maroon:"#800000",red:"#FF0000",purple:"#800080",fuchsia:"#FF00FF",green:"#008000",lime:"#00FF00",olive:"#808000",yellow:"#FFFF00",navy:"#000080",blue:"#0000FF",teal:"#008080",aqua:"#00FFFF",darkblue:"#00008B",mediumblue:"#0000CD",darkgreen:"#006400",darkcyan:"#008B8B",deepskyblue:"#00BFFF",darkturquoise:"#00CED1",mediumspringgreen:"#00FA9A",springgreen:"#00FF7F",cyan:"#00FFFF",midnightblue:"#191970",dodgerblue:"#1E90FF",lightseagreen:"#20B2AA",forestgreen:"#228B22",seagreen:"#2E8B57",darkslategray:"#2F4F4F",darkslategrey:"#2F4F4F",limegreen:"#32CD32",mediumseagreen:"#3CB371",turquoise:"#40E0D0",royalblue:"#4169E1",steelblue:"#4682B4",darkslateblue:"#483D8B",mediumturquoise:"#48D1CC",indigo:"#4B0082",darkolivegreen:"#556B2F",cadetblue:"#5F9EA0",cornflowerblue:"#6495ED",rebeccapurple:"#663399",mediumaquamarine:"#66CDAA",dimgray:"#696969",dimgrey:"#696969",slateblue:"#6A5ACD",olivedrab:"#6B8E23",slategray:"#708090",slategrey:"#708090",lightslategray:"#778899",lightslategrey:"#778899",mediumslateblue:"#7B68EE",lawngreen:"#7CFC00",chartreuse:"#7FFF00",aquamarine:"#7FFFD4",skyblue:"#87CEEB",lightskyblue:"#87CEFA",blueviolet:"#8A2BE2",darkred:"#8B0000",darkmagenta:"#8B008B",saddlebrown:"#8B4513",darkseagreen:"#8FBC8F",lightgreen:"#90EE90",mediumpurple:"#9370DB",darkviolet:"#9400D3",palegreen:"#98FB98",darkorchid:"#9932CC",yellowgreen:"#9ACD32",sienna:"#A0522D",brown:"#A52A2A",darkgray:"#A9A9A9",darkgrey:"#A9A9A9",lightblue:"#ADD8E6",greenyellow:"#ADFF2F",paleturquoise:"#AFEEEE",lightsteelblue:"#B0C4DE",powderblue:"#B0E0E6",firebrick:"#B22222",darkgoldenrod:"#B8860B",mediumorchid:"#BA55D3",rosybrown:"#BC8F8F",darkkhaki:"#BDB76B",mediumvioletred:"#C71585",indianred:"#CD5C5C",peru:"#CD853F",chocolate:"#D2691E",tan:"#D2B48C",lightgray:"#D3D3D3",lightgrey:"#D3D3D3",thistle:"#D8BFD8",orchid:"#DA70D6",goldenrod:"#DAA520",palevioletred:"#DB7093",crimson:"#DC143C",gainsboro:"#DCDCDC",plum:"#DDA0DD",burlywood:"#DEB887",lightcyan:"#E0FFFF",lavender:"#E6E6FA",darksalmon:"#E9967A",violet:"#EE82EE",palegoldenrod:"#EEE8AA",lightcoral:"#F08080",khaki:"#F0E68C",aliceblue:"#F0F8FF",honeydew:"#F0FFF0",azure:"#F0FFFF",sandybrown:"#F4A460",wheat:"#F5DEB3",beige:"#F5F5DC",whitesmoke:"#F5F5F5",mintcream:"#F5FFFA",ghostwhite:"#F8F8FF",salmon:"#FA8072",antiquewhite:"#FAEBD7",linen:"#FAF0E6",lightgoldenrodyellow:"#FAFAD2",oldlace:"#FDF5E6",magenta:"#FF00FF",deeppink:"#FF1493",orangered:"#FF4500",tomato:"#FF6347",hotpink:"#FF69B4",coral:"#FF7F50",darkorange:"#FF8C00",lightsalmon:"#FFA07A",orange:"#FFA500",lightpink:"#FFB6C1",pink:"#FFC0CB",gold:"#FFD700",peachpuff:"#FFDAB9",navajowhite:"#FFDEAD",moccasin:"#FFE4B5",bisque:"#FFE4C4",mistyrose:"#FFE4E1",blanchedalmond:"#FFEBCD",papayawhip:"#FFEFD5",lavenderblush:"#FFF0F5",seashell:"#FFF5EE",cornsilk:"#FFF8DC",lemonchiffon:"#FFFACD",floralwhite:"#FFFAF0",snow:"#FFFAFA",lightyellow:"#FFFFE0",ivory:"#FFFFF0"},Ct=new Int8Array(4),be=new Int32Array(Ct.buffer,0,1),$t=new Float32Array(Ct.buffer,0,1),Kt=/^\s*rgba?\s*\(/,Jt=/^\s*rgba?\s*\(\s*([0-9]*)\s*,\s*([0-9]*)\s*,\s*([0-9]*)(?:\s*,\s*(.*)?)?\)\s*$/;function Qt(r){var n=0,i=0,e=0,t=1;if(r[0]==="#")r.length===4?(n=parseInt(r.charAt(1)+r.charAt(1),16),i=parseInt(r.charAt(2)+r.charAt(2),16),e=parseInt(r.charAt(3)+r.charAt(3),16)):(n=parseInt(r.charAt(1)+r.charAt(2),16),i=parseInt(r.charAt(3)+r.charAt(4),16),e=parseInt(r.charAt(5)+r.charAt(6),16)),r.length===9&&(t=parseInt(r.charAt(7)+r.charAt(8),16)/255);else if(Kt.test(r)){var a=r.match(Jt);a&&(n=+a[1],i=+a[2],e=+a[3],a[4]&&(t=+a[4]))}return{r:n,g:i,b:e,a:t}}var J={};for(var le in pe)J[le]=oe(pe[le]),J[pe[le]]=J[le];function Rt(r,n,i,e,t){return be[0]=e<<24|i<<16|n<<8|r,be[0]=be[0]&4278190079,$t[0]}function oe(r){if(r=r.toLowerCase(),typeof J[r]<"u")return J[r];var n=Qt(r),i=n.r,e=n.g,t=n.b,a=n.a;a=a*255|0;var o=Rt(i,e,t,a);return J[r]=o,o}var Ee={};function _t(r){if(typeof Ee[r]<"u")return Ee[r];var n=(r&16711680)>>>16,i=(r&65280)>>>8,e=r&255,t=255,a=Rt(n,i,e,t);return Ee[r]=a,a}function We(r,n,i,e){return i+(n<<8)+(r<<16)}function Ve(r,n,i,e,t,a){var o=Math.floor(i/a*t),s=Math.floor(r.drawingBufferHeight/a-e/a*t),l=new Uint8Array(4);r.bindFramebuffer(r.FRAMEBUFFER,n),r.readPixels(o,s,1,1,r.RGBA,r.UNSIGNED_BYTE,l);var h=ee(l,4),u=h[0],d=h[1],f=h[2],g=h[3];return[u,d,f,g]}function c(r,n,i){return(n=ae(n))in r?Object.defineProperty(r,n,{value:i,enumerable:!0,configurable:!0,writable:!0}):r[n]=i,r}function je(r,n){var i=Object.keys(r);if(Object.getOwnPropertySymbols){var e=Object.getOwnPropertySymbols(r);n&&(e=e.filter(function(t){return Object.getOwnPropertyDescriptor(r,t).enumerable})),i.push.apply(i,e)}return i}function b(r){for(var n=1;n<arguments.length;n++){var i=arguments[n]!=null?arguments[n]:{};n%2?je(Object(i),!0).forEach(function(e){c(r,e,i[e])}):Object.getOwnPropertyDescriptors?Object.defineProperties(r,Object.getOwnPropertyDescriptors(i)):je(Object(i)).forEach(function(e){Object.defineProperty(r,e,Object.getOwnPropertyDescriptor(i,e))})}return r}function ei(r,n){for(;!{}.hasOwnProperty.call(r,n)&&(r=Q(r))!==null;);return r}function Ae(){return Ae=typeof Reflect<"u"&&Reflect.get?Reflect.get.bind():function(r,n,i){var e=ei(r,n);if(e){var t=Object.getOwnPropertyDescriptor(e,n);return t.get?t.get.call(arguments.length<3?r:i):t.value}},Ae.apply(null,arguments)}function wt(r,n,i,e){var t=Ae(Q(r.prototype),n,i);return typeof t=="function"?function(a){return t.apply(i,a)}:t}function ti(r){return r.normalized?1:r.size}function ye(r){var n=0;return r.forEach(function(i){return n+=ti(i)}),n}function xt(r,n,i){var e=r==="VERTEX"?n.VERTEX_SHADER:n.FRAGMENT_SHADER,t=n.createShader(e);if(t===null)throw new Error("loadShader: error while creating the shader");n.shaderSource(t,i),n.compileShader(t);var a=n.getShaderParameter(t,n.COMPILE_STATUS);if(!a){var o=n.getShaderInfoLog(t);throw n.deleteShader(t),new Error(`loadShader: error while compiling the shader:
3
3
  `.concat(o,`
4
4
  `).concat(i))}return t}function ii(r,n){return xt("VERTEX",r,n)}function ri(r,n){return xt("FRAGMENT",r,n)}function ni(r,n){var i=r.createProgram();if(i===null)throw new Error("loadProgram: error while creating the program.");var e,t;for(e=0,t=n.length;e<t;e++)r.attachShader(i,n[e]);r.linkProgram(i);var a=r.getProgramParameter(i,r.LINK_STATUS);if(!a){var o=r.getProgramInfoLog(i);throw r.deleteProgram(i),new Error("loadProgram: error while linking the program: ".concat(o))}return i}function Ze(r){var n=r.gl,i=r.buffer,e=r.program,t=r.vertexShader,a=r.fragmentShader;n.deleteShader(t),n.deleteShader(a),n.deleteProgram(e),n.deleteBuffer(i)}var qe=`#define PICKING_MODE
package/assets/index.html CHANGED
@@ -12,7 +12,7 @@
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-DVa1lkiE.js"></script>
15
+ <script type="module" crossorigin src="./assets/index-Dr_-IUJz.js"></script>
16
16
  <link rel="modulepreload" crossorigin href="./assets/vendor-tanstack-CYBjbqVr.js">
17
17
  <link rel="modulepreload" crossorigin href="./assets/vendor-radix-BpmvLvpp.js">
18
18
  <link rel="modulepreload" crossorigin href="./assets/vendor-recharts-FA0uySU7.js">
@@ -47,6 +47,7 @@ import {
47
47
  DEFAULT_DISCOVERY_PROMOTE_BUCKETS,
48
48
  DEFAULT_DISCOVERY_PROMOTE_COMPETITOR_TYPES,
49
49
  DEFAULT_MEASUREMENT_CONFIG,
50
+ DEFAULT_VIEWER_RESEARCH_DAILY_RUN_LIMIT,
50
51
  DISCOVERY_DEFAULT_DEDUP_THRESHOLD,
51
52
  DISCOVERY_DEFAULT_PROBE_CONCURRENCY,
52
53
  DISCOVERY_PROBE_CONCURRENCY_CAP,
@@ -553,6 +554,7 @@ import {
553
554
  reportConfidenceLabel,
554
555
  reportHorizonLabel,
555
556
  reportSeverityLabel,
557
+ researchDailyLimitExceeded,
556
558
  researchRunCreateSchema,
557
559
  researchRunDetailSchema,
558
560
  researchRunListSchema,
@@ -666,10 +668,10 @@ import {
666
668
  wordpressSchemaDeployResultDtoSchema,
667
669
  wordpressSchemaStatusResultDtoSchema,
668
670
  wordpressStatusDtoSchema
669
- } from "./chunk-CFJHRXZ3.js";
671
+ } from "./chunk-LNBX7RTN.js";
670
672
 
671
673
  // src/intelligence-service.ts
672
- import { eq as eq67, desc as desc29, asc as asc12, and as and56, ne as ne10, or as or14, inArray as inArray25, gte as gte17, lte as lte15, isNull as isNull12, sql as sql25, exists } from "drizzle-orm";
674
+ import { eq as eq67, desc as desc29, asc as asc12, and as and56, ne as ne10, or as or14, inArray as inArray25, gte as gte18, lte as lte15, isNull as isNull12, sql as sql26, exists } from "drizzle-orm";
673
675
 
674
676
  // ../db/src/client.ts
675
677
  import { mkdirSync } from "fs";
@@ -2722,6 +2724,7 @@ var researchRuns = sqliteTable("research_runs", {
2722
2724
  idempotencyKey: text("idempotency_key"),
2723
2725
  requestHash: text("request_hash"),
2724
2726
  error: text("error"),
2727
+ initiatedBy: text("initiated_by", { mode: "json" }).$type(),
2725
2728
  startedAt: text("started_at"),
2726
2729
  finishedAt: text("finished_at"),
2727
2730
  createdAt: text("created_at").notNull()
@@ -7139,6 +7142,13 @@ var MIGRATION_VERSIONS = [
7139
7142
  SELECT RAISE(ABORT, 'simple measurement definition is immutable');
7140
7143
  END`
7141
7144
  ]
7145
+ },
7146
+ {
7147
+ version: 151,
7148
+ name: "research-run-principal-attribution",
7149
+ statements: [
7150
+ `ALTER TABLE research_runs ADD COLUMN initiated_by TEXT`
7151
+ ]
7142
7152
  }
7143
7153
  ];
7144
7154
  function addRunsMeasurementPlanVersionForeignKey(tx) {
@@ -9169,15 +9179,15 @@ function buildAiSourceOrigin(snapshots, projectDomains, competitorDomains, topDo
9169
9179
  totalCitations++;
9170
9180
  }
9171
9181
  }
9172
- const categories = [...categoryCounts.entries()].map(([category, { label, count: count2 }]) => ({
9182
+ const categories = [...categoryCounts.entries()].map(([category, { label, count: count3 }]) => ({
9173
9183
  category,
9174
9184
  label,
9175
- count: count2,
9176
- sharePct: totalCitations > 0 ? Math.round(count2 / totalCitations * 100) : 0
9185
+ count: count3,
9186
+ sharePct: totalCitations > 0 ? Math.round(count3 / totalCitations * 100) : 0
9177
9187
  })).sort((a, b) => b.count - a.count);
9178
- const topDomains = [...domainCounts.entries()].map(([domain, count2]) => ({
9188
+ const topDomains = [...domainCounts.entries()].map(([domain, count3]) => ({
9179
9189
  domain,
9180
- count: count2,
9190
+ count: count3,
9181
9191
  isCompetitor: citedDomainBelongsToProject(domain, competitorDomains)
9182
9192
  })).sort((a, b) => b.count - a.count).slice(0, topDomainsLimit);
9183
9193
  return { categories, topDomains };
@@ -10771,6 +10781,9 @@ function principalScopes(request) {
10771
10781
  function isReadSemanticRoute(request) {
10772
10782
  return request.routeOptions.config.readSemantic === true;
10773
10783
  }
10784
+ function isPaidReadRoute(request) {
10785
+ return request.routeOptions.config.paidRead === true;
10786
+ }
10774
10787
  function isTransportEnvelopeRoute(request) {
10775
10788
  return request.routeOptions.config.transportEnvelope === true;
10776
10789
  }
@@ -10800,6 +10813,17 @@ function requirePaidReadScope(request) {
10800
10813
  throw forbidden("This API key was not granted access to OpenAI Ads paid reads.");
10801
10814
  }
10802
10815
  }
10816
+ function requireResearchGrant(request, allowViewers) {
10817
+ const principal = request.principal;
10818
+ if (!principal) return;
10819
+ if (principal.kind === "api-key") {
10820
+ if (principal.scopes.includes(WILDCARD_SCOPE)) return;
10821
+ throw forbidden("This API key was not granted access to paid research queries.");
10822
+ }
10823
+ if (principal.role === UserRoles.admin) return;
10824
+ if (principal.role === UserRoles.viewer && allowViewers) return;
10825
+ throw forbidden("Research queries are not enabled for viewer accounts on this deployment.");
10826
+ }
10803
10827
  function requireBroadInstanceKey(request) {
10804
10828
  const principal = request.principal;
10805
10829
  if (!principal || principal.kind !== "api-key") return;
@@ -10959,7 +10983,7 @@ function applyRoleGates(request) {
10959
10983
  const principal = request.principal;
10960
10984
  if (!principal || principal.kind !== "user") return;
10961
10985
  request.readSemanticGrant = principal.role === UserRoles.viewer && isReadSemanticRoute(request);
10962
- if (isReadOnlyKey(principal.scopes) && WRITE_METHODS2.has(request.method) && !request.readSemanticGrant && !isTransportEnvelopeRoute(request)) {
10986
+ if (isReadOnlyKey(principal.scopes) && WRITE_METHODS2.has(request.method) && !request.readSemanticGrant && !isPaidReadRoute(request) && !isTransportEnvelopeRoute(request)) {
10963
10987
  throw forbidden(VIEWER_DENIED_MESSAGE);
10964
10988
  }
10965
10989
  }
@@ -11995,7 +12019,7 @@ async function projectRoutes(app, opts) {
11995
12019
  app.get("/projects/:name/delete-preview", async (request, reply) => {
11996
12020
  const project = resolveProject(app.db, request.params.name);
11997
12021
  const pid = project.id;
11998
- const count2 = (n) => n ?? 0;
12022
+ const count3 = (n) => n ?? 0;
11999
12023
  const queryCount = app.db.select({ n: sql4`count(*)` }).from(queries).where(eq5(queries.projectId, pid)).get();
12000
12024
  const competitorCount = app.db.select({ n: sql4`count(*)` }).from(competitors).where(eq5(competitors.projectId, pid)).get();
12001
12025
  const runCount = app.db.select({ n: sql4`count(*)` }).from(runs).where(eq5(runs.projectId, pid)).get();
@@ -12005,14 +12029,14 @@ async function projectRoutes(app, opts) {
12005
12029
  return reply.send({
12006
12030
  project: { id: project.id, name: project.name },
12007
12031
  cascadeRows: {
12008
- queries: count2(queryCount?.n),
12009
- competitors: count2(competitorCount?.n),
12010
- runs: count2(runCount?.n),
12011
- snapshots: count2(snapshotCount?.n),
12012
- insights: count2(insightCount?.n)
12032
+ queries: count3(queryCount?.n),
12033
+ competitors: count3(competitorCount?.n),
12034
+ runs: count3(runCount?.n),
12035
+ snapshots: count3(snapshotCount?.n),
12036
+ insights: count3(insightCount?.n)
12013
12037
  },
12014
12038
  detachedRows: {
12015
- auditLog: count2(auditLogCount?.n)
12039
+ auditLog: count3(auditLogCount?.n)
12016
12040
  }
12017
12041
  });
12018
12042
  });
@@ -12698,14 +12722,14 @@ async function queryRoutes(app, opts) {
12698
12722
  validProviders: validNames
12699
12723
  });
12700
12724
  }
12701
- const count2 = body.count ?? 5;
12725
+ const count3 = body.count ?? 5;
12702
12726
  if (!opts.onGenerateQueries) {
12703
12727
  throw notImplemented("Query generation is not supported in this deployment");
12704
12728
  }
12705
12729
  const existingRows = app.db.select().from(queries).where(eq8(queries.projectId, project.id)).all();
12706
12730
  const existingQueries = existingRows.map((r) => r.query);
12707
12731
  try {
12708
- const generated = await opts.onGenerateQueries(provider, count2, {
12732
+ const generated = await opts.onGenerateQueries(provider, count3, {
12709
12733
  domain: project.canonicalDomain,
12710
12734
  displayName: project.displayName,
12711
12735
  country: project.country,
@@ -12831,14 +12855,14 @@ async function queryRoutes(app, opts) {
12831
12855
  validProviders: validNames
12832
12856
  });
12833
12857
  }
12834
- const count2 = body.count ?? 5;
12858
+ const count3 = body.count ?? 5;
12835
12859
  if (!opts.onGenerateQueries) {
12836
12860
  throw notImplemented("Keyword generation is not supported in this deployment");
12837
12861
  }
12838
12862
  const existingRows = app.db.select().from(queries).where(eq8(queries.projectId, project.id)).all();
12839
12863
  const existingQueries = existingRows.map((r) => r.query);
12840
12864
  try {
12841
- const generated = await opts.onGenerateQueries(provider, count2, {
12865
+ const generated = await opts.onGenerateQueries(provider, count3, {
12842
12866
  domain: project.canonicalDomain,
12843
12867
  displayName: project.displayName,
12844
12868
  country: project.country,
@@ -18682,19 +18706,19 @@ function groupChangesFromRows(rows, identities) {
18682
18706
  });
18683
18707
  }
18684
18708
  function countsFromRows(rows, groupChanges) {
18685
- const count2 = (status) => rows.filter((row) => row.status === status).length;
18686
- const ambiguousRows = count2("ambiguous");
18687
- const unmatchedRows = count2("unmatched");
18688
- const invalidRows = count2("invalid");
18689
- const proposedRows = count2("proposed");
18690
- const excludedRows = count2("excluded");
18709
+ const count3 = (status) => rows.filter((row) => row.status === status).length;
18710
+ const ambiguousRows = count3("ambiguous");
18711
+ const unmatchedRows = count3("unmatched");
18712
+ const invalidRows = count3("invalid");
18713
+ const proposedRows = count3("proposed");
18714
+ const excludedRows = count3("excluded");
18691
18715
  return {
18692
18716
  dataRows: rows.length,
18693
- matchedRows: count2("matched"),
18717
+ matchedRows: count3("matched"),
18694
18718
  ambiguousRows,
18695
18719
  unmatchedRows,
18696
18720
  invalidRows,
18697
- duplicateRows: count2("duplicate"),
18721
+ duplicateRows: count3("duplicate"),
18698
18722
  proposedRows,
18699
18723
  excludedRows,
18700
18724
  needsAttention: ambiguousRows + unmatchedRows + invalidRows + proposedRows + excludedRows,
@@ -24056,9 +24080,9 @@ function workloadDiff(before, after) {
24056
24080
  nextSweepNodes: after.size,
24057
24081
  nextSweepProviderCalls: sum(after.values()),
24058
24082
  addedNodes: added.length,
24059
- addedProviderCalls: sum(added.map(([, count2]) => count2)),
24083
+ addedProviderCalls: sum(added.map(([, count3]) => count3)),
24060
24084
  removedNodes: removed.length,
24061
- removedProviderCalls: sum(removed.map(([, count2]) => count2))
24085
+ removedProviderCalls: sum(removed.map(([, count3]) => count3))
24062
24086
  };
24063
24087
  }
24064
24088
  function candidateDiff(state, nextRows, plan, reusedQueryIds, mutatedQueryIds, scopedRemovalQueryIds) {
@@ -26201,10 +26225,10 @@ function computeQueryChanges(projectQueries, cutoff) {
26201
26225
  }
26202
26226
  const days = [...byDay.entries()].sort((a, b) => a[0].localeCompare(b[0]));
26203
26227
  if (days.length <= 1) return [];
26204
- return days.slice(1).map(([date, count2]) => ({
26228
+ return days.slice(1).map(([date, count3]) => ({
26205
26229
  date: (/* @__PURE__ */ new Date(date + "T00:00:00.000Z")).toISOString(),
26206
- delta: count2,
26207
- label: `+${count2} kp`
26230
+ delta: count3,
26231
+ label: `+${count3} kp`
26208
26232
  }));
26209
26233
  }
26210
26234
  function pooledRate(buckets, rateKey) {
@@ -26279,15 +26303,15 @@ function buildRankedList(domains, limit) {
26279
26303
  function buildCategoryCounts(counts2) {
26280
26304
  let grandTotal = 0;
26281
26305
  for (const domains of counts2.values()) {
26282
- for (const count2 of domains.values()) grandTotal += count2;
26306
+ for (const count3 of domains.values()) grandTotal += count3;
26283
26307
  }
26284
26308
  const result = [];
26285
26309
  for (const [category, domains] of counts2) {
26286
26310
  let categoryTotal = 0;
26287
26311
  const domainEntries = [];
26288
- for (const [domain, count2] of domains) {
26289
- categoryTotal += count2;
26290
- domainEntries.push({ domain, count: count2 });
26312
+ for (const [domain, count3] of domains) {
26313
+ categoryTotal += count3;
26314
+ domainEntries.push({ domain, count: count3 });
26291
26315
  }
26292
26316
  domainEntries.sort((a, b) => b.count - a.count);
26293
26317
  result.push({
@@ -27563,8 +27587,8 @@ function gscDateRange(report) {
27563
27587
  const end = summary2?.periodEnd || gsc?.periodEnd || gsc?.trend.at(-1)?.date || "";
27564
27588
  return formatDateRange(start, end);
27565
27589
  }
27566
- function pluralize(count2, singular, plural = `${singular}s`) {
27567
- return count2 === 1 ? singular : plural;
27590
+ function pluralize(count3, singular, plural = `${singular}s`) {
27591
+ return count3 === 1 ? singular : plural;
27568
27592
  }
27569
27593
  var PROVIDER_DISPLAY_NAMES = {
27570
27594
  gemini: "Gemini",
@@ -29398,9 +29422,9 @@ function renderInsights(report) {
29398
29422
  );
29399
29423
  }
29400
29424
  const haveDeduped = list.every((i) => typeof i.instanceCount === "number");
29401
- const rows = (haveDeduped ? list.map((i) => ({ rep: i, count: i.instanceCount })) : groupInsights(list).map((g) => ({ rep: g.representative, count: g.count }))).map(({ rep: i, count: count2 }) => {
29425
+ const rows = (haveDeduped ? list.map((i) => ({ rep: i, count: i.instanceCount })) : groupInsights(list).map((g) => ({ rep: g.representative, count: g.count }))).map(({ rep: i, count: count3 }) => {
29402
29426
  const tone = severityTone(i.severity);
29403
- const countChip = count2 > 1 ? ` <span class="badge tone-neutral">\xD7 ${count2}</span>` : "";
29427
+ const countChip = count3 > 1 ? ` <span class="badge tone-neutral">\xD7 ${count3}</span>` : "";
29404
29428
  return `<tr>
29405
29429
  <td class="col-severity"><span class="badge tone-${tone}">${escapeHtml2(reportSeverityLabel(i.severity))}</span></td>
29406
29430
  <td class="col-title">${escapeHtml2(i.title)}${countChip}</td>
@@ -30857,9 +30881,9 @@ function contentActionVerb(action) {
30857
30881
  return "Add schema to";
30858
30882
  }
30859
30883
  }
30860
- function confidenceFromEvidence(count2) {
30861
- if (count2 >= 3) return "high";
30862
- if (count2 >= 1) return "medium";
30884
+ function confidenceFromEvidence(count3) {
30885
+ if (count3 >= 3) return "high";
30886
+ if (count3 >= 1) return "medium";
30863
30887
  return "low";
30864
30888
  }
30865
30889
  function actionAudienceMatches2(action, audience) {
@@ -32033,9 +32057,9 @@ function summarizeVerificationHits(rows) {
32033
32057
  return { counts: counts2, unsupportedRows, unsupportedHits };
32034
32058
  }
32035
32059
  function summarizeServer(crawlers, fetches, referrals) {
32036
- const total = referrals.reduce((count2, row) => count2 + row.sessionsOrHits, 0);
32037
- const paid = referrals.reduce((count2, row) => count2 + row.paidSessionsOrHits, 0);
32038
- const organic = referrals.reduce((count2, row) => count2 + row.organicSessionsOrHits, 0);
32060
+ const total = referrals.reduce((count3, row) => count3 + row.sessionsOrHits, 0);
32061
+ const paid = referrals.reduce((count3, row) => count3 + row.paidSessionsOrHits, 0);
32062
+ const organic = referrals.reduce((count3, row) => count3 + row.organicSessionsOrHits, 0);
32039
32063
  const crawlerHits = summarizeVerificationHits(crawlers);
32040
32064
  const userFetchHits = summarizeVerificationHits(fetches);
32041
32065
  return {
@@ -32184,10 +32208,10 @@ function buildOrganicEvidence(db, projectName, periodDays) {
32184
32208
  const gaWindow = gaStartDate && gaEndDate ? gaRows.filter((row) => inRange(row.date, gaStartDate, gaEndDate)) : [];
32185
32209
  const legacyGaCohorts = gaCohorts.map((cohort) => ({
32186
32210
  ...cohort,
32187
- organicSessions: gaRows.filter((row) => inRange(row.date, cohort.startDate, cohort.endDate)).reduce((count2, row) => count2 + row.organicSessions, 0)
32211
+ organicSessions: gaRows.filter((row) => inRange(row.date, cohort.startDate, cohort.endDate)).reduce((count3, row) => count3 + row.organicSessions, 0)
32188
32212
  }));
32189
32213
  let ga4 = gaRows.length ? {
32190
- organicSessions: gaWindow.reduce((count2, row) => count2 + row.organicSessions, 0),
32214
+ organicSessions: gaWindow.reduce((count3, row) => count3 + row.organicSessions, 0),
32191
32215
  cohorts: legacyGaCohorts
32192
32216
  } : null;
32193
32217
  if (nativeAcquisitionActive && nativeAcquisition.periods.length === 0) {
@@ -32210,8 +32234,8 @@ function buildOrganicEvidence(db, projectName, periodDays) {
32210
32234
  const allGaAiRows = db.select().from(gaAiReferrals).where(and30(eq35(gaAiReferrals.projectId, project.id), gte10(gaAiReferrals.date, gaStartDate ?? startDate), lte8(gaAiReferrals.date, gaEndDate ?? endDate))).all().filter((row) => row.sourceDimension === "session");
32211
32235
  const aiRows = gaStartDate && gaEndDate ? allGaAiRows.filter((row) => inRange(row.date, gaStartDate, gaEndDate)) : [];
32212
32236
  const gaAiReferralSummary = allGaAiRows.length ? {
32213
- paidSessions: aiRows.filter((row) => row.trafficClass === AiReferralTrafficClasses.paid).reduce((count2, row) => count2 + row.sessions, 0),
32214
- organicSessions: aiRows.filter((row) => row.trafficClass === AiReferralTrafficClasses.organic).reduce((count2, row) => count2 + row.sessions, 0)
32237
+ paidSessions: aiRows.filter((row) => row.trafficClass === AiReferralTrafficClasses.paid).reduce((count3, row) => count3 + row.sessions, 0),
32238
+ organicSessions: aiRows.filter((row) => row.trafficClass === AiReferralTrafficClasses.organic).reduce((count3, row) => count3 + row.sessions, 0)
32215
32239
  } : null;
32216
32240
  const serverSources = db.select().from(trafficSources).where(eq35(trafficSources.projectId, project.id)).all();
32217
32241
  const serverStart = `${startDate}T00:00:00`;
@@ -35937,11 +35961,11 @@ var routeCatalog = [
35937
35961
  method: "post",
35938
35962
  path: "/api/v1/projects/{name}/research/runs",
35939
35963
  summary: "Start an isolated research query batch",
35940
- description: "Runs one to fifty ad-hoc queries through one API provider and saves the answer evidence. Research never creates tracked queries, shared runs, snapshots, insights, or notifications.",
35964
+ description: "Runs one to fifty ad-hoc queries through one API provider and saves the answer evidence. Research never creates tracked queries, shared runs, snapshots, insights, or notifications. Administrators are always allowed; signed-in viewers require the deployment opt-in and a per-project daily allowance. Read-only and narrowly scoped API keys are refused.",
35941
35965
  tags: ["research"],
35942
35966
  parameters: [nameParameter],
35943
35967
  requestBody: { required: true, content: { "application/json": { schema: { $ref: "#/components/schemas/ResearchRunCreate" } } } },
35944
- responses: { 200: jsonResponse("Idempotent request returned its existing research run.", "ResearchRunDetailDto"), 202: jsonResponse("Research batch queued.", "ResearchRunDetailDto"), 400: errorResponse("Invalid provider, model, location, or request."), 404: errorResponse("Project not found."), 409: errorResponse("Idempotency key was reused with a different payload."), 422: errorResponse("Research executor is unavailable on this deployment.") }
35968
+ responses: { 200: jsonResponse("Idempotent request returned its existing research run.", "ResearchRunDetailDto"), 202: jsonResponse("Research batch queued.", "ResearchRunDetailDto"), 400: errorResponse("Invalid provider, model, location, or request."), 403: errorResponse("Paid research was not granted to this principal."), 404: errorResponse("Project not found."), 409: errorResponse("Idempotency key was reused with a different payload."), 422: errorResponse("Research executor is unavailable on this deployment."), 429: errorResponse("The viewer reached the per-project UTC-day research limit.") }
35945
35969
  },
35946
35970
  {
35947
35971
  method: "get",
@@ -44402,12 +44426,12 @@ async function getLodging(accessToken, locationName, opts = {}) {
44402
44426
  }
44403
44427
  }
44404
44428
  function countPopulatedGroups(lodging) {
44405
- let count2 = 0;
44429
+ let count3 = 0;
44406
44430
  for (const [key, value] of Object.entries(lodging)) {
44407
44431
  if (key === "name" || key === "metadata") continue;
44408
- if (isPopulated(value)) count2++;
44432
+ if (isPopulated(value)) count3++;
44409
44433
  }
44410
- return count2;
44434
+ return count3;
44411
44435
  }
44412
44436
  function isPopulated(value) {
44413
44437
  if (value === null || value === void 0) return false;
@@ -49451,7 +49475,7 @@ function createActivationStore(app, request) {
49451
49475
  function countManifest(manifest) {
49452
49476
  return {
49453
49477
  adGroupCount: manifest.campaign.adGroups.length,
49454
- adCount: manifest.campaign.adGroups.reduce((count2, group) => count2 + group.ads.length, 0)
49478
+ adCount: manifest.campaign.adGroups.reduce((count3, group) => count3 + group.ads.length, 0)
49455
49479
  };
49456
49480
  }
49457
49481
  function resolveAdsActivationMaxEntities(env = process.env) {
@@ -56734,7 +56758,7 @@ async function queryBacklinks(opts) {
56734
56758
  const reversed = opts.targets.map(reverseDomain);
56735
56759
  const targetList = reversed.map(quote).join(", ");
56736
56760
  const limitClause = opts.limitPerTarget ? `QUALIFY row_number() OVER (PARTITION BY t.target_rev_domain ORDER BY v.num_hosts DESC) <= ${Math.floor(opts.limitPerTarget)}` : "";
56737
- const sql26 = `
56761
+ const sql27 = `
56738
56762
  WITH vertices AS (
56739
56763
  SELECT * FROM read_csv(
56740
56764
  ${quote(opts.vertexPath)},
@@ -56770,7 +56794,7 @@ async function queryBacklinks(opts) {
56770
56794
  const conn = await instance.connect();
56771
56795
  let rows;
56772
56796
  try {
56773
- const reader = await conn.runAndReadAll(sql26);
56797
+ const reader = await conn.runAndReadAll(sql27);
56774
56798
  rows = reader.getRowObjects();
56775
56799
  } finally {
56776
56800
  conn.disconnectSync?.();
@@ -82860,10 +82884,17 @@ async function technicalAeoRoutes(app, opts) {
82860
82884
 
82861
82885
  // ../api-routes/src/research.ts
82862
82886
  import crypto49 from "crypto";
82863
- import { and as and54, desc as desc28, eq as eq65 } from "drizzle-orm";
82887
+ import { and as and54, count as count2, desc as desc28, eq as eq65, gte as gte17, lt as lt11, sql as sql25 } from "drizzle-orm";
82864
82888
  var sameLocation = (a, b) => a.label === b.label && a.city === b.city && a.region === b.region && a.country === b.country && a.timezone === b.timezone;
82865
82889
  async function researchRoutes(app, opts) {
82866
- app.post("/projects/:name/research/runs", async (request, reply) => {
82890
+ const viewerDailyRunLimit = opts.viewerDailyRunLimit ?? DEFAULT_VIEWER_RESEARCH_DAILY_RUN_LIMIT;
82891
+ if (!Number.isInteger(viewerDailyRunLimit) || viewerDailyRunLimit <= 0) {
82892
+ throw new Error("viewerDailyRunLimit must be a positive integer");
82893
+ }
82894
+ app.post("/projects/:name/research/runs", {
82895
+ config: { paidRead: true }
82896
+ }, async (request, reply) => {
82897
+ requireResearchGrant(request, opts.allowViewers ?? false);
82867
82898
  const project = resolveProject(app.db, request.params.name);
82868
82899
  if (!opts.onResearchRunRequested) throw missingDependency("Research execution is not available on this deployment.", { reason: "no-research-handler" });
82869
82900
  const parsed = researchRunCreateSchema.safeParse(request.body ?? {});
@@ -82888,6 +82919,7 @@ async function researchRoutes(app, opts) {
82888
82919
  const normalized = { queries: input.queries, provider: providerName, model: requestedModel2, location: location ?? null };
82889
82920
  const requestHash2 = crypto49.createHash("sha256").update(JSON.stringify(normalized)).digest("hex");
82890
82921
  const now = (/* @__PURE__ */ new Date()).toISOString();
82922
+ const initiatedBy = researchPrincipal(request);
82891
82923
  const decision = app.db.transaction((tx) => {
82892
82924
  if (input.idempotencyKey) {
82893
82925
  const existing = tx.select().from(researchRuns).where(and54(eq65(researchRuns.projectId, project.id), eq65(researchRuns.idempotencyKey, input.idempotencyKey))).get();
@@ -82896,10 +82928,22 @@ async function researchRoutes(app, opts) {
82896
82928
  return { reused: true, id: existing.id, shouldDispatch: existing.status === ResearchRunStatuses.queued };
82897
82929
  }
82898
82930
  }
82931
+ if (initiatedBy?.kind === "user" && initiatedBy.role === UserRoles.viewer) {
82932
+ const { start, end, date } = utcDayBounds(now);
82933
+ const used = tx.select({ value: count2() }).from(researchRuns).where(and54(
82934
+ eq65(researchRuns.projectId, project.id),
82935
+ gte17(researchRuns.createdAt, start),
82936
+ lt11(researchRuns.createdAt, end),
82937
+ sql25`json_extract(${researchRuns.initiatedBy}, '$.role') = ${UserRoles.viewer}`
82938
+ )).get()?.value ?? 0;
82939
+ if (used >= viewerDailyRunLimit) {
82940
+ throw researchDailyLimitExceeded(project.name, viewerDailyRunLimit, date);
82941
+ }
82942
+ }
82899
82943
  const id = crypto49.randomUUID();
82900
- tx.insert(researchRuns).values({ id, projectId: project.id, status: ResearchRunStatuses.queued, provider: providerName, requestedModel: requestedModel2, resolvedModel, location: location ?? null, totalQueries: input.queries.length, idempotencyKey: input.idempotencyKey ?? null, requestHash: input.idempotencyKey ? requestHash2 : null, createdAt: now }).run();
82944
+ tx.insert(researchRuns).values({ id, projectId: project.id, status: ResearchRunStatuses.queued, provider: providerName, requestedModel: requestedModel2, resolvedModel, location: location ?? null, totalQueries: input.queries.length, idempotencyKey: input.idempotencyKey ?? null, requestHash: input.idempotencyKey ? requestHash2 : null, initiatedBy, createdAt: now }).run();
82901
82945
  for (const [position, query] of input.queries.entries()) tx.insert(researchRunQueries).values({ id: crypto49.randomUUID(), researchRunId: id, position, queryText: query, status: ResearchQueryStatuses.queued, requestedModel: requestedModel2, resolvedModel, groundingSources: [], citedDomains: [], searchQueries: [], createdAt: now }).run();
82902
- writeAuditLog(tx, { projectId: project.id, actor: "api", action: "research.created", entityType: "research_run", entityId: id });
82946
+ writeAuditLog(tx, { projectId: project.id, actor: initiatedBy ? `${initiatedBy.kind}:${initiatedBy.id}` : "api", action: "research.created", entityType: "research_run", entityId: id });
82903
82947
  return { reused: false, id, shouldDispatch: true };
82904
82948
  });
82905
82949
  const result = getDetail(app, project.id, decision.id);
@@ -82926,11 +82970,27 @@ function getDetail(app, projectId, id) {
82926
82970
  return { ...serializeRun(row), queries: queries2 };
82927
82971
  }
82928
82972
  function serializeRun(row) {
82929
- return { id: row.id, projectId: row.projectId, status: row.status, provider: row.provider, requestedModel: row.requestedModel, resolvedModel: row.resolvedModel, location: row.location ?? null, totalQueries: row.totalQueries, completedQueries: row.completedQueries, failedQueries: row.failedQueries, error: row.error, startedAt: row.startedAt, finishedAt: row.finishedAt, createdAt: row.createdAt };
82973
+ return { id: row.id, projectId: row.projectId, status: row.status, provider: row.provider, requestedModel: row.requestedModel, resolvedModel: row.resolvedModel, location: row.location ?? null, totalQueries: row.totalQueries, completedQueries: row.completedQueries, failedQueries: row.failedQueries, error: row.error, initiatedBy: row.initiatedBy ?? null, startedAt: row.startedAt, finishedAt: row.finishedAt, createdAt: row.createdAt };
82930
82974
  }
82931
82975
  function serializeQuery(row) {
82932
82976
  return { id: row.id, position: row.position, query: row.queryText, status: row.status, requestedModel: row.requestedModel, resolvedModel: row.resolvedModel, servedModel: row.servedModel, answerText: row.answerText, groundingSources: row.groundingSources, citedDomains: row.citedDomains, searchQueries: row.searchQueries, namedCompetitors: row.namedCompetitors, citedCompetitorDomains: row.citedCompetitorDomains, answerMentioned: row.answerMentioned, citationState: row.citationState, error: row.error, startedAt: row.startedAt, finishedAt: row.finishedAt, createdAt: row.createdAt };
82933
82977
  }
82978
+ function researchPrincipal(request) {
82979
+ const principal = request.principal;
82980
+ if (!principal) return null;
82981
+ return {
82982
+ kind: principal.kind,
82983
+ id: principal.id,
82984
+ name: principal.name,
82985
+ role: principal.kind === "user" ? principal.role ?? null : null
82986
+ };
82987
+ }
82988
+ function utcDayBounds(now) {
82989
+ const date = now.slice(0, 10);
82990
+ const start = `${date}T00:00:00.000Z`;
82991
+ const end = new Date(Date.parse(start) + 24 * 60 * 60 * 1e3).toISOString();
82992
+ return { start, end, date };
82993
+ }
82934
82994
 
82935
82995
  // ../api-routes/src/simple-measurement-definitions.ts
82936
82996
  import crypto50 from "crypto";
@@ -83263,7 +83323,9 @@ async function apiRoutes(app, opts) {
83263
83323
  await api.register(researchRoutes, {
83264
83324
  providerAdapters: opts.providerAdapters,
83265
83325
  configuredProviderNames: opts.providerSummary?.filter((provider) => provider.configured).map((provider) => provider.name),
83266
- onResearchRunRequested: opts.onResearchRunRequested
83326
+ onResearchRunRequested: opts.onResearchRunRequested,
83327
+ allowViewers: opts.researchAllowViewers,
83328
+ viewerDailyRunLimit: opts.researchViewerDailyRunLimit
83267
83329
  });
83268
83330
  await api.register(technicalAeoRoutes, {
83269
83331
  onSiteAuditRequested: opts.onSiteAuditRequested
@@ -83694,7 +83756,7 @@ function matchesBrandAlias(candidate, aliases) {
83694
83756
  var RECURRENCE_LOOKBACK_RUNS = 5;
83695
83757
  var HISTORY_WINDOW_RUNS = Math.max(PERSISTENT_GAP_THRESHOLD, 5);
83696
83758
  var log = createLogger("IntelligenceService");
83697
- var runChronologyKey = sql25`coalesce(${runs.finishedAt}, ${runs.createdAt})`;
83759
+ var runChronologyKey = sql26`coalesce(${runs.finishedAt}, ${runs.createdAt})`;
83698
83760
  function analyzableRunPredicates(projectId) {
83699
83761
  return [
83700
83762
  eq67(runs.projectId, projectId),
@@ -83762,7 +83824,7 @@ var IntelligenceService = class {
83762
83824
  // MEASUREMENT: only runs that actually wrote snapshots — see above.
83763
83825
  // Index seek on idx_snapshots_run, short-circuited on the first row.
83764
83826
  exists(
83765
- this.db.select({ one: sql25`1` }).from(querySnapshots).where(eq67(querySnapshots.runId, runs.id))
83827
+ this.db.select({ one: sql26`1` }).from(querySnapshots).where(eq67(querySnapshots.runId, runs.id))
83766
83828
  )
83767
83829
  )
83768
83830
  ).orderBy(desc29(runs.finishedAt), desc29(runs.createdAt)).limit(HISTORY_WINDOW_RUNS).all();
@@ -83848,7 +83910,7 @@ var IntelligenceService = class {
83848
83910
  const selected = this.db.select().from(gbpLocations).where(and56(
83849
83911
  eq67(gbpLocations.projectId, projectId),
83850
83912
  eq67(gbpLocations.selected, true),
83851
- gte17(gbpLocations.syncedAt, windowStart),
83913
+ gte18(gbpLocations.syncedAt, windowStart),
83852
83914
  lte15(gbpLocations.syncedAt, windowEnd)
83853
83915
  )).all();
83854
83916
  if (selected.length === 0) {
@@ -84228,7 +84290,7 @@ var IntelligenceService = class {
84228
84290
  position: gscSearchData.position
84229
84291
  }).from(gscSearchData).where(and56(
84230
84292
  eq67(gscSearchData.projectId, projectId),
84231
- gte17(gscSearchData.date, window.startDate),
84293
+ gte18(gscSearchData.date, window.startDate),
84232
84294
  lte15(gscSearchData.date, window.endDate)
84233
84295
  )).all();
84234
84296
  const byDay = /* @__PURE__ */ new Map();