@canonry/canonry 7.80.0 → 7.82.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +16 -0
- package/assets/agent-workspace/skills/canonry/references/server-side-traffic.md +13 -0
- package/assets/assets/{AgentSetupPage-Cr2ZU_4s.js → AgentSetupPage-CcjQeUvK.js} +1 -1
- package/assets/assets/{AuditHistoryPanel-Bb5wq-Bk.js → AuditHistoryPanel-DgTvt2hQ.js} +1 -1
- package/assets/assets/{BacklinksPage-i1cfxTDj.js → BacklinksPage-DFQhtk4d.js} +1 -1
- package/assets/assets/{HistoryPage-C2NRdNrn.js → HistoryPage-CkkWqQol.js} +1 -1
- package/assets/assets/{MeasurementPropertyPage-ighxjOiq.js → MeasurementPropertyPage-Cwnda2BA.js} +1 -1
- package/assets/assets/{ProjectPage-DnAXCPle.js → ProjectPage-C39ua8CG.js} +1 -1
- package/assets/assets/{RunsPage-Ds5K3fOQ.js → RunsPage-B4tMl-_9.js} +1 -1
- package/assets/assets/{SettingsPage-B8Jed-xH.js → SettingsPage-DmDdf02o.js} +1 -1
- package/assets/assets/{SiteHealthSection-cjNQasWj.js → SiteHealthSection-CfQePJ7s.js} +2 -2
- package/assets/assets/ToneBadge-bXf5LaSI.js +60 -0
- package/assets/assets/{TrafficPage-BUid1rkh.js → TrafficPage-C9y8xEr6.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-D3KHgRvF.js → TrafficSourceDetailPage-Dklvlek4.js} +1 -1
- package/assets/assets/{account-context-B50QqDEA.js → account-context-BjM5ZlYl.js} +1 -1
- package/assets/assets/{card-BE2fqwVq.js → card-CDFncx20.js} +1 -1
- package/assets/assets/{extract-error-message-xuN_xeRJ.js → extract-error-message-s3FKmMjt.js} +1 -1
- package/assets/assets/{format-helpers-CiC6gALY.js → format-helpers-CYQ-LjnQ.js} +1 -1
- package/assets/assets/{index-BhoSVTom.js → index-qWvnlyfA.js} +4 -4
- package/assets/assets/{query-client-C5GhDMY7.js → query-client-C_dAEVvf.js} +1 -1
- package/assets/assets/{react-query-CmHyfYFq.js → react-query-BKozxbv1.js} +1 -1
- package/assets/assets/{react-sigma_core.esm.min-SbOH7c6K.js → react-sigma_core.esm.min-J2l6CcJl.js} +1 -1
- package/assets/assets/{service-B2i_ZzWP.js → service-BhAtQFOI.js} +1 -1
- package/assets/index.html +7 -7
- package/dist/{ads-MNMND5VH.js → ads-UDOONRKQ.js} +3 -3
- package/dist/{agent-4KNDL3D3.js → agent-JTEGEX6X.js} +7 -7
- package/dist/{agent-ask-32RTOL2M.js → agent-ask-PUV4EVGS.js} +3 -3
- package/dist/{agent-conversations-BXVWHUZC.js → agent-conversations-TWTSQHBR.js} +3 -3
- package/dist/{agent-memory-7R7UVFS5.js → agent-memory-ZJEXAUJB.js} +3 -3
- package/dist/{agent-providers-QGVLCCH4.js → agent-providers-2RQZSGMP.js} +3 -3
- package/dist/{agent-transcript-7Y6BJCIT.js → agent-transcript-YYS3XBES.js} +3 -3
- package/dist/{analytics-YOGEHW6J.js → analytics-RLC6PRUZ.js} +3 -3
- package/dist/{apply-TB3MF3PH.js → apply-Z5SSCEJ2.js} +3 -3
- package/dist/{backfill-BXEYMV3Y.js → backfill-6K2UMALK.js} +9 -9
- package/dist/{backlinks-6GTL324T.js → backlinks-LDI2VTPT.js} +3 -3
- package/dist/{bing-OMQACSDY.js → bing-SSNMNZEF.js} +3 -3
- package/dist/{bootstrap-ITVA7X45.js → bootstrap-ISRV5I52.js} +10 -10
- package/dist/{cdp-3WBLAWOL.js → cdp-WU7QCPQ3.js} +7 -7
- package/dist/{chunk-7HB6QLX4.js → chunk-2FIDPBWE.js} +1 -1
- package/dist/{chunk-HRFPGXON.js → chunk-2GJLKCXL.js} +61 -1
- package/dist/{chunk-ENUSVOQ5.js → chunk-2PDDTRTG.js} +4 -4
- package/dist/{chunk-AI44ZCMK.js → chunk-462CIYLY.js} +1 -1
- package/dist/{chunk-BLZCNL3B.js → chunk-4IYFVC22.js} +3 -3
- package/dist/{chunk-6ACWXQNZ.js → chunk-5K3S74ZX.js} +2 -1
- package/dist/{chunk-MIQMUSE4.js → chunk-5NDUJLUL.js} +6 -6
- package/dist/{chunk-3QJWOF2B.js → chunk-7ZJHKLTK.js} +1 -1
- package/dist/{chunk-3MGM3U6V.js → chunk-AAMOBJXJ.js} +1 -1
- package/dist/{chunk-OIRHCT63.js → chunk-DWFVH3E2.js} +2 -2
- package/dist/{chunk-72YPSNCG.js → chunk-EIDOBJF2.js} +2 -2
- package/dist/{chunk-45RGH7XI.js → chunk-F4FDJI5O.js} +3 -3
- package/dist/{chunk-7S6SQFFT.js → chunk-GKHM5OT6.js} +2 -2
- package/dist/{chunk-MUYTDD35.js → chunk-HDVJNYYE.js} +18 -3
- package/dist/{chunk-DKONZ36U.js → chunk-J2DA4LUR.js} +402 -270
- package/dist/{chunk-JJO5XNS6.js → chunk-J7EYQFMJ.js} +4 -4
- package/dist/{chunk-WSWHLKKJ.js → chunk-JDEHNF7K.js} +1 -1
- package/dist/{chunk-FCYGY4FT.js → chunk-JMUSNZJR.js} +1 -1
- package/dist/{chunk-KF7I7FBI.js → chunk-JP4RTZGE.js} +2 -2
- package/dist/{chunk-2EWZ3DJ4.js → chunk-LRFCNERP.js} +3 -3
- package/dist/{chunk-B36XUBYK.js → chunk-M7WS2BL3.js} +102 -57
- package/dist/{chunk-PLLDJUG2.js → chunk-NNUZ37NJ.js} +4 -4
- package/dist/{chunk-R3US2RG6.js → chunk-NPMJFPBM.js} +1501 -1353
- package/dist/{chunk-JERATA5R.js → chunk-OH42CAWP.js} +1 -1
- package/dist/{chunk-R26CF4WT.js → chunk-QHEKMJL4.js} +2 -2
- package/dist/{chunk-CJ4JDM5W.js → chunk-RMZCQUKN.js} +347 -335
- package/dist/{chunk-PM2DB3HE.js → chunk-S6IC7ZK2.js} +38 -1
- package/dist/{chunk-T2TIQP5O.js → chunk-SIRWPXGJ.js} +8 -8
- package/dist/{chunk-KZB6HLFT.js → chunk-SVF4P6DB.js} +1 -1
- package/dist/{chunk-RPTOVEZT.js → chunk-T76RQIGK.js} +3 -3
- package/dist/{chunk-TZRYVW75.js → chunk-VVOCZTNC.js} +2 -2
- package/dist/{chunk-4GEEYY3N.js → chunk-VWYB4RA4.js} +2 -2
- package/dist/{chunk-ZHFE6J3A.js → chunk-WB6VIQ7L.js} +1 -1
- package/dist/{chunk-QAVJ6LHN.js → chunk-ZR3RYJF3.js} +5 -5
- package/dist/{chunk-7OAXEMOF.js → chunk-ZX7XDVPC.js} +10 -2
- package/dist/{citations-PGG7JEIN.js → citations-X5LS7SZX.js} +3 -3
- package/dist/{cli-catalog-KC6JYHSW.js → cli-catalog-S4RLOOCK.js} +3 -3
- package/dist/{cli-commands-XFFBO46N.js → cli-commands-BGNM6H6P.js} +7 -7
- package/dist/cli.js +13 -13
- package/dist/{competitor-B3CZOKZU.js → competitor-O5YXN5SD.js} +3 -3
- package/dist/{content-Z4XJMZB4.js → content-C6SO5LNZ.js} +3 -3
- package/dist/{dashboard-L5XCPIFW.js → dashboard-3P6FFYCW.js} +6 -6
- package/dist/{demo-HWM3OWEO.js → demo-OJ6C4DU3.js} +1 -1
- package/dist/{demo-server-5DE5PAEC.js → demo-server-GJXDVREB.js} +12 -12
- package/dist/{discover-OT3K7KQK.js → discover-Z355GK36.js} +3 -3
- package/dist/{discover-eval-DN4J4KD5.js → discover-eval-6OVS55LV.js} +3 -3
- package/dist/{doctor-UDF6OCSR.js → doctor-E52VMIEP.js} +3 -3
- package/dist/{evidence-Z364UFOD.js → evidence-ITVU3HYF.js} +3 -3
- package/dist/{export-cmd-K27XN4G5.js → export-cmd-S2UD3ALT.js} +3 -3
- package/dist/{ga-Q2CELDLG.js → ga-T3UEGAZC.js} +3 -3
- package/dist/{gbp-V2APGSIQ.js → gbp-3G45OTYX.js} +3 -3
- package/dist/{google-WFHYA3QT.js → google-YZS4HLN3.js} +4 -4
- package/dist/{google-marketing-5ROIKBC7.js → google-marketing-LIV6O63R.js} +1 -1
- package/dist/{health-cmd-RGFAJ4YP.js → health-cmd-2FC73XH6.js} +3 -3
- package/dist/{history-RUSU66BZ.js → history-KLRMAVLG.js} +3 -3
- package/dist/index.d.ts +344 -83
- package/dist/index.js +25 -25
- package/dist/{init-WXKOOS25.js → init-CTP7OZTR.js} +12 -12
- package/dist/{insights-4IFQLPS3.js → insights-KTGRGFAN.js} +3 -3
- package/dist/intelligence-service-YX72V6NG.js +16 -0
- package/dist/{journeys-PTC2QY57.js → journeys-PF2OO5W2.js} +3 -3
- package/dist/{keys-JASXI2SR.js → keys-7NTJ7TFJ.js} +3 -3
- package/dist/{keyword-SK2HJXCJ.js → keyword-C37ZTBOS.js} +3 -3
- package/dist/{logs-AS5I5V2A.js → logs-GU223TLX.js} +3 -3
- package/dist/{mcp-BQSOYHEX.js → mcp-2DZFAHOW.js} +4 -4
- package/dist/mcp.js +9 -9
- package/dist/{measurement-plan-LZYYH3OD.js → measurement-plan-ZSZUAW4Y.js} +5 -5
- package/dist/{notify-JM6252F6.js → notify-GYSA5V67.js} +5 -4
- package/dist/{organic-evidence-L3Y6RZIH.js → organic-evidence-2NLMSKHX.js} +3 -3
- package/dist/{overview-VIF2GXYW.js → overview-NL3B5XME.js} +4 -4
- package/dist/{project-FQEPFPRD.js → project-3THJAPRN.js} +3 -3
- package/dist/{query-VKRESS6B.js → query-JU5UHTGT.js} +3 -3
- package/dist/{research-6A6QWKN3.js → research-WLS77NOR.js} +3 -3
- package/dist/{results-export-VUB2ZD5B.js → results-export-RQKUM5OA.js} +3 -3
- package/dist/{run-PG5GYZT7.js → run-H6BA7COC.js} +4 -4
- package/dist/{schedule-EBGOLCQZ.js → schedule-ISTF4OHX.js} +3 -3
- package/dist/{search-NS4XPZRL.js → search-HIYAHDZR.js} +3 -3
- package/dist/{serve-7JITP7HH.js → serve-5WPYWTOF.js} +26 -26
- package/dist/{session-PCUTTGTF.js → session-4B5VWFCF.js} +13 -13
- package/dist/{settings-LNCAYCDF.js → settings-ZI4UQXJO.js} +3 -3
- package/dist/{skills-DWXA55QA.js → skills-LN34DUVA.js} +2 -2
- package/dist/{snapshot-LX7OR7SO.js → snapshot-BDPWMMDS.js} +3 -3
- package/dist/{src-QC2VNJWJ.js → src-UENQLEK4.js} +6 -2
- package/dist/{status-DQ6FNWUT.js → status-6CIRB7TO.js} +4 -4
- package/dist/{technical-aeo-5GU2IY6W.js → technical-aeo-L435UGJL.js} +3 -3
- package/dist/{telemetry-QK2GPII3.js → telemetry-VIY5GILS.js} +4 -4
- package/dist/{tools-LILIKI26.js → tools-LILY27JS.js} +11 -11
- package/dist/{traffic-77CKSYM3.js → traffic-KHGUZSP6.js} +30 -7
- package/dist/{users-7OJ2RGV3.js → users-KLOJMJSB.js} +3 -3
- package/dist/{visibility-stats-VR25XMMY.js → visibility-stats-OG2HEGFX.js} +3 -3
- package/dist/{wordpress-JYXTF4PL.js → wordpress-NPVZAUSN.js} +3 -3
- package/package.json +1 -1
- package/assets/assets/ToneBadge-CUe-WrzN.js +0 -60
- package/dist/intelligence-service-RVKSPP4X.js +0 -16
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
answerServiceAdapters,
|
|
3
3
|
answerServiceDescriptors
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-JDEHNF7K.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-
|
|
30
|
+
} from "./chunk-5NDUJLUL.js";
|
|
31
31
|
import {
|
|
32
32
|
AeroToolProfiles,
|
|
33
33
|
AeroToolScopes,
|
|
34
34
|
buildAeroStateTools,
|
|
35
35
|
isAeroToolProfile,
|
|
36
36
|
truncateToolResult
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-J7EYQFMJ.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-
|
|
56
|
+
} from "./chunk-SVF4P6DB.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-
|
|
67
|
+
} from "./chunk-GKHM5OT6.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-
|
|
79
|
+
} from "./chunk-NNUZ37NJ.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-
|
|
87
|
+
} from "./chunk-JMUSNZJR.js";
|
|
88
88
|
import {
|
|
89
89
|
checkLatestVersionForServer,
|
|
90
90
|
getOrCreateAnonymousId,
|
|
@@ -96,17 +96,17 @@ import {
|
|
|
96
96
|
recordDashboardEvent,
|
|
97
97
|
setTelemetryPreference,
|
|
98
98
|
trackEvent
|
|
99
|
-
} from "./chunk-
|
|
99
|
+
} from "./chunk-VWYB4RA4.js";
|
|
100
100
|
import {
|
|
101
101
|
createCanonryMcpServer
|
|
102
|
-
} from "./chunk-
|
|
102
|
+
} from "./chunk-T76RQIGK.js";
|
|
103
103
|
import {
|
|
104
104
|
CANONRY_MCP_TIERS,
|
|
105
105
|
CANONRY_MCP_TOOLKIT_NAMES
|
|
106
106
|
} from "./chunk-JI7FDO7I.js";
|
|
107
107
|
import {
|
|
108
108
|
ApiClient
|
|
109
|
-
} from "./chunk-
|
|
109
|
+
} from "./chunk-ZX7XDVPC.js";
|
|
110
110
|
import {
|
|
111
111
|
AGENT_PROVIDERS,
|
|
112
112
|
adapterSupportsBatch,
|
|
@@ -138,21 +138,21 @@ import {
|
|
|
138
138
|
saveConfigPatch,
|
|
139
139
|
sentimentInstallReadiness,
|
|
140
140
|
updateSharedProviderExecutionGate
|
|
141
|
-
} from "./chunk-
|
|
141
|
+
} from "./chunk-7ZJHKLTK.js";
|
|
142
142
|
import {
|
|
143
143
|
getBundledSkillSnapshots
|
|
144
|
-
} from "./chunk-
|
|
144
|
+
} from "./chunk-462CIYLY.js";
|
|
145
145
|
import {
|
|
146
146
|
computeFactorAverages,
|
|
147
147
|
deleteSiteCrawlGraphLayout,
|
|
148
148
|
persistSiteCrawlGraphLayout,
|
|
149
149
|
prepareSiteCrawlGraphLayout,
|
|
150
150
|
recordedSharePct
|
|
151
|
-
} from "./chunk-
|
|
151
|
+
} from "./chunk-DWFVH3E2.js";
|
|
152
152
|
import {
|
|
153
153
|
CanonryMcpToolNames,
|
|
154
154
|
canonryMcpTools
|
|
155
|
-
} from "./chunk-
|
|
155
|
+
} from "./chunk-HDVJNYYE.js";
|
|
156
156
|
import {
|
|
157
157
|
PACKAGE_VERSION
|
|
158
158
|
} from "./chunk-AOJU5LKX.js";
|
|
@@ -161,16 +161,16 @@ import {
|
|
|
161
161
|
} from "./chunk-HGMYEDAQ.js";
|
|
162
162
|
import {
|
|
163
163
|
IntelligenceService
|
|
164
|
-
} from "./chunk-
|
|
164
|
+
} from "./chunk-ZR3RYJF3.js";
|
|
165
165
|
import {
|
|
166
166
|
addLogListener,
|
|
167
167
|
createFastifyLogger,
|
|
168
168
|
createLogger
|
|
169
|
-
} from "./chunk-
|
|
169
|
+
} from "./chunk-VVOCZTNC.js";
|
|
170
170
|
import {
|
|
171
171
|
computeCitedCompetitorDomains,
|
|
172
172
|
determineCitationState
|
|
173
|
-
} from "./chunk-
|
|
173
|
+
} from "./chunk-OH42CAWP.js";
|
|
174
174
|
import {
|
|
175
175
|
BingApiError,
|
|
176
176
|
CC_CACHE_DIR,
|
|
@@ -291,7 +291,7 @@ import {
|
|
|
291
291
|
webhookResponseReason,
|
|
292
292
|
webhookTargetRefusalReason,
|
|
293
293
|
writeAuditLog
|
|
294
|
-
} from "./chunk-
|
|
294
|
+
} from "./chunk-NPMJFPBM.js";
|
|
295
295
|
import {
|
|
296
296
|
OperationalLogStore,
|
|
297
297
|
SentimentIdempotencyConflict,
|
|
@@ -376,8 +376,10 @@ import {
|
|
|
376
376
|
siteCrawlSnapshots,
|
|
377
377
|
siteLivenessState,
|
|
378
378
|
trafficSources,
|
|
379
|
+
unrecognizedAgentEventsHourly,
|
|
380
|
+
unrecognizedAgentNotifyState,
|
|
379
381
|
usageCounters
|
|
380
|
-
} from "./chunk-
|
|
382
|
+
} from "./chunk-2GJLKCXL.js";
|
|
381
383
|
import {
|
|
382
384
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
383
385
|
AI_ENGINE_DOMAINS,
|
|
@@ -633,7 +635,7 @@ import {
|
|
|
633
635
|
winnabilityClassLabel,
|
|
634
636
|
withRetry,
|
|
635
637
|
withSkippedProviders
|
|
636
|
-
} from "./chunk-
|
|
638
|
+
} from "./chunk-S6IC7ZK2.js";
|
|
637
639
|
|
|
638
640
|
// src/google-marketing-runtime.ts
|
|
639
641
|
import crypto2 from "crypto";
|
|
@@ -3323,7 +3325,7 @@ import path5 from "path";
|
|
|
3323
3325
|
import { isIP as isIP2 } from "net";
|
|
3324
3326
|
import { fileURLToPath } from "url";
|
|
3325
3327
|
import { setImmediate as yieldToEventLoop4 } from "timers/promises";
|
|
3326
|
-
import { and as
|
|
3328
|
+
import { and as and28, eq as eq34, isNotNull as isNotNull5, isNull as isNull8, lte as lte3, max, ne as ne4, or as or4, sql as sql14 } from "drizzle-orm";
|
|
3327
3329
|
|
|
3328
3330
|
// src/seo-runtime.ts
|
|
3329
3331
|
import { eq, inArray } from "drizzle-orm";
|
|
@@ -5937,7 +5939,7 @@ function nonBlank(value) {
|
|
|
5937
5939
|
return trimmed ? trimmed : void 0;
|
|
5938
5940
|
}
|
|
5939
5941
|
function resolveBuildCommit(env = process.env) {
|
|
5940
|
-
const embedded = true ? "
|
|
5942
|
+
const embedded = true ? "2d31f874106c0d9772c47b19f7d79f2257976fef" : void 0;
|
|
5941
5943
|
return nonBlank(embedded) ?? nonBlank(env.CANONRY_COMMIT);
|
|
5942
5944
|
}
|
|
5943
5945
|
function resolveInstanceIdentity(env = process.env) {
|
|
@@ -14297,9 +14299,84 @@ async function deliverReviewEvent(options) {
|
|
|
14297
14299
|
return sent;
|
|
14298
14300
|
}
|
|
14299
14301
|
|
|
14302
|
+
// src/unrecognized-agent-alerts.ts
|
|
14303
|
+
import { and as and19, desc as desc8, eq as eq22, gte as gte3, sql as sql9 } from "drizzle-orm";
|
|
14304
|
+
var log20 = createLogger("UnrecognizedAgentAlerts");
|
|
14305
|
+
var UNRECOGNIZED_AGENT_ALERT_WINDOW_MS = 7 * 864e5;
|
|
14306
|
+
var UNRECOGNIZED_AGENT_ALERT_MIN_HITS = 3;
|
|
14307
|
+
var UNRECOGNIZED_AGENT_ALERT_INTERVAL_MS = 15 * 6e4;
|
|
14308
|
+
var UnrecognizedAgentNotifyStates = {
|
|
14309
|
+
sent: "sent",
|
|
14310
|
+
skipped: "skipped",
|
|
14311
|
+
stale: "stale",
|
|
14312
|
+
baseline: "baseline"
|
|
14313
|
+
};
|
|
14314
|
+
function projectsWithRecentUnrecognizedAgents(db, now) {
|
|
14315
|
+
const since = new Date(now - UNRECOGNIZED_AGENT_ALERT_WINDOW_MS).toISOString();
|
|
14316
|
+
return db.selectDistinct({ projectId: unrecognizedAgentEventsHourly.projectId }).from(unrecognizedAgentEventsHourly).where(gte3(unrecognizedAgentEventsHourly.tsHour, since)).all().map((row) => row.projectId);
|
|
14317
|
+
}
|
|
14318
|
+
function undecidedUnrecognizedAgents(db, projectId, now) {
|
|
14319
|
+
const decided = new Set(db.select({ agentName: unrecognizedAgentNotifyState.agentName }).from(unrecognizedAgentNotifyState).where(eq22(unrecognizedAgentNotifyState.projectId, projectId)).all().map((row) => row.agentName));
|
|
14320
|
+
const totals = db.select({
|
|
14321
|
+
name: unrecognizedAgentEventsHourly.agentName,
|
|
14322
|
+
hits: sql9`SUM(${unrecognizedAgentEventsHourly.hits})`,
|
|
14323
|
+
firstSeenAt: sql9`MIN(${unrecognizedAgentEventsHourly.tsHour})`,
|
|
14324
|
+
lastSeenAt: sql9`MAX(${unrecognizedAgentEventsHourly.tsHour})`
|
|
14325
|
+
}).from(unrecognizedAgentEventsHourly).where(eq22(unrecognizedAgentEventsHourly.projectId, projectId)).groupBy(unrecognizedAgentEventsHourly.agentName).all();
|
|
14326
|
+
const windowStart = new Date(now - UNRECOGNIZED_AGENT_ALERT_WINDOW_MS).toISOString();
|
|
14327
|
+
const recordingStart = totals.reduce((earliest, total) => earliest === null || total.firstSeenAt < earliest ? total.firstSeenAt : earliest, null);
|
|
14328
|
+
const baselineEnd = recordingStart === null ? null : new Date(Date.parse(recordingStart) + UNRECOGNIZED_AGENT_ALERT_WINDOW_MS).toISOString();
|
|
14329
|
+
const due = [];
|
|
14330
|
+
const stale = [];
|
|
14331
|
+
const baseline = [];
|
|
14332
|
+
for (const total of totals) {
|
|
14333
|
+
if (decided.has(total.name)) continue;
|
|
14334
|
+
if (total.firstSeenAt < windowStart) {
|
|
14335
|
+
stale.push(total.name);
|
|
14336
|
+
continue;
|
|
14337
|
+
}
|
|
14338
|
+
if (baselineEnd !== null && total.firstSeenAt < baselineEnd) {
|
|
14339
|
+
baseline.push(total.name);
|
|
14340
|
+
continue;
|
|
14341
|
+
}
|
|
14342
|
+
if (Number(total.hits) < UNRECOGNIZED_AGENT_ALERT_MIN_HITS) continue;
|
|
14343
|
+
const latest = db.select({ sampledUserAgent: unrecognizedAgentEventsHourly.sampledUserAgent, sampledPath: unrecognizedAgentEventsHourly.sampledPath }).from(unrecognizedAgentEventsHourly).where(and19(eq22(unrecognizedAgentEventsHourly.projectId, projectId), eq22(unrecognizedAgentEventsHourly.agentName, total.name))).orderBy(desc8(unrecognizedAgentEventsHourly.tsHour)).limit(1).get();
|
|
14344
|
+
due.push({
|
|
14345
|
+
name: total.name,
|
|
14346
|
+
hits: Number(total.hits),
|
|
14347
|
+
firstSeenAt: total.firstSeenAt,
|
|
14348
|
+
lastSeenAt: total.lastSeenAt,
|
|
14349
|
+
sampleUserAgent: latest?.sampledUserAgent ?? null,
|
|
14350
|
+
samplePath: latest?.sampledPath ?? null
|
|
14351
|
+
});
|
|
14352
|
+
}
|
|
14353
|
+
due.sort((a, b) => b.hits - a.hits || a.name.localeCompare(b.name));
|
|
14354
|
+
return { due, stale, baseline };
|
|
14355
|
+
}
|
|
14356
|
+
function recordUnrecognizedAgentDecisions(db, projectId, names, state, now) {
|
|
14357
|
+
const recordedAt = new Date(now).toISOString();
|
|
14358
|
+
for (const agentName of names) {
|
|
14359
|
+
db.insert(unrecognizedAgentNotifyState).values({ projectId, agentName, state, recordedAt }).onConflictDoNothing().run();
|
|
14360
|
+
}
|
|
14361
|
+
}
|
|
14362
|
+
function startUnrecognizedAgentAlertLoop(dispatch, intervalMs = UNRECOGNIZED_AGENT_ALERT_INTERVAL_MS) {
|
|
14363
|
+
let running = false;
|
|
14364
|
+
const timer = setInterval(() => {
|
|
14365
|
+
if (running) return;
|
|
14366
|
+
running = true;
|
|
14367
|
+
void dispatch().catch((err) => {
|
|
14368
|
+
log20.warn("unrecognized-agent-alerts.dispatch-failed", { error: describeError(err) });
|
|
14369
|
+
}).finally(() => {
|
|
14370
|
+
running = false;
|
|
14371
|
+
});
|
|
14372
|
+
}, intervalMs);
|
|
14373
|
+
timer.unref?.();
|
|
14374
|
+
return () => clearInterval(timer);
|
|
14375
|
+
}
|
|
14376
|
+
|
|
14300
14377
|
// src/provider-batch-poller.ts
|
|
14301
|
-
import { and as
|
|
14302
|
-
var
|
|
14378
|
+
import { and as and20, eq as eq23, inArray as inArray11, isNotNull as isNotNull3, isNull as isNull4, notExists as notExists2, sql as sql10 } from "drizzle-orm";
|
|
14379
|
+
var log21 = createLogger("ProviderBatchPoller");
|
|
14303
14380
|
var PROVIDER_BATCH_POLL_INITIAL_MS = 3e4;
|
|
14304
14381
|
var PROVIDER_BATCH_POLL_MAX_MS = 10 * 6e4;
|
|
14305
14382
|
var PROVIDER_BATCH_CANCEL_GRACE_MS = 60 * 6e4;
|
|
@@ -14323,7 +14400,7 @@ var ProviderBatchPoller = class {
|
|
|
14323
14400
|
try {
|
|
14324
14401
|
if (await this.advance(row)) settledRuns.set(row.runId, row.projectId);
|
|
14325
14402
|
} catch (err) {
|
|
14326
|
-
|
|
14403
|
+
log21.warn("batch.advance-failed", { batchId: row.id, runId: row.runId, provider: row.provider, error: describeError(err) });
|
|
14327
14404
|
this.reschedule(row.id, null);
|
|
14328
14405
|
}
|
|
14329
14406
|
}
|
|
@@ -14332,7 +14409,7 @@ var ProviderBatchPoller = class {
|
|
|
14332
14409
|
try {
|
|
14333
14410
|
this.deps.runner.finalizeBatchRun(runId, projectId);
|
|
14334
14411
|
} catch (err) {
|
|
14335
|
-
|
|
14412
|
+
log21.warn("batch.finalize-failed", { runId, error: describeError(err) });
|
|
14336
14413
|
}
|
|
14337
14414
|
}
|
|
14338
14415
|
}
|
|
@@ -14341,7 +14418,7 @@ var ProviderBatchPoller = class {
|
|
|
14341
14418
|
* outstanding (ingested or cancelled), so its run may now finalize.
|
|
14342
14419
|
*/
|
|
14343
14420
|
async advance(row) {
|
|
14344
|
-
const runStatus = this.deps.db.select({ status: runs.status }).from(runs).where(
|
|
14421
|
+
const runStatus = this.deps.db.select({ status: runs.status }).from(runs).where(eq23(runs.id, row.runId)).get()?.status;
|
|
14345
14422
|
if (runStatus !== RunStatuses.running) {
|
|
14346
14423
|
await this.deps.runner.abandonProviderBatches([row.id], "Cancelled because its run is no longer running.");
|
|
14347
14424
|
this.schedule.delete(row.id);
|
|
@@ -14358,15 +14435,15 @@ var ProviderBatchPoller = class {
|
|
|
14358
14435
|
status = poll.status;
|
|
14359
14436
|
if (poll.status === "ended") {
|
|
14360
14437
|
const endedAt = poll.endedAt ?? new Date(this.now()).toISOString();
|
|
14361
|
-
const ended = this.deps.db.update(providerBatches).set({ status: ProviderBatchStatuses.ended, endedAt, resultsExpireAt: poll.resultsExpireAt ?? null, updatedAt: endedAt }).where(
|
|
14438
|
+
const ended = this.deps.db.update(providerBatches).set({ status: ProviderBatchStatuses.ended, endedAt, resultsExpireAt: poll.resultsExpireAt ?? null, updatedAt: endedAt }).where(and20(eq23(providerBatches.id, row.id), eq23(providerBatches.status, ProviderBatchStatuses.submitted))).run().changes === 1;
|
|
14362
14439
|
this.schedule.delete(row.id);
|
|
14363
14440
|
return ended ? this.ingest({ ...row, status: ProviderBatchStatuses.ended, resultsExpireAt: poll.resultsExpireAt ?? null }) : false;
|
|
14364
14441
|
}
|
|
14365
14442
|
} catch (err) {
|
|
14366
|
-
|
|
14443
|
+
log21.warn("batch.poll-failed", { batchId: row.id, runId: row.runId, provider: row.provider, error: describeError(err) });
|
|
14367
14444
|
}
|
|
14368
14445
|
} else {
|
|
14369
|
-
|
|
14446
|
+
log21.warn("batch.poll-unavailable", { batchId: row.id, runId: row.runId, provider: row.provider });
|
|
14370
14447
|
}
|
|
14371
14448
|
this.reschedule(row.id, status);
|
|
14372
14449
|
return this.enforceDeadline(row, registered);
|
|
@@ -14382,18 +14459,18 @@ var ProviderBatchPoller = class {
|
|
|
14382
14459
|
const at = new Date(now).toISOString();
|
|
14383
14460
|
if (row.cancelRequestedAt === null) {
|
|
14384
14461
|
if (now < Date.parse(row.deadlineAt)) return false;
|
|
14385
|
-
const claimed = this.deps.db.update(providerBatches).set({ cancelRequestedAt: at, updatedAt: at }).where(
|
|
14386
|
-
|
|
14387
|
-
|
|
14462
|
+
const claimed = this.deps.db.update(providerBatches).set({ cancelRequestedAt: at, updatedAt: at }).where(and20(
|
|
14463
|
+
eq23(providerBatches.id, row.id),
|
|
14464
|
+
eq23(providerBatches.status, ProviderBatchStatuses.submitted),
|
|
14388
14465
|
isNull4(providerBatches.cancelRequestedAt)
|
|
14389
14466
|
)).run().changes === 1;
|
|
14390
14467
|
if (!claimed) return false;
|
|
14391
|
-
|
|
14468
|
+
log21.warn("batch.deadline-passed", { batchId: row.id, runId: row.runId, provider: row.provider, deadlineAt: row.deadlineAt });
|
|
14392
14469
|
if (registered?.adapter.batch && row.providerBatchId) {
|
|
14393
14470
|
try {
|
|
14394
14471
|
await registered.adapter.batch.cancel(row.providerBatchId, registered.config);
|
|
14395
14472
|
} catch (err) {
|
|
14396
|
-
|
|
14473
|
+
log21.warn("batch.cancel-failed", { batchId: row.id, provider: row.provider, error: describeError(err) });
|
|
14397
14474
|
}
|
|
14398
14475
|
}
|
|
14399
14476
|
return false;
|
|
@@ -14403,9 +14480,9 @@ var ProviderBatchPoller = class {
|
|
|
14403
14480
|
status: ProviderBatchStatuses.cancelled,
|
|
14404
14481
|
error: `The provider batch did not end within an hour of being cancelled at its deadline; its ${row.requestCount} answer(s) were not recorded.`,
|
|
14405
14482
|
updatedAt: at
|
|
14406
|
-
}).where(
|
|
14483
|
+
}).where(and20(eq23(providerBatches.id, row.id), eq23(providerBatches.status, ProviderBatchStatuses.submitted))).run().changes === 1;
|
|
14407
14484
|
this.schedule.delete(row.id);
|
|
14408
|
-
if (gaveUp)
|
|
14485
|
+
if (gaveUp) log21.warn("batch.abandoned-after-deadline", { batchId: row.id, runId: row.runId, provider: row.provider });
|
|
14409
14486
|
return gaveUp;
|
|
14410
14487
|
}
|
|
14411
14488
|
async ingest(row) {
|
|
@@ -14414,7 +14491,7 @@ var ProviderBatchPoller = class {
|
|
|
14414
14491
|
return result.kind !== "skipped";
|
|
14415
14492
|
} catch (err) {
|
|
14416
14493
|
const error = describeError(err);
|
|
14417
|
-
|
|
14494
|
+
log21.warn("batch.ingest-failed", { batchId: row.id, runId: row.runId, provider: row.provider, error });
|
|
14418
14495
|
const now = this.now();
|
|
14419
14496
|
const unreadable = row.resultsExpireAt !== null && now >= Date.parse(row.resultsExpireAt);
|
|
14420
14497
|
if (unreadable || now >= Date.parse(row.deadlineAt) + PROVIDER_BATCH_CANCEL_GRACE_MS) {
|
|
@@ -14443,10 +14520,10 @@ var ProviderBatchPoller = class {
|
|
|
14443
14520
|
/** Runs the sweep handed off whose batches have all settled. */
|
|
14444
14521
|
finalizableRuns() {
|
|
14445
14522
|
const holding = [ProviderBatchStatuses.submitting, ...OUTSTANDING_PROVIDER_BATCH_STATUSES];
|
|
14446
|
-
return this.deps.db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(
|
|
14447
|
-
|
|
14523
|
+
return this.deps.db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(and20(
|
|
14524
|
+
eq23(runs.status, RunStatuses.running),
|
|
14448
14525
|
isNotNull3(runs.pendingProviderErrors),
|
|
14449
|
-
notExists2(this.deps.db.select({ one:
|
|
14526
|
+
notExists2(this.deps.db.select({ one: sql10`1` }).from(providerBatches).where(and20(eq23(providerBatches.runId, runs.id), inArray11(providerBatches.status, holding))))
|
|
14450
14527
|
)).all();
|
|
14451
14528
|
}
|
|
14452
14529
|
};
|
|
@@ -14456,7 +14533,7 @@ function startProviderBatchPoller(deps, intervalMs = PROVIDER_BATCH_TICK_MS) {
|
|
|
14456
14533
|
const timer = setInterval(() => {
|
|
14457
14534
|
if (running) return;
|
|
14458
14535
|
running = true;
|
|
14459
|
-
void poller.tick().catch((err) =>
|
|
14536
|
+
void poller.tick().catch((err) => log21.warn("batch.pass-failed", { error: describeError(err) })).finally(() => {
|
|
14460
14537
|
running = false;
|
|
14461
14538
|
});
|
|
14462
14539
|
}, intervalMs);
|
|
@@ -14465,7 +14542,7 @@ function startProviderBatchPoller(deps, intervalMs = PROVIDER_BATCH_TICK_MS) {
|
|
|
14465
14542
|
}
|
|
14466
14543
|
|
|
14467
14544
|
// src/data-refresh.ts
|
|
14468
|
-
var
|
|
14545
|
+
var log22 = createLogger("DataRefresh");
|
|
14469
14546
|
function gaRefreshDays(now = /* @__PURE__ */ new Date()) {
|
|
14470
14547
|
const [reportMonth] = reportMonthsForDoctor(void 0, now);
|
|
14471
14548
|
const reach = inclusiveDayCount(`${reportMonth}-01`, now.toISOString().slice(0, 10)) ?? 0;
|
|
@@ -14506,15 +14583,15 @@ async function refreshAllIntegrations(client, projectName, now = /* @__PURE__ */
|
|
|
14506
14583
|
if (result.status === "rejected") {
|
|
14507
14584
|
const reason = result.reason;
|
|
14508
14585
|
const message = describeError(reason);
|
|
14509
|
-
|
|
14586
|
+
log22.warn("integration.refresh-failed", { projectName, integration, error: message });
|
|
14510
14587
|
return;
|
|
14511
14588
|
}
|
|
14512
14589
|
switch (completion) {
|
|
14513
14590
|
case "synchronous":
|
|
14514
|
-
|
|
14591
|
+
log22.info("integration.refreshed", { projectName, integration });
|
|
14515
14592
|
return;
|
|
14516
14593
|
case "queued":
|
|
14517
|
-
|
|
14594
|
+
log22.info("integration.queued", { projectName, integration, ...queuedRun(result.value) });
|
|
14518
14595
|
return;
|
|
14519
14596
|
default: {
|
|
14520
14597
|
const _exhaustive = completion;
|
|
@@ -14525,9 +14602,9 @@ async function refreshAllIntegrations(client, projectName, now = /* @__PURE__ */
|
|
|
14525
14602
|
}
|
|
14526
14603
|
|
|
14527
14604
|
// src/notifier.ts
|
|
14528
|
-
import { eq as
|
|
14605
|
+
import { eq as eq24, desc as desc9, and as and21, inArray as inArray12, or as or2, isNull as isNull5 } from "drizzle-orm";
|
|
14529
14606
|
import crypto21 from "crypto";
|
|
14530
|
-
var
|
|
14607
|
+
var log23 = createLogger("Notifier");
|
|
14531
14608
|
var sampleDelivery = createOutcomeSampler({ burst: 20, refillMs: 6e4, now: () => Date.now() });
|
|
14532
14609
|
function trackWebhookDelivery(input) {
|
|
14533
14610
|
const sample = sampleDelivery(input.failure ? "failed" : "succeeded");
|
|
@@ -14638,6 +14715,8 @@ var Notifier = class {
|
|
|
14638
14715
|
allowLoopbackWebhooks;
|
|
14639
14716
|
/** Projects with a review dispatch in progress; see {@link dispatchReviewAlerts}. */
|
|
14640
14717
|
reviewDispatchInFlight = /* @__PURE__ */ new Set();
|
|
14718
|
+
/** Set while a {@link dispatchUnrecognizedAgentAlerts} pass runs, so passes never overlap. */
|
|
14719
|
+
unrecognizedAgentDispatchInFlight = false;
|
|
14641
14720
|
constructor(db, serverUrl, opts = {}) {
|
|
14642
14721
|
this.db = db;
|
|
14643
14722
|
this.serverUrl = serverUrl;
|
|
@@ -14645,26 +14724,26 @@ var Notifier = class {
|
|
|
14645
14724
|
}
|
|
14646
14725
|
/** Called after a run completes (success, partial, or failed). */
|
|
14647
14726
|
async onRunCompleted(runId, projectId, opts) {
|
|
14648
|
-
|
|
14649
|
-
const notifs = this.db.select().from(notifications).where(
|
|
14727
|
+
log23.info("run.completed", { runId, projectId, origin: opts?.origin ?? "sweep" });
|
|
14728
|
+
const notifs = this.db.select().from(notifications).where(eq24(notifications.projectId, projectId)).all().filter((n) => n.enabled);
|
|
14650
14729
|
if (notifs.length === 0) {
|
|
14651
|
-
|
|
14730
|
+
log23.info("notifications.none-enabled", { projectId });
|
|
14652
14731
|
return;
|
|
14653
14732
|
}
|
|
14654
|
-
|
|
14655
|
-
const run = this.db.select().from(runs).where(
|
|
14733
|
+
log23.info("notifications.found", { projectId, count: notifs.length });
|
|
14734
|
+
const run = this.db.select().from(runs).where(eq24(runs.id, runId)).get();
|
|
14656
14735
|
if (!run) {
|
|
14657
|
-
|
|
14736
|
+
log23.error("run.not-found", { runId, msg: "skipping notification dispatch" });
|
|
14658
14737
|
return;
|
|
14659
14738
|
}
|
|
14660
|
-
const project = this.db.select().from(projects).where(
|
|
14739
|
+
const project = this.db.select().from(projects).where(eq24(projects.id, projectId)).get();
|
|
14661
14740
|
if (!project) {
|
|
14662
|
-
|
|
14741
|
+
log23.error("project.not-found", { projectId, msg: "skipping notification dispatch" });
|
|
14663
14742
|
return;
|
|
14664
14743
|
}
|
|
14665
14744
|
const transitions = this.computeTransitions(runId, projectId);
|
|
14666
14745
|
const events = [];
|
|
14667
|
-
|
|
14746
|
+
log23.info("run.status", { runId: run.id, status: run.status, projectId });
|
|
14668
14747
|
if ((run.status === "completed" || run.status === "partial") && opts?.origin !== "fill") {
|
|
14669
14748
|
events.push("run.completed");
|
|
14670
14749
|
}
|
|
@@ -14680,7 +14759,7 @@ var Notifier = class {
|
|
|
14680
14759
|
if (!config.url) continue;
|
|
14681
14760
|
const subscribedEvents = config.events;
|
|
14682
14761
|
const matchingEvents = events.filter((e) => subscribedEvents.includes(e));
|
|
14683
|
-
|
|
14762
|
+
log23.info("notification.match", { notificationId: notif.id, subscribedEvents, matchedEvents: matchingEvents });
|
|
14684
14763
|
if (matchingEvents.length === 0) continue;
|
|
14685
14764
|
for (const event of matchingEvents) {
|
|
14686
14765
|
const relevantTransitions = event === "citation.lost" ? lostTransitions : event === "citation.gained" ? gainedTransitions : transitions;
|
|
@@ -14736,9 +14815,9 @@ var Notifier = class {
|
|
|
14736
14815
|
* than inferred from the absence of a webhook.
|
|
14737
14816
|
*/
|
|
14738
14817
|
async onHealthChecked(projectId, report) {
|
|
14739
|
-
const project = this.db.select().from(projects).where(
|
|
14818
|
+
const project = this.db.select().from(projects).where(eq24(projects.id, projectId)).get();
|
|
14740
14819
|
if (!project) {
|
|
14741
|
-
|
|
14820
|
+
log23.error("project.not-found", { projectId, msg: "skipping health notification" });
|
|
14742
14821
|
return null;
|
|
14743
14822
|
}
|
|
14744
14823
|
const rank = healthRank;
|
|
@@ -14769,7 +14848,7 @@ var Notifier = class {
|
|
|
14769
14848
|
const status = noSignal ? "warn" : worst ? worst.status : "ok";
|
|
14770
14849
|
const code = noSignal ? HEALTH_NO_SIGNAL_CODE : worst?.code ?? "health.ok";
|
|
14771
14850
|
const summary = noSignal ? `No health check produced a result (${healthChecks.length} skipped) \u2014 health is unknown, not confirmed.` : worst?.summary ?? `All ${graded.length} health check(s) passing.`;
|
|
14772
|
-
const previous = this.db.select().from(doctorHealthState).where(
|
|
14851
|
+
const previous = this.db.select().from(doctorHealthState).where(eq24(doctorHealthState.projectId, projectId)).get();
|
|
14773
14852
|
const previousStatus = previous?.status ?? null;
|
|
14774
14853
|
const failingSignature = failing.map((c) => `${c.status}:${c.code}`).join(",");
|
|
14775
14854
|
const failingNow = failing.map((c) => ({ id: c.id, code: c.code, rank: rank(c.status) }));
|
|
@@ -14839,7 +14918,7 @@ var Notifier = class {
|
|
|
14839
14918
|
const urgentById = new Map(urgentItems.map((item) => [item.id, item]));
|
|
14840
14919
|
const generalEvent = event;
|
|
14841
14920
|
const urgentRecord = previous?.urgentNotified ?? null;
|
|
14842
|
-
const enabled = generalEvent || urgentItems.length > 0 || urgentRecord ? this.db.select().from(notifications).where(
|
|
14921
|
+
const enabled = generalEvent || urgentItems.length > 0 || urgentRecord ? this.db.select().from(notifications).where(eq24(notifications.projectId, projectId)).all().filter((n) => n.enabled) : [];
|
|
14843
14922
|
const listedEvents = (notif) => notif.config.events;
|
|
14844
14923
|
const hasUrl = (notif) => Boolean(notif.config.url);
|
|
14845
14924
|
const urgentSubscribers = enabled.filter((n) => hasUrl(n) && (listedEvents(n) ?? []).some((e) => URGENT_HEALTH_EVENTS.has(e)));
|
|
@@ -14887,7 +14966,7 @@ var Notifier = class {
|
|
|
14887
14966
|
if (previous === void 0) {
|
|
14888
14967
|
this.db.insert(doctorHealthState).values({ ...observation, notifiedAt: null }).run();
|
|
14889
14968
|
} else {
|
|
14890
|
-
this.db.update(doctorHealthState).set(observation).where(
|
|
14969
|
+
this.db.update(doctorHealthState).set(observation).where(eq24(doctorHealthState.projectId, projectId)).run();
|
|
14891
14970
|
}
|
|
14892
14971
|
for (const group of urgentGroups) {
|
|
14893
14972
|
const { urgentEvent, urgentTold, urgentNew, urgentCleared, urgentNotifs, urgentSettled } = group;
|
|
@@ -14931,10 +15010,10 @@ var Notifier = class {
|
|
|
14931
15010
|
}
|
|
14932
15011
|
if (accepted > 0) {
|
|
14933
15012
|
recordUrgentReceipts(urgentReceipts, group.subscribers, formatUrgentBreaches(urgentSettled) || null);
|
|
14934
|
-
this.db.update(doctorHealthState).set({ urgentNotified: doctorUrgentSummary(urgentReceipts), urgentReceipts }).where(
|
|
15013
|
+
this.db.update(doctorHealthState).set({ urgentNotified: doctorUrgentSummary(urgentReceipts), urgentReceipts }).where(eq24(doctorHealthState.projectId, projectId)).run();
|
|
14935
15014
|
}
|
|
14936
15015
|
if (urgentNotifs.length > 0) {
|
|
14937
|
-
|
|
15016
|
+
log23.info("health.urgent-notified", {
|
|
14938
15017
|
projectId,
|
|
14939
15018
|
event: urgentEvent,
|
|
14940
15019
|
codes: urgentEvent === "health.urgent" ? breachCodes(urgentNew) : breachCodes(urgentCleared),
|
|
@@ -14945,9 +15024,9 @@ var Notifier = class {
|
|
|
14945
15024
|
}
|
|
14946
15025
|
if (!event) {
|
|
14947
15026
|
if (clearedThisPass.length > 0 || easedThisPass.length > 0) {
|
|
14948
|
-
|
|
15027
|
+
log23.info("health.improved", { projectId, status, code, cleared: clearedThisPass, eased: easedThisPass });
|
|
14949
15028
|
} else {
|
|
14950
|
-
|
|
15029
|
+
log23.info("health.unchanged", { projectId, status, code });
|
|
14951
15030
|
}
|
|
14952
15031
|
return null;
|
|
14953
15032
|
}
|
|
@@ -14975,9 +15054,9 @@ var Notifier = class {
|
|
|
14975
15054
|
if (await this.sendWebhook(config.url, payload, notif.id, projectId, notif.webhookSecret ?? null)) delivered += 1;
|
|
14976
15055
|
}
|
|
14977
15056
|
if (delivered > 0) {
|
|
14978
|
-
this.db.update(doctorHealthState).set({ notifiedAt: now, notifiedSignature: formatBreaches(failingNow), pendingSignature: null }).where(
|
|
15057
|
+
this.db.update(doctorHealthState).set({ notifiedAt: now, notifiedSignature: formatBreaches(failingNow), pendingSignature: null }).where(eq24(doctorHealthState.projectId, projectId)).run();
|
|
14979
15058
|
}
|
|
14980
|
-
|
|
15059
|
+
log23.info("health.notified", {
|
|
14981
15060
|
projectId,
|
|
14982
15061
|
event,
|
|
14983
15062
|
status,
|
|
@@ -15005,12 +15084,12 @@ var Notifier = class {
|
|
|
15005
15084
|
async onSiteLivenessChecked(projectId, result) {
|
|
15006
15085
|
const { check, checkedAt } = result;
|
|
15007
15086
|
if (check.status !== "ok" && check.status !== "fail") return null;
|
|
15008
|
-
const project = this.db.select().from(projects).where(
|
|
15087
|
+
const project = this.db.select().from(projects).where(eq24(projects.id, projectId)).get();
|
|
15009
15088
|
if (!project) {
|
|
15010
|
-
|
|
15089
|
+
log23.error("project.not-found", { projectId, msg: "skipping site liveness notification" });
|
|
15011
15090
|
return null;
|
|
15012
15091
|
}
|
|
15013
|
-
const previous = this.db.select().from(siteLivenessState).where(
|
|
15092
|
+
const previous = this.db.select().from(siteLivenessState).where(eq24(siteLivenessState.projectId, projectId)).get();
|
|
15014
15093
|
const sincePrevious = previous ? Date.parse(checkedAt) - Date.parse(previous.checkedAt) : Number.NaN;
|
|
15015
15094
|
const countsAsRepeat = Number.isFinite(sincePrevious) && sincePrevious >= SITE_LIVENESS_MIN_PASS_GAP_MS && sincePrevious <= SITE_LIVENESS_MAX_PASS_GAP_MS;
|
|
15016
15095
|
const failures = check.status === "fail" ? countsAsRepeat ? (previous?.consecutiveFailures ?? 0) + 1 : 1 : 0;
|
|
@@ -15020,7 +15099,7 @@ var Notifier = class {
|
|
|
15020
15099
|
if (check.status === "ok" && outagePaged) event = "health.recovered";
|
|
15021
15100
|
const confirmed = check.status === "fail" && failures >= SITE_LIVENESS_FAILURES_TO_PAGE;
|
|
15022
15101
|
const urgentRecord = previous?.urgentNotifiedAt ?? null;
|
|
15023
|
-
const enabled = event !== null || confirmed || urgentRecord !== null ? this.db.select().from(notifications).where(
|
|
15102
|
+
const enabled = event !== null || confirmed || urgentRecord !== null ? this.db.select().from(notifications).where(eq24(notifications.projectId, projectId)).all().filter((n) => n.enabled) : [];
|
|
15024
15103
|
const listedEvents = (notif) => notif.config.events;
|
|
15025
15104
|
const hasUrl = (notif) => Boolean(notif.config.url);
|
|
15026
15105
|
const scoped = enabled.filter((n) => hasUrl(n) && (listedEvents(n) ?? []).some((e) => URGENT_HEALTH_EVENTS.has(e)));
|
|
@@ -15053,7 +15132,7 @@ var Notifier = class {
|
|
|
15053
15132
|
if (previous === void 0) {
|
|
15054
15133
|
this.db.insert(siteLivenessState).values(observation).run();
|
|
15055
15134
|
} else {
|
|
15056
|
-
this.db.update(siteLivenessState).set(observation).where(
|
|
15135
|
+
this.db.update(siteLivenessState).set(observation).where(eq24(siteLivenessState.projectId, projectId)).run();
|
|
15057
15136
|
}
|
|
15058
15137
|
if (!event && urgentGroups.every((group) => group.urgentEvent === null)) return null;
|
|
15059
15138
|
const payloadFor = (name) => {
|
|
@@ -15089,9 +15168,9 @@ var Notifier = class {
|
|
|
15089
15168
|
}
|
|
15090
15169
|
}
|
|
15091
15170
|
if (event === "health.degraded" && delivered > 0) {
|
|
15092
|
-
this.db.update(siteLivenessState).set({ notifiedAt: checkedAt }).where(
|
|
15171
|
+
this.db.update(siteLivenessState).set({ notifiedAt: checkedAt }).where(eq24(siteLivenessState.projectId, projectId)).run();
|
|
15093
15172
|
}
|
|
15094
|
-
|
|
15173
|
+
log23.info("site-liveness.notified", { projectId, event, code: check.code, subscribers: notifs.length, delivered });
|
|
15095
15174
|
}
|
|
15096
15175
|
for (const group of urgentGroups) {
|
|
15097
15176
|
const { urgentEvent, urgentNotifs, generalIds } = group;
|
|
@@ -15103,10 +15182,10 @@ var Notifier = class {
|
|
|
15103
15182
|
}
|
|
15104
15183
|
if (accepted > 0) {
|
|
15105
15184
|
recordUrgentReceipts(urgentReceipts, group.subscribers, group.settled);
|
|
15106
|
-
this.db.update(siteLivenessState).set({ urgentNotifiedAt: siteUrgentSummary(urgentReceipts), urgentReceipts }).where(
|
|
15185
|
+
this.db.update(siteLivenessState).set({ urgentNotifiedAt: siteUrgentSummary(urgentReceipts), urgentReceipts }).where(eq24(siteLivenessState.projectId, projectId)).run();
|
|
15107
15186
|
}
|
|
15108
15187
|
if (urgentNotifs.length > 0 || generalIds.size > 0) {
|
|
15109
|
-
|
|
15188
|
+
log23.info("site-liveness.urgent-notified", { projectId, event: urgentEvent, code: check.code, subscribers: urgentNotifs.length + generalIds.size, delivered: accepted });
|
|
15110
15189
|
}
|
|
15111
15190
|
}
|
|
15112
15191
|
return event;
|
|
@@ -15129,7 +15208,7 @@ var Notifier = class {
|
|
|
15129
15208
|
*/
|
|
15130
15209
|
isNewInsightNews(projectId, insight) {
|
|
15131
15210
|
const key = insightNotifyKey(projectId, insight);
|
|
15132
|
-
const previous = this.db.select().from(insightNotifyState).where(
|
|
15211
|
+
const previous = this.db.select().from(insightNotifyState).where(eq24(insightNotifyState.key, key)).get();
|
|
15133
15212
|
if (!previous) return true;
|
|
15134
15213
|
if (severityRank(insight.severity) > severityRank(previous.severity)) return true;
|
|
15135
15214
|
const magnitude = insightMagnitude(insight);
|
|
@@ -15151,9 +15230,9 @@ var Notifier = class {
|
|
|
15151
15230
|
magnitude: insightMagnitude(insight),
|
|
15152
15231
|
notifiedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
15153
15232
|
};
|
|
15154
|
-
const existing = this.db.select().from(insightNotifyState).where(
|
|
15233
|
+
const existing = this.db.select().from(insightNotifyState).where(eq24(insightNotifyState.key, key)).get();
|
|
15155
15234
|
if (existing) {
|
|
15156
|
-
this.db.update(insightNotifyState).set(row).where(
|
|
15235
|
+
this.db.update(insightNotifyState).set(row).where(eq24(insightNotifyState.key, key)).run();
|
|
15157
15236
|
} else {
|
|
15158
15237
|
this.db.insert(insightNotifyState).values(row).run();
|
|
15159
15238
|
}
|
|
@@ -15167,11 +15246,11 @@ var Notifier = class {
|
|
|
15167
15246
|
if (criticalInsights.length > 0) insightEvents.push("insight.critical");
|
|
15168
15247
|
if (highInsights.length > 0) insightEvents.push("insight.high");
|
|
15169
15248
|
if (insightEvents.length === 0) return;
|
|
15170
|
-
const notifs = this.db.select().from(notifications).where(
|
|
15249
|
+
const notifs = this.db.select().from(notifications).where(eq24(notifications.projectId, projectId)).all().filter((n) => n.enabled);
|
|
15171
15250
|
if (notifs.length === 0) return;
|
|
15172
|
-
const run = this.db.select().from(runs).where(
|
|
15251
|
+
const run = this.db.select().from(runs).where(eq24(runs.id, runId)).get();
|
|
15173
15252
|
if (!run) return;
|
|
15174
|
-
const project = this.db.select().from(projects).where(
|
|
15253
|
+
const project = this.db.select().from(projects).where(eq24(projects.id, projectId)).get();
|
|
15175
15254
|
if (!project) return;
|
|
15176
15255
|
for (const notif of notifs) {
|
|
15177
15256
|
const config = notif.config;
|
|
@@ -15252,9 +15331,9 @@ var Notifier = class {
|
|
|
15252
15331
|
return delivered;
|
|
15253
15332
|
}
|
|
15254
15333
|
async dispatchQueuedReviewAlerts(projectId) {
|
|
15255
|
-
const pendingReviews = this.db.select().from(gbpReviews).where(
|
|
15256
|
-
const pendingRatings = this.db.select().from(gbpReviewRatings).where(
|
|
15257
|
-
const project = this.db.select().from(projects).where(
|
|
15334
|
+
const pendingReviews = this.db.select().from(gbpReviews).where(and21(eq24(gbpReviews.projectId, projectId), eq24(gbpReviews.alertState, "pending"))).all();
|
|
15335
|
+
const pendingRatings = this.db.select().from(gbpReviewRatings).where(and21(eq24(gbpReviewRatings.projectId, projectId), eq24(gbpReviewRatings.alertState, "pending"))).all();
|
|
15336
|
+
const project = this.db.select().from(projects).where(eq24(projects.id, projectId)).get();
|
|
15258
15337
|
if (!project) return { reviews: 0, ratingDrops: 0, delivered: 0 };
|
|
15259
15338
|
const chatSendsLeft = /* @__PURE__ */ new Map();
|
|
15260
15339
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -15266,10 +15345,10 @@ var Notifier = class {
|
|
|
15266
15345
|
displayName: gbpLocations.displayName,
|
|
15267
15346
|
mapsUri: gbpLocations.mapsUri,
|
|
15268
15347
|
reviewsAccess: gbpLocations.reviewsAccess
|
|
15269
|
-
}).from(gbpLocations).where(
|
|
15348
|
+
}).from(gbpLocations).where(eq24(gbpLocations.projectId, projectId)).all().map((loc) => [loc.locationName, loc])
|
|
15270
15349
|
);
|
|
15271
15350
|
const v4HasWorked = new Set(
|
|
15272
|
-
this.db.select({ locationName: gbpReviewRatings.locationName }).from(gbpReviewRatings).where(
|
|
15351
|
+
this.db.select({ locationName: gbpReviewRatings.locationName }).from(gbpReviewRatings).where(and21(eq24(gbpReviewRatings.projectId, projectId), eq24(gbpReviewRatings.origin, "gbp"))).all().map((row) => row.locationName)
|
|
15273
15352
|
);
|
|
15274
15353
|
const coveredByBusinessProfile = (locationName) => businessProfileReviewsCover(locations.get(locationName)?.reviewsAccess ?? null, v4HasWorked.has(locationName));
|
|
15275
15354
|
const locationRef = (locationName) => {
|
|
@@ -15298,7 +15377,7 @@ var Notifier = class {
|
|
|
15298
15377
|
else if (row.rating === null || row.previousRating === null) setRatingState([row.id], "none");
|
|
15299
15378
|
else ratingsToSend.push(row);
|
|
15300
15379
|
}
|
|
15301
|
-
const enabled = this.db.select().from(notifications).where(
|
|
15380
|
+
const enabled = this.db.select().from(notifications).where(eq24(notifications.projectId, projectId)).all().filter((n) => n.enabled && n.config.url);
|
|
15302
15381
|
const subscribedTo = (event) => enabled.filter((n) => (n.config.events ?? []).includes(event));
|
|
15303
15382
|
const projectRef = { name: project.name, canonicalDomain: project.canonicalDomain };
|
|
15304
15383
|
const dashboardUrl = `${this.serverUrl}/projects/${project.name}`;
|
|
@@ -15352,13 +15431,13 @@ var Notifier = class {
|
|
|
15352
15431
|
if (!sent) all = false;
|
|
15353
15432
|
}
|
|
15354
15433
|
if (all) {
|
|
15355
|
-
this.db.update(gbpReviews).set({ alertState: "sent", alertStateAt: nowIso }).where(
|
|
15356
|
-
|
|
15357
|
-
|
|
15358
|
-
|
|
15359
|
-
review.alertStateAt === null ? isNull5(gbpReviews.alertStateAt) :
|
|
15360
|
-
review.comment === null ? isNull5(gbpReviews.comment) :
|
|
15361
|
-
review.starRating === null ? isNull5(gbpReviews.starRating) :
|
|
15434
|
+
this.db.update(gbpReviews).set({ alertState: "sent", alertStateAt: nowIso }).where(and21(
|
|
15435
|
+
eq24(gbpReviews.id, review.id),
|
|
15436
|
+
eq24(gbpReviews.alertState, "pending"),
|
|
15437
|
+
eq24(gbpReviews.updateTime, review.updateTime),
|
|
15438
|
+
review.alertStateAt === null ? isNull5(gbpReviews.alertStateAt) : eq24(gbpReviews.alertStateAt, review.alertStateAt),
|
|
15439
|
+
review.comment === null ? isNull5(gbpReviews.comment) : eq24(gbpReviews.comment, review.comment),
|
|
15440
|
+
review.starRating === null ? isNull5(gbpReviews.starRating) : eq24(gbpReviews.starRating, review.starRating)
|
|
15362
15441
|
)).run();
|
|
15363
15442
|
}
|
|
15364
15443
|
}
|
|
@@ -15390,7 +15469,7 @@ var Notifier = class {
|
|
|
15390
15469
|
delivered += accepted;
|
|
15391
15470
|
}
|
|
15392
15471
|
}
|
|
15393
|
-
|
|
15472
|
+
log23.info("reviews.dispatched", {
|
|
15394
15473
|
projectId,
|
|
15395
15474
|
reviews: reviewsToSend.length,
|
|
15396
15475
|
ratingDrops: ratingsToSend.length,
|
|
@@ -15399,21 +15478,21 @@ var Notifier = class {
|
|
|
15399
15478
|
return { reviews: reviewsToSend.length, ratingDrops: ratingsToSend.length, delivered };
|
|
15400
15479
|
}
|
|
15401
15480
|
computeTransitions(runId, projectId) {
|
|
15402
|
-
const thisRun = this.db.select().from(runs).where(
|
|
15481
|
+
const thisRun = this.db.select().from(runs).where(eq24(runs.id, runId)).get();
|
|
15403
15482
|
if (!thisRun) return [];
|
|
15404
15483
|
const completeness = measurementRunCompleteness(this.db, runId);
|
|
15405
15484
|
if (completeness.planned && !completeness.complete) {
|
|
15406
|
-
|
|
15485
|
+
log23.info("transitions.skipped-incomplete", {
|
|
15407
15486
|
runId,
|
|
15408
15487
|
executed: completeness.executed,
|
|
15409
15488
|
expected: completeness.expected
|
|
15410
15489
|
});
|
|
15411
15490
|
return [];
|
|
15412
15491
|
}
|
|
15413
|
-
const groupSiblings = this.db.select().from(runs).where(
|
|
15414
|
-
|
|
15415
|
-
|
|
15416
|
-
|
|
15492
|
+
const groupSiblings = this.db.select().from(runs).where(and21(
|
|
15493
|
+
eq24(runs.projectId, projectId),
|
|
15494
|
+
eq24(runs.kind, thisRun.kind),
|
|
15495
|
+
eq24(runs.createdAt, thisRun.createdAt),
|
|
15417
15496
|
notProbeRun()
|
|
15418
15497
|
)).all();
|
|
15419
15498
|
const stillPending = groupSiblings.some((r) => r.status === "queued" || r.status === "running");
|
|
@@ -15430,20 +15509,20 @@ var Notifier = class {
|
|
|
15430
15509
|
return candidate.id > best.id ? candidate : best;
|
|
15431
15510
|
});
|
|
15432
15511
|
if (winner.id !== runId) return [];
|
|
15433
|
-
const projectLocations = this.db.select({ locations: projects.locations }).from(projects).where(
|
|
15512
|
+
const projectLocations = this.db.select({ locations: projects.locations }).from(projects).where(eq24(projects.id, projectId)).get();
|
|
15434
15513
|
const locationCount = Math.max(
|
|
15435
15514
|
1,
|
|
15436
15515
|
(projectLocations?.locations ?? []).length
|
|
15437
15516
|
);
|
|
15438
15517
|
const RECENT_FETCH_LIMIT = Math.max(8, locationCount * 4);
|
|
15439
15518
|
const recentRuns = this.db.select().from(runs).where(
|
|
15440
|
-
|
|
15441
|
-
|
|
15442
|
-
|
|
15443
|
-
or2(
|
|
15519
|
+
and21(
|
|
15520
|
+
eq24(runs.projectId, projectId),
|
|
15521
|
+
eq24(runs.kind, thisRun.kind),
|
|
15522
|
+
or2(eq24(runs.status, "completed"), eq24(runs.status, "partial")),
|
|
15444
15523
|
notProbeRun()
|
|
15445
15524
|
)
|
|
15446
|
-
).orderBy(
|
|
15525
|
+
).orderBy(desc9(runs.createdAt), desc9(runs.id)).limit(RECENT_FETCH_LIMIT).all();
|
|
15447
15526
|
const groups = groupRunsByCreatedAt(recentRuns);
|
|
15448
15527
|
const currentGroupIdx = groups.findIndex((g) => g[0]?.createdAt === thisRun.createdAt);
|
|
15449
15528
|
if (currentGroupIdx < 0) return [];
|
|
@@ -15458,7 +15537,7 @@ var Notifier = class {
|
|
|
15458
15537
|
provider: querySnapshots.provider,
|
|
15459
15538
|
location: querySnapshots.location,
|
|
15460
15539
|
citationState: querySnapshots.citationState
|
|
15461
|
-
}).from(querySnapshots).leftJoin(queries,
|
|
15540
|
+
}).from(querySnapshots).leftJoin(queries, eq24(querySnapshots.queryId, queries.id)).where(inArray12(querySnapshots.runId, currentRunIds)).all();
|
|
15462
15541
|
const previousSnapshots = this.db.select({
|
|
15463
15542
|
queryId: querySnapshots.queryId,
|
|
15464
15543
|
provider: querySnapshots.provider,
|
|
@@ -15488,6 +15567,55 @@ var Notifier = class {
|
|
|
15488
15567
|
return transitions;
|
|
15489
15568
|
}
|
|
15490
15569
|
/** True only when the destination accepted the payload. Callers that record "already said" must not count an attempt. */
|
|
15570
|
+
/**
|
|
15571
|
+
* Send `traffic.agent.discovered` for bots and agents no rule recognizes:
|
|
15572
|
+
* one message per project listing the names first seen in the last 7 days
|
|
15573
|
+
* that reached 3 hits. A name is decided once: `sent` when a webhook accepted
|
|
15574
|
+
* it, `skipped` when nothing subscribed, so subscribing later starts from new
|
|
15575
|
+
* names, `stale` when first seen before the window, and `baseline` when first
|
|
15576
|
+
* seen in the project's first 7 days of recorded traffic. When every delivery
|
|
15577
|
+
* fails the names stay undecided and the next pass retries them until they
|
|
15578
|
+
* age out of the window. Returns how many names were sent.
|
|
15579
|
+
*/
|
|
15580
|
+
async dispatchUnrecognizedAgentAlerts(now = Date.now()) {
|
|
15581
|
+
if (this.unrecognizedAgentDispatchInFlight) return 0;
|
|
15582
|
+
this.unrecognizedAgentDispatchInFlight = true;
|
|
15583
|
+
let sentNames = 0;
|
|
15584
|
+
try {
|
|
15585
|
+
for (const projectId of projectsWithRecentUnrecognizedAgents(this.db, now)) {
|
|
15586
|
+
const { due, stale, baseline } = undecidedUnrecognizedAgents(this.db, projectId, now);
|
|
15587
|
+
recordUnrecognizedAgentDecisions(this.db, projectId, stale, UnrecognizedAgentNotifyStates.stale, now);
|
|
15588
|
+
recordUnrecognizedAgentDecisions(this.db, projectId, baseline, UnrecognizedAgentNotifyStates.baseline, now);
|
|
15589
|
+
if (due.length === 0) continue;
|
|
15590
|
+
const names = due.map((agent) => agent.name);
|
|
15591
|
+
const subscribed = this.db.select().from(notifications).where(eq24(notifications.projectId, projectId)).all().filter((n) => n.enabled && n.config.url && n.config.events.includes("traffic.agent.discovered"));
|
|
15592
|
+
if (subscribed.length === 0) {
|
|
15593
|
+
recordUnrecognizedAgentDecisions(this.db, projectId, names, UnrecognizedAgentNotifyStates.skipped, now);
|
|
15594
|
+
continue;
|
|
15595
|
+
}
|
|
15596
|
+
const project = this.db.select().from(projects).where(eq24(projects.id, projectId)).get();
|
|
15597
|
+
if (!project) continue;
|
|
15598
|
+
const payload = {
|
|
15599
|
+
source: "canonry",
|
|
15600
|
+
event: "traffic.agent.discovered",
|
|
15601
|
+
project: { name: project.name, canonicalDomain: project.canonicalDomain },
|
|
15602
|
+
agents: due,
|
|
15603
|
+
dashboardUrl: `${this.serverUrl}/projects/${project.name}`
|
|
15604
|
+
};
|
|
15605
|
+
let accepted = false;
|
|
15606
|
+
for (const notif of subscribed) {
|
|
15607
|
+
if (await this.sendWebhook(notif.config.url, payload, notif.id, projectId, notif.webhookSecret ?? null)) accepted = true;
|
|
15608
|
+
}
|
|
15609
|
+
if (accepted) {
|
|
15610
|
+
recordUnrecognizedAgentDecisions(this.db, projectId, names, UnrecognizedAgentNotifyStates.sent, now);
|
|
15611
|
+
sentNames += names.length;
|
|
15612
|
+
}
|
|
15613
|
+
}
|
|
15614
|
+
} finally {
|
|
15615
|
+
this.unrecognizedAgentDispatchInFlight = false;
|
|
15616
|
+
}
|
|
15617
|
+
return sentNames;
|
|
15618
|
+
}
|
|
15491
15619
|
async sendWebhook(url, payload, notificationId, projectId, webhookSecret) {
|
|
15492
15620
|
const elapsed = startOutcomeTimer();
|
|
15493
15621
|
const destination = resolveDestination(url);
|
|
@@ -15496,7 +15624,7 @@ var Notifier = class {
|
|
|
15496
15624
|
const targetLabel = redactNotificationUrl(url).urlDisplay;
|
|
15497
15625
|
const targetCheck = await resolveWebhookTarget(url, { allowLoopback: this.allowLoopbackWebhooks });
|
|
15498
15626
|
if (!targetCheck.ok) {
|
|
15499
|
-
|
|
15627
|
+
log23.error("webhook.ssrf-blocked", { url: targetLabel, reason: targetCheck.message });
|
|
15500
15628
|
this.logDelivery(projectId, notificationId, payload.event, "failed", `SSRF: ${targetCheck.message}`);
|
|
15501
15629
|
const reasonCode = webhookTargetRefusalReason(targetCheck);
|
|
15502
15630
|
trackWebhookDelivery({
|
|
@@ -15509,7 +15637,7 @@ var Notifier = class {
|
|
|
15509
15637
|
});
|
|
15510
15638
|
return false;
|
|
15511
15639
|
}
|
|
15512
|
-
|
|
15640
|
+
log23.info("webhook.send", { event: payload.event, url: targetLabel });
|
|
15513
15641
|
const maxRetries = 3;
|
|
15514
15642
|
const delays = [1e3, 4e3, 16e3];
|
|
15515
15643
|
let lastStatusClass = "network";
|
|
@@ -15518,13 +15646,13 @@ var Notifier = class {
|
|
|
15518
15646
|
try {
|
|
15519
15647
|
const response = await deliverWebhook(targetCheck.target, body, signingSecret);
|
|
15520
15648
|
if (response.status >= 200 && response.status < 300) {
|
|
15521
|
-
|
|
15649
|
+
log23.info("webhook.delivered", { event: payload.event, url: targetLabel, httpStatus: response.status });
|
|
15522
15650
|
this.logDelivery(projectId, notificationId, payload.event, "sent", null);
|
|
15523
15651
|
trackWebhookDelivery({ url, event: payload.event, attempts: attempt + 1, durationBucket: elapsed(), statusClass: "2xx" });
|
|
15524
15652
|
return true;
|
|
15525
15653
|
}
|
|
15526
15654
|
const errorDetail = response.error ?? `HTTP ${response.status}`;
|
|
15527
|
-
|
|
15655
|
+
log23.warn("webhook.attempt-failed", { event: payload.event, url: targetLabel, attempt: attempt + 1, maxRetries, httpStatus: response.status, error: errorDetail });
|
|
15528
15656
|
lastStatusClass = response.status === 0 && response.timedOut ? "timeout" : statusClassOf(response.status);
|
|
15529
15657
|
lastFailure = { reasonCode: webhookResponseReason(response) ?? "UNKNOWN" };
|
|
15530
15658
|
if (attempt === maxRetries - 1) {
|
|
@@ -15536,7 +15664,7 @@ var Notifier = class {
|
|
|
15536
15664
|
lastFailure = outcomeFailure(err);
|
|
15537
15665
|
if (attempt === maxRetries - 1) {
|
|
15538
15666
|
this.logDelivery(projectId, notificationId, payload.event, "failed", errorDetail);
|
|
15539
|
-
|
|
15667
|
+
log23.error("webhook.exhausted", { event: payload.event, url: targetLabel, maxRetries, error: errorDetail });
|
|
15540
15668
|
}
|
|
15541
15669
|
}
|
|
15542
15670
|
if (attempt < maxRetries - 1) {
|
|
@@ -15568,8 +15696,8 @@ var Notifier = class {
|
|
|
15568
15696
|
};
|
|
15569
15697
|
|
|
15570
15698
|
// src/run-coordinator.ts
|
|
15571
|
-
import { eq as
|
|
15572
|
-
var
|
|
15699
|
+
import { eq as eq25 } from "drizzle-orm";
|
|
15700
|
+
var log24 = createLogger("RunCoordinator");
|
|
15573
15701
|
function reportInsightsGenerated(runTrigger, outcome) {
|
|
15574
15702
|
trackFeatureCompleted({
|
|
15575
15703
|
feature: "insights",
|
|
@@ -15602,22 +15730,22 @@ var RunCoordinator = class {
|
|
|
15602
15730
|
*/
|
|
15603
15731
|
onAnswersRecorded;
|
|
15604
15732
|
async onRunCompleted(runId, projectId, opts) {
|
|
15605
|
-
const runRow = this.db.select().from(runs).where(
|
|
15733
|
+
const runRow = this.db.select().from(runs).where(eq25(runs.id, runId)).get();
|
|
15606
15734
|
const kind = runRow?.kind ?? RunKinds["answer-visibility"];
|
|
15607
15735
|
if (runRow?.trigger === RunTriggers.probe) {
|
|
15608
|
-
|
|
15736
|
+
log24.info("probe.skip-side-effects", { runId, projectId, kind });
|
|
15609
15737
|
return;
|
|
15610
15738
|
}
|
|
15611
15739
|
if (kind === RunKinds["answer-visibility"] && (runRow?.status === RunStatuses.completed || runRow?.status === RunStatuses.partial) && this.onAnswersRecorded) {
|
|
15612
15740
|
try {
|
|
15613
15741
|
await this.onAnswersRecorded(runId, projectId);
|
|
15614
15742
|
} catch (err) {
|
|
15615
|
-
|
|
15743
|
+
log24.error("answers-recorded.failed", { runId, projectId, error: describeError(err) });
|
|
15616
15744
|
}
|
|
15617
15745
|
}
|
|
15618
15746
|
const completeness = measurementRunCompleteness(this.db, runId);
|
|
15619
15747
|
if (completeness.planned && !completeness.complete) {
|
|
15620
|
-
|
|
15748
|
+
log24.info("measurement.incomplete-skip-derived", {
|
|
15621
15749
|
runId,
|
|
15622
15750
|
projectId,
|
|
15623
15751
|
executed: completeness.executed,
|
|
@@ -15626,7 +15754,7 @@ var RunCoordinator = class {
|
|
|
15626
15754
|
try {
|
|
15627
15755
|
await this.notifier.onRunCompleted(runId, projectId);
|
|
15628
15756
|
} catch (err) {
|
|
15629
|
-
|
|
15757
|
+
log24.error("notifier.failed", { runId, error: describeError(err) });
|
|
15630
15758
|
}
|
|
15631
15759
|
return;
|
|
15632
15760
|
}
|
|
@@ -15634,7 +15762,7 @@ var RunCoordinator = class {
|
|
|
15634
15762
|
try {
|
|
15635
15763
|
await this.onSentimentCompleted(runId, projectId);
|
|
15636
15764
|
} catch {
|
|
15637
|
-
|
|
15765
|
+
log24.error("sentiment.wakeup-failed", { runId, projectId });
|
|
15638
15766
|
}
|
|
15639
15767
|
}
|
|
15640
15768
|
let insightCount = 0;
|
|
@@ -15654,12 +15782,12 @@ var RunCoordinator = class {
|
|
|
15654
15782
|
try {
|
|
15655
15783
|
await this.onInsightsGenerated(runId, projectId, result);
|
|
15656
15784
|
} catch (err) {
|
|
15657
|
-
|
|
15785
|
+
log24.error("insight-webhook.failed", { runId, error: describeError(err) });
|
|
15658
15786
|
}
|
|
15659
15787
|
}
|
|
15660
15788
|
}
|
|
15661
15789
|
} catch (err) {
|
|
15662
|
-
|
|
15790
|
+
log24.error("intelligence.failed", { runId, error: describeError(err) });
|
|
15663
15791
|
insightsOutcome = { status: OutcomeStatuses.failed, ...outcomeFailure(err), durationBucket: elapsed() };
|
|
15664
15792
|
}
|
|
15665
15793
|
reportInsightsGenerated(runRow?.trigger, insightsOutcome);
|
|
@@ -15677,24 +15805,24 @@ var RunCoordinator = class {
|
|
|
15677
15805
|
try {
|
|
15678
15806
|
await this.onInsightsGenerated(runId, projectId, analysisResultFromInsights(gbpInsights));
|
|
15679
15807
|
} catch (err) {
|
|
15680
|
-
|
|
15808
|
+
log24.error("gbp-insight-webhook.failed", { runId, error: describeError(err) });
|
|
15681
15809
|
}
|
|
15682
15810
|
}
|
|
15683
15811
|
} catch (err) {
|
|
15684
|
-
|
|
15812
|
+
log24.error("gbp-intelligence.failed", { runId, error: describeError(err) });
|
|
15685
15813
|
insightsOutcome = { status: OutcomeStatuses.failed, ...outcomeFailure(err), durationBucket: elapsed() };
|
|
15686
15814
|
}
|
|
15687
15815
|
reportInsightsGenerated(runRow?.trigger, insightsOutcome);
|
|
15688
15816
|
try {
|
|
15689
15817
|
await this.notifier.dispatchReviewAlerts(projectId);
|
|
15690
15818
|
} catch (err) {
|
|
15691
|
-
|
|
15819
|
+
log24.error("review-webhook.failed", { runId, error: describeError(err) });
|
|
15692
15820
|
}
|
|
15693
15821
|
}
|
|
15694
15822
|
try {
|
|
15695
15823
|
await (opts ? this.notifier.onRunCompleted(runId, projectId, opts) : this.notifier.onRunCompleted(runId, projectId));
|
|
15696
15824
|
} catch (err) {
|
|
15697
|
-
|
|
15825
|
+
log24.error("notifier.failed", { runId, error: describeError(err) });
|
|
15698
15826
|
}
|
|
15699
15827
|
if (this.onAeroEvent) {
|
|
15700
15828
|
try {
|
|
@@ -15707,7 +15835,7 @@ var RunCoordinator = class {
|
|
|
15707
15835
|
};
|
|
15708
15836
|
await this.onAeroEvent(ctx);
|
|
15709
15837
|
} catch (err) {
|
|
15710
|
-
|
|
15838
|
+
log24.error("aero.failed", { runId, error: describeError(err) });
|
|
15711
15839
|
}
|
|
15712
15840
|
}
|
|
15713
15841
|
}
|
|
@@ -15722,7 +15850,7 @@ var RunCoordinator = class {
|
|
|
15722
15850
|
* so the Aero queue is never starved of a follow-up.
|
|
15723
15851
|
*/
|
|
15724
15852
|
buildDiscoveryAeroContext(runId, projectId, status, error) {
|
|
15725
|
-
const session = this.db.select().from(discoverySessions).where(
|
|
15853
|
+
const session = this.db.select().from(discoverySessions).where(eq25(discoverySessions.runId, runId)).get();
|
|
15726
15854
|
const competitorMap = session ? session.competitorMap : [];
|
|
15727
15855
|
return {
|
|
15728
15856
|
kind: RunKinds["aeo-discover-probe"],
|
|
@@ -15765,12 +15893,12 @@ function analysisResultFromInsights(insights2) {
|
|
|
15765
15893
|
|
|
15766
15894
|
// src/agent/session-registry.ts
|
|
15767
15895
|
import crypto23 from "crypto";
|
|
15768
|
-
import { eq as
|
|
15896
|
+
import { eq as eq28 } from "drizzle-orm";
|
|
15769
15897
|
|
|
15770
15898
|
// src/agent/remote-mcp.ts
|
|
15771
15899
|
import { Type } from "@earendil-works/pi-ai";
|
|
15772
15900
|
import { Client, StreamableHTTPClientTransport } from "@modelcontextprotocol/client";
|
|
15773
|
-
var
|
|
15901
|
+
var log25 = createLogger("RemoteMcp");
|
|
15774
15902
|
var AERO_EXCLUDED_MCP_TOOLS = /* @__PURE__ */ new Set([]);
|
|
15775
15903
|
async function connectStreamableHttp(server) {
|
|
15776
15904
|
const transport = new StreamableHTTPClientTransport(new URL(server.url), {
|
|
@@ -15838,7 +15966,7 @@ async function loadExternalMcpTools(servers, opts = {}) {
|
|
|
15838
15966
|
try {
|
|
15839
15967
|
client = await connect(server);
|
|
15840
15968
|
} catch (err) {
|
|
15841
|
-
|
|
15969
|
+
log25.error("external-mcp.connect-failed", {
|
|
15842
15970
|
label,
|
|
15843
15971
|
error: describeError(err)
|
|
15844
15972
|
});
|
|
@@ -15848,7 +15976,7 @@ async function loadExternalMcpTools(servers, opts = {}) {
|
|
|
15848
15976
|
try {
|
|
15849
15977
|
listed = await client.listTools();
|
|
15850
15978
|
} catch (err) {
|
|
15851
|
-
|
|
15979
|
+
log25.error("external-mcp.list-failed", {
|
|
15852
15980
|
label,
|
|
15853
15981
|
error: describeError(err)
|
|
15854
15982
|
});
|
|
@@ -15859,7 +15987,7 @@ async function loadExternalMcpTools(servers, opts = {}) {
|
|
|
15859
15987
|
if (!isReadOnly(tool)) continue;
|
|
15860
15988
|
tools.push(adaptRemoteTool(client, tool));
|
|
15861
15989
|
}
|
|
15862
|
-
|
|
15990
|
+
log25.info("external-mcp.loaded", {
|
|
15863
15991
|
label,
|
|
15864
15992
|
discovered: listed.tools.length,
|
|
15865
15993
|
adopted: tools.length
|
|
@@ -15870,7 +15998,7 @@ async function loadExternalMcpTools(servers, opts = {}) {
|
|
|
15870
15998
|
|
|
15871
15999
|
// src/agent/memory-store.ts
|
|
15872
16000
|
import crypto22 from "crypto";
|
|
15873
|
-
import { and as
|
|
16001
|
+
import { and as and22, desc as desc10, eq as eq26, like, sql as sql11, or as or3 } from "drizzle-orm";
|
|
15874
16002
|
var COMPACTION_KEY_PREFIX = "compaction:";
|
|
15875
16003
|
var COMPACTION_NOTES_PER_SESSION = 3;
|
|
15876
16004
|
function rowToDto(row) {
|
|
@@ -15884,7 +16012,7 @@ function rowToDto(row) {
|
|
|
15884
16012
|
};
|
|
15885
16013
|
}
|
|
15886
16014
|
function listMemoryEntries(db, projectId, opts = {}) {
|
|
15887
|
-
const query = db.select().from(agentMemory).where(
|
|
16015
|
+
const query = db.select().from(agentMemory).where(eq26(agentMemory.projectId, projectId)).orderBy(desc10(agentMemory.updatedAt));
|
|
15888
16016
|
const rows = opts.limit === void 0 ? query.all() : query.limit(opts.limit).all();
|
|
15889
16017
|
return rows.map(rowToDto);
|
|
15890
16018
|
}
|
|
@@ -15915,22 +16043,22 @@ function upsertMemoryEntry(db, args) {
|
|
|
15915
16043
|
updatedAt: now
|
|
15916
16044
|
}
|
|
15917
16045
|
}).run();
|
|
15918
|
-
const row = db.select().from(agentMemory).where(
|
|
16046
|
+
const row = db.select().from(agentMemory).where(and22(eq26(agentMemory.projectId, args.projectId), eq26(agentMemory.key, args.key))).get();
|
|
15919
16047
|
if (!row) throw new Error("memory upsert produced no row");
|
|
15920
16048
|
return rowToDto(row);
|
|
15921
16049
|
}
|
|
15922
16050
|
function deleteMemoryEntry(db, projectId, key) {
|
|
15923
|
-
const result = db.delete(agentMemory).where(
|
|
16051
|
+
const result = db.delete(agentMemory).where(and22(eq26(agentMemory.projectId, projectId), eq26(agentMemory.key, key))).run();
|
|
15924
16052
|
const changes = result.changes ?? 0;
|
|
15925
16053
|
return changes > 0;
|
|
15926
16054
|
}
|
|
15927
16055
|
function loadRecentForHydrate(db, projectId, limit, sessionId) {
|
|
15928
16056
|
if (sessionId === void 0) return listMemoryEntries(db, projectId, { limit });
|
|
15929
16057
|
const sessionPrefix = `compaction:${sessionId ?? ""}:`;
|
|
15930
|
-
return db.select().from(agentMemory).where(
|
|
15931
|
-
|
|
15932
|
-
or3(
|
|
15933
|
-
)).orderBy(
|
|
16058
|
+
return db.select().from(agentMemory).where(and22(
|
|
16059
|
+
eq26(agentMemory.projectId, projectId),
|
|
16060
|
+
or3(sql11`substr(${agentMemory.key}, 1, ${COMPACTION_KEY_PREFIX.length}) <> ${COMPACTION_KEY_PREFIX}`, sessionId ? sql11`substr(${agentMemory.key}, 1, ${sessionPrefix.length}) = ${sessionPrefix}` : void 0)
|
|
16061
|
+
)).orderBy(desc10(agentMemory.updatedAt)).limit(limit).all().map(rowToDto);
|
|
15934
16062
|
}
|
|
15935
16063
|
function writeCompactionNote(db, args) {
|
|
15936
16064
|
if (Buffer.byteLength(args.summary, "utf8") > AGENT_MEMORY_VALUE_MAX_BYTES) {
|
|
@@ -15954,16 +16082,16 @@ function writeCompactionNote(db, args) {
|
|
|
15954
16082
|
}).run();
|
|
15955
16083
|
const sessionPrefix = `${COMPACTION_KEY_PREFIX}${args.sessionId}:`;
|
|
15956
16084
|
const existing = tx.select({ id: agentMemory.id, updatedAt: agentMemory.updatedAt }).from(agentMemory).where(
|
|
15957
|
-
|
|
15958
|
-
|
|
16085
|
+
and22(
|
|
16086
|
+
eq26(agentMemory.projectId, args.projectId),
|
|
15959
16087
|
like(agentMemory.key, `${sessionPrefix}%`)
|
|
15960
16088
|
)
|
|
15961
|
-
).orderBy(
|
|
16089
|
+
).orderBy(desc10(agentMemory.updatedAt)).all();
|
|
15962
16090
|
const stale = existing.slice(COMPACTION_NOTES_PER_SESSION).map((r) => r.id);
|
|
15963
16091
|
if (stale.length > 0) {
|
|
15964
|
-
tx.delete(agentMemory).where(
|
|
16092
|
+
tx.delete(agentMemory).where(sql11`${agentMemory.id} IN (${sql11.join(stale.map((s) => sql11`${s}`), sql11`, `)})`).run();
|
|
15965
16093
|
}
|
|
15966
|
-
const row = tx.select().from(agentMemory).where(
|
|
16094
|
+
const row = tx.select().from(agentMemory).where(and22(eq26(agentMemory.projectId, args.projectId), eq26(agentMemory.key, key))).get();
|
|
15967
16095
|
if (row) inserted = rowToDto(row);
|
|
15968
16096
|
});
|
|
15969
16097
|
if (!inserted) throw new Error("compaction note write produced no row");
|
|
@@ -16037,7 +16165,7 @@ function trackAeroTurn(agent, turn) {
|
|
|
16037
16165
|
}
|
|
16038
16166
|
|
|
16039
16167
|
// src/agent/project-shape.ts
|
|
16040
|
-
import { and as
|
|
16168
|
+
import { and as and23, count as count4, eq as eq27 } from "drizzle-orm";
|
|
16041
16169
|
var ADVANCED_TOOLS = [
|
|
16042
16170
|
"canonry_measurement_portfolio_summary",
|
|
16043
16171
|
"canonry_measurement_overview",
|
|
@@ -16064,10 +16192,10 @@ var ADVANCED_ROUTES = [
|
|
|
16064
16192
|
];
|
|
16065
16193
|
function aeroProjectShape(db, projectId) {
|
|
16066
16194
|
try {
|
|
16067
|
-
const pointer = db.select().from(measurementPlans).where(
|
|
16068
|
-
const version = pointer ? db.select().from(measurementPlanVersions).where(
|
|
16069
|
-
|
|
16070
|
-
|
|
16195
|
+
const pointer = db.select().from(measurementPlans).where(eq27(measurementPlans.projectId, projectId)).get();
|
|
16196
|
+
const version = pointer ? db.select().from(measurementPlanVersions).where(and23(
|
|
16197
|
+
eq27(measurementPlanVersions.projectId, projectId),
|
|
16198
|
+
eq27(measurementPlanVersions.id, pointer.activeVersionId)
|
|
16071
16199
|
)).get() : void 0;
|
|
16072
16200
|
if (version) {
|
|
16073
16201
|
const plan = JSON.parse(version.canonicalJson);
|
|
@@ -16094,7 +16222,7 @@ canonry_visibility_report describes the whole project, not one Property. canonry
|
|
|
16094
16222
|
pinned: ADVANCED_TOOLS
|
|
16095
16223
|
};
|
|
16096
16224
|
}
|
|
16097
|
-
const tracked = db.select({ total: count4() }).from(queries).where(
|
|
16225
|
+
const tracked = db.select({ total: count4() }).from(queries).where(eq27(queries.projectId, projectId)).get()?.total ?? 0;
|
|
16098
16226
|
return {
|
|
16099
16227
|
prompt: `
|
|
16100
16228
|
|
|
@@ -16234,7 +16362,7 @@ async function compactMessages(args) {
|
|
|
16234
16362
|
}
|
|
16235
16363
|
|
|
16236
16364
|
// src/agent/session-registry.ts
|
|
16237
|
-
var
|
|
16365
|
+
var log26 = createLogger("SessionRegistry");
|
|
16238
16366
|
var MAX_HYDRATE_NOTES = 20;
|
|
16239
16367
|
var MAX_HYDRATE_BYTES = 32 * 1024;
|
|
16240
16368
|
function escapeMemoryFragment(value) {
|
|
@@ -16293,7 +16421,7 @@ var SessionRegistry = class {
|
|
|
16293
16421
|
if (!this.externalToolsPromise) {
|
|
16294
16422
|
this.externalToolsPromise = loadExternalMcpTools(this.opts.config.externalMcpServers).catch(
|
|
16295
16423
|
(err) => {
|
|
16296
|
-
|
|
16424
|
+
log26.error("external-mcp.load-failed", {
|
|
16297
16425
|
error: describeError(err)
|
|
16298
16426
|
});
|
|
16299
16427
|
return [];
|
|
@@ -16350,7 +16478,7 @@ var SessionRegistry = class {
|
|
|
16350
16478
|
modelProvider: effectiveProvider,
|
|
16351
16479
|
modelId: effectiveModelId,
|
|
16352
16480
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
16353
|
-
}).where(
|
|
16481
|
+
}).where(eq28(agentSessions.projectId, projectId)).run();
|
|
16354
16482
|
}
|
|
16355
16483
|
const agent2 = createAeroSession({
|
|
16356
16484
|
projectName,
|
|
@@ -16569,13 +16697,13 @@ ${lines.join("\n")}
|
|
|
16569
16697
|
agent.state.messages = result.messages;
|
|
16570
16698
|
setAeroSystemPrompt(agent, this.buildHydratedSystemPrompt(projectId, row.systemPrompt));
|
|
16571
16699
|
this.save(projectName);
|
|
16572
|
-
|
|
16700
|
+
log26.info("compaction.completed", {
|
|
16573
16701
|
projectName,
|
|
16574
16702
|
removedCount: result.removedCount,
|
|
16575
16703
|
summaryBytes: Buffer.byteLength(result.summary, "utf8")
|
|
16576
16704
|
});
|
|
16577
16705
|
} catch (err) {
|
|
16578
|
-
|
|
16706
|
+
log26.error("compaction.failed", {
|
|
16579
16707
|
projectName,
|
|
16580
16708
|
error: describeError(err)
|
|
16581
16709
|
});
|
|
@@ -16669,7 +16797,7 @@ ${lines.join("\n")}
|
|
|
16669
16797
|
modelProvider: nextProvider,
|
|
16670
16798
|
modelId: nextModelId,
|
|
16671
16799
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
16672
|
-
}).where(
|
|
16800
|
+
}).where(eq28(agentSessions.projectId, projectId)).run();
|
|
16673
16801
|
}
|
|
16674
16802
|
/** Snapshot pending work without consuming it before an atomic conversation switch. */
|
|
16675
16803
|
prepareConversationChange(projectName) {
|
|
@@ -16753,7 +16881,7 @@ ${lines.join("\n")}
|
|
|
16753
16881
|
}
|
|
16754
16882
|
trackAeroTurn(agent, { trigger: OutcomeTriggers.agent });
|
|
16755
16883
|
} catch (err) {
|
|
16756
|
-
|
|
16884
|
+
log26.error("drain.failed", {
|
|
16757
16885
|
projectName,
|
|
16758
16886
|
error: describeError(err)
|
|
16759
16887
|
});
|
|
@@ -16850,11 +16978,11 @@ ${lines.join("\n")}
|
|
|
16850
16978
|
return id;
|
|
16851
16979
|
}
|
|
16852
16980
|
tryResolveProjectId(projectName) {
|
|
16853
|
-
const row = this.opts.db.select({ id: projects.id }).from(projects).where(
|
|
16981
|
+
const row = this.opts.db.select({ id: projects.id }).from(projects).where(eq28(projects.name, projectName)).get();
|
|
16854
16982
|
return row?.id;
|
|
16855
16983
|
}
|
|
16856
16984
|
loadRow(projectId) {
|
|
16857
|
-
const row = this.opts.db.select().from(agentSessions).where(
|
|
16985
|
+
const row = this.opts.db.select().from(agentSessions).where(eq28(agentSessions.projectId, projectId)).get();
|
|
16858
16986
|
return row ?? null;
|
|
16859
16987
|
}
|
|
16860
16988
|
insertRow(params) {
|
|
@@ -16874,7 +17002,7 @@ ${lines.join("\n")}
|
|
|
16874
17002
|
}
|
|
16875
17003
|
updateRow(projectId, patch) {
|
|
16876
17004
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
16877
|
-
this.opts.db.update(agentSessions).set({ ...patch, updatedAt: now }).where(
|
|
17005
|
+
this.opts.db.update(agentSessions).set({ ...patch, updatedAt: now }).where(eq28(agentSessions.projectId, projectId)).run();
|
|
16878
17006
|
}
|
|
16879
17007
|
/**
|
|
16880
17008
|
* Store a refreshed prompt snapshot without touching `updatedAt`, which the
|
|
@@ -16883,13 +17011,13 @@ ${lines.join("\n")}
|
|
|
16883
17011
|
* recently used right after an upgrade.
|
|
16884
17012
|
*/
|
|
16885
17013
|
persistPromptSnapshot(projectId, systemPrompt) {
|
|
16886
|
-
this.opts.db.update(agentSessions).set({ systemPrompt }).where(
|
|
17014
|
+
this.opts.db.update(agentSessions).set({ systemPrompt }).where(eq28(agentSessions.projectId, projectId)).run();
|
|
16887
17015
|
}
|
|
16888
17016
|
};
|
|
16889
17017
|
|
|
16890
17018
|
// src/agent/viewer-sessions.ts
|
|
16891
17019
|
import crypto24 from "crypto";
|
|
16892
|
-
import { and as
|
|
17020
|
+
import { and as and24, eq as eq29, isNotNull as isNotNull4, like as like2 } from "drizzle-orm";
|
|
16893
17021
|
var AERO_VIEWER_EXCLUDED_MCP_TOOLS = /* @__PURE__ */ new Set([
|
|
16894
17022
|
CanonryMcpToolNames.canonry_ads_account,
|
|
16895
17023
|
CanonryMcpToolNames.canonry_ads_geo_search,
|
|
@@ -17066,12 +17194,12 @@ var ViewerAeroSessions = class {
|
|
|
17066
17194
|
*/
|
|
17067
17195
|
removeKey(id) {
|
|
17068
17196
|
try {
|
|
17069
|
-
this.opts.db.delete(apiKeys).where(
|
|
17197
|
+
this.opts.db.delete(apiKeys).where(eq29(apiKeys.id, id)).run();
|
|
17070
17198
|
} catch {
|
|
17071
17199
|
try {
|
|
17072
17200
|
this.opts.db.transaction((tx) => {
|
|
17073
|
-
tx.delete(dashboardSessions).where(
|
|
17074
|
-
tx.update(apiKeys).set({ revokedAt: new Date(this.now()).toISOString() }).where(
|
|
17201
|
+
tx.delete(dashboardSessions).where(eq29(dashboardSessions.apiKeyId, id)).run();
|
|
17202
|
+
tx.update(apiKeys).set({ revokedAt: new Date(this.now()).toISOString() }).where(eq29(apiKeys.id, id)).run();
|
|
17075
17203
|
});
|
|
17076
17204
|
} catch {
|
|
17077
17205
|
}
|
|
@@ -17084,7 +17212,7 @@ var ViewerAeroSessions = class {
|
|
|
17084
17212
|
*/
|
|
17085
17213
|
revokeOrphanedKeys() {
|
|
17086
17214
|
try {
|
|
17087
|
-
this.opts.db.delete(apiKeys).where(
|
|
17215
|
+
this.opts.db.delete(apiKeys).where(and24(like2(apiKeys.name, `${VIEWER_AERO_KEY_PREFIX}%`), isNotNull4(apiKeys.delegatedUserId))).run();
|
|
17088
17216
|
} catch {
|
|
17089
17217
|
}
|
|
17090
17218
|
}
|
|
@@ -17101,7 +17229,7 @@ var ViewerAeroSessions = class {
|
|
|
17101
17229
|
// src/mcp-http.ts
|
|
17102
17230
|
import crypto25 from "crypto";
|
|
17103
17231
|
import { isIP } from "net";
|
|
17104
|
-
import { and as
|
|
17232
|
+
import { and as and25, eq as eq30, isNull as isNull6 } from "drizzle-orm";
|
|
17105
17233
|
import { NodeStreamableHTTPServerTransport, toNodeHandler, toWebRequest } from "@modelcontextprotocol/node";
|
|
17106
17234
|
import { McpServer, createMcpHandler, isInitializeRequest, isLegacyRequest } from "@modelcontextprotocol/server";
|
|
17107
17235
|
var SESSION_IDLE_MS = 10 * 60 * 1e3;
|
|
@@ -17207,12 +17335,12 @@ function registerMcpHttpRoutes(scope, opts) {
|
|
|
17207
17335
|
try {
|
|
17208
17336
|
withoutLockWait(() => {
|
|
17209
17337
|
opts.db.transaction((tx) => {
|
|
17210
|
-
tx.delete(dashboardSessions).where(
|
|
17211
|
-
tx.update(apiKeys).set({ revokedAt: new Date(now()).toISOString() }).where(
|
|
17338
|
+
tx.delete(dashboardSessions).where(eq30(dashboardSessions.apiKeyId, id)).run();
|
|
17339
|
+
tx.update(apiKeys).set({ revokedAt: new Date(now()).toISOString() }).where(and25(eq30(apiKeys.id, id), isNull6(apiKeys.revokedAt))).run();
|
|
17212
17340
|
});
|
|
17213
17341
|
if (remove) {
|
|
17214
17342
|
try {
|
|
17215
|
-
opts.db.delete(apiKeys).where(
|
|
17343
|
+
opts.db.delete(apiKeys).where(eq30(apiKeys.id, id)).run();
|
|
17216
17344
|
} catch (error) {
|
|
17217
17345
|
scope.log.warn({ err: error, keyId: id }, "Retained revoked MCP request key after deletion failed");
|
|
17218
17346
|
}
|
|
@@ -17263,14 +17391,14 @@ function registerMcpHttpRoutes(scope, opts) {
|
|
|
17263
17391
|
withoutLockWait(() => {
|
|
17264
17392
|
const cutoff = now() - SESSION_IDLE_MS;
|
|
17265
17393
|
for (const session of legacyServers) {
|
|
17266
|
-
const key = session.sessionKeyId ? opts.db.select({ revokedAt: apiKeys.revokedAt }).from(apiKeys).where(
|
|
17394
|
+
const key = session.sessionKeyId ? opts.db.select({ revokedAt: apiKeys.revokedAt }).from(apiKeys).where(eq30(apiKeys.id, session.sessionKeyId)).get() : void 0;
|
|
17267
17395
|
if (session.lastSeenAt < cutoff || session.expiresAt !== void 0 && session.expiresAt <= now() || session.sessionKeyId && (!key || key.revokedAt)) {
|
|
17268
17396
|
void session.close().catch((error) => scope.log.error({ err: error }, "Failed to close expired MCP session"));
|
|
17269
17397
|
}
|
|
17270
17398
|
}
|
|
17271
17399
|
for (const exchange of modernExchanges) {
|
|
17272
17400
|
if (!exchange.sessionKey) continue;
|
|
17273
|
-
const key = opts.db.select({ revokedAt: apiKeys.revokedAt }).from(apiKeys).where(
|
|
17401
|
+
const key = opts.db.select({ revokedAt: apiKeys.revokedAt }).from(apiKeys).where(eq30(apiKeys.id, exchange.sessionKey.id)).get();
|
|
17274
17402
|
if (!key || key.revokedAt || exchange.sessionKey.expiresAt <= now()) {
|
|
17275
17403
|
void exchange.close().catch((error) => scope.log.error({ err: error }, "Failed to close expired MCP request"));
|
|
17276
17404
|
}
|
|
@@ -17520,7 +17648,7 @@ function registerMcpHttpRoutes(scope, opts) {
|
|
|
17520
17648
|
}
|
|
17521
17649
|
|
|
17522
17650
|
// src/agent/agent-routes.ts
|
|
17523
|
-
import { eq as
|
|
17651
|
+
import { eq as eq31 } from "drizzle-orm";
|
|
17524
17652
|
function viewerAeroCaller(request, opts) {
|
|
17525
17653
|
if (!opts.viewerSessions) return null;
|
|
17526
17654
|
const principal = request.principal;
|
|
@@ -17560,7 +17688,7 @@ function turnAttribution(request) {
|
|
|
17560
17688
|
};
|
|
17561
17689
|
}
|
|
17562
17690
|
function resolveProject(db, name) {
|
|
17563
|
-
const row = db.select({ id: projects.id, name: projects.name }).from(projects).where(
|
|
17691
|
+
const row = db.select({ id: projects.id, name: projects.name }).from(projects).where(eq31(projects.name, name)).get();
|
|
17564
17692
|
if (!row) throw notFound("project", name);
|
|
17565
17693
|
return row;
|
|
17566
17694
|
}
|
|
@@ -17679,7 +17807,7 @@ function registerAgentRoutes(app, opts) {
|
|
|
17679
17807
|
}
|
|
17680
17808
|
requireInstanceAdministrator(request);
|
|
17681
17809
|
const project = resolveProject(opts.db, request.params.name);
|
|
17682
|
-
const row = opts.db.select().from(agentSessions).where(
|
|
17810
|
+
const row = opts.db.select().from(agentSessions).where(eq31(agentSessions.projectId, project.id)).get();
|
|
17683
17811
|
const nextTurn = opts.sessionRegistry.nextTurnModel(project.name);
|
|
17684
17812
|
if (!row) {
|
|
17685
17813
|
return { conversationId: null, messages: [], modelProvider: null, modelId: null, nextTurn, updatedAt: null };
|
|
@@ -17721,7 +17849,7 @@ function registerAgentRoutes(app, opts) {
|
|
|
17721
17849
|
const project = resolveProject(opts.db, request.params.name);
|
|
17722
17850
|
if (opts.sessionRegistry.isBusy(project.name)) throw agentBusy(project.name);
|
|
17723
17851
|
opts.sessionRegistry.reset(project.name);
|
|
17724
|
-
opts.db.update(agentSessions).set({ messages: "[]", followUpQueue: "[]", updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(
|
|
17852
|
+
opts.db.update(agentSessions).set({ messages: "[]", followUpQueue: "[]", updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq31(agentSessions.projectId, project.id)).run();
|
|
17725
17853
|
return { status: "reset" };
|
|
17726
17854
|
}
|
|
17727
17855
|
);
|
|
@@ -17745,7 +17873,7 @@ function registerAgentRoutes(app, opts) {
|
|
|
17745
17873
|
return streamViewerTurn(opts, project, viewerId, body, reply, turnAttribution(request));
|
|
17746
17874
|
}
|
|
17747
17875
|
if (body.conversationId !== void 0) {
|
|
17748
|
-
const current = opts.db.select({ id: agentSessions.id }).from(agentSessions).where(
|
|
17876
|
+
const current = opts.db.select({ id: agentSessions.id }).from(agentSessions).where(eq31(agentSessions.projectId, project.id)).get();
|
|
17749
17877
|
if ((current?.id ?? null) !== body.conversationId) throw validationError("The active conversation changed. Reload the conversation before sending.");
|
|
17750
17878
|
}
|
|
17751
17879
|
const promptText = body.prompt;
|
|
@@ -18156,7 +18284,7 @@ function stripHtml(html) {
|
|
|
18156
18284
|
}
|
|
18157
18285
|
|
|
18158
18286
|
// src/snapshot-service.ts
|
|
18159
|
-
var
|
|
18287
|
+
var log27 = createLogger("Snapshot");
|
|
18160
18288
|
var ANALYSIS_PROVIDER_PRIORITY = ["openai", "claude", "gemini", "perplexity", "muse", "local"];
|
|
18161
18289
|
var SNAPSHOT_QUERY_COUNT = 6;
|
|
18162
18290
|
var SnapshotService = class {
|
|
@@ -18258,7 +18386,7 @@ var SnapshotService = class {
|
|
|
18258
18386
|
return mapAuditReport(report);
|
|
18259
18387
|
} catch (err) {
|
|
18260
18388
|
const message = describeError(err);
|
|
18261
|
-
|
|
18389
|
+
log27.warn("audit.failed", { homepageUrl, error: message });
|
|
18262
18390
|
return {
|
|
18263
18391
|
url: homepageUrl,
|
|
18264
18392
|
finalUrl: homepageUrl,
|
|
@@ -18287,7 +18415,7 @@ var SnapshotService = class {
|
|
|
18287
18415
|
queries: parsedQueries
|
|
18288
18416
|
};
|
|
18289
18417
|
} catch (err) {
|
|
18290
|
-
|
|
18418
|
+
log27.warn("profile.generation-failed", {
|
|
18291
18419
|
domain: ctx.domain,
|
|
18292
18420
|
provider: ctx.analysisProvider.adapter.name,
|
|
18293
18421
|
error: describeError(err)
|
|
@@ -18435,7 +18563,7 @@ var SnapshotService = class {
|
|
|
18435
18563
|
recommendedActions: uniqueStrings2(parsed.recommendedActions ?? []).slice(0, 4)
|
|
18436
18564
|
};
|
|
18437
18565
|
} catch (err) {
|
|
18438
|
-
|
|
18566
|
+
log27.warn("response.analysis-failed", {
|
|
18439
18567
|
provider: ctx.analysisProvider.adapter.name,
|
|
18440
18568
|
error: describeError(err)
|
|
18441
18569
|
});
|
|
@@ -18705,7 +18833,7 @@ function clipText(value, length) {
|
|
|
18705
18833
|
}
|
|
18706
18834
|
|
|
18707
18835
|
// src/research-runner.ts
|
|
18708
|
-
import { and as
|
|
18836
|
+
import { and as and26, eq as eq32, inArray as inArray13, sql as sql12 } from "drizzle-orm";
|
|
18709
18837
|
var unfinishedResearchQueryStatuses = [ResearchQueryStatuses.queued, ResearchQueryStatuses.running];
|
|
18710
18838
|
function finalResearchRunStatus(completed, failed, waiting) {
|
|
18711
18839
|
if (waiting > 0) return ResearchRunStatuses["waiting-upstream"];
|
|
@@ -18713,13 +18841,13 @@ function finalResearchRunStatus(completed, failed, waiting) {
|
|
|
18713
18841
|
return completed > 0 ? ResearchRunStatuses.partial : ResearchRunStatuses.failed;
|
|
18714
18842
|
}
|
|
18715
18843
|
async function executeResearchRun(db, registry, runId, projectId, options = {}) {
|
|
18716
|
-
const run = db.select().from(researchRuns).where(
|
|
18844
|
+
const run = db.select().from(researchRuns).where(and26(eq32(researchRuns.id, runId), eq32(researchRuns.projectId, projectId))).get();
|
|
18717
18845
|
if (!run || run.status !== ResearchRunStatuses.queued) return;
|
|
18718
|
-
const project = db.select().from(projects).where(
|
|
18846
|
+
const project = db.select().from(projects).where(eq32(projects.id, projectId)).get();
|
|
18719
18847
|
if (!project) return;
|
|
18720
18848
|
const provider = registry.get(run.provider);
|
|
18721
18849
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
18722
|
-
const claim = db.update(researchRuns).set({ status: ResearchRunStatuses.running, startedAt: now }).where(
|
|
18850
|
+
const claim = db.update(researchRuns).set({ status: ResearchRunStatuses.running, startedAt: now }).where(and26(eq32(researchRuns.id, runId), eq32(researchRuns.status, ResearchRunStatuses.queued))).run();
|
|
18723
18851
|
if (claim.changes !== 1) return;
|
|
18724
18852
|
const elapsed = startOutcomeTimer();
|
|
18725
18853
|
const requested = currentOutcomeOrigin();
|
|
@@ -18753,8 +18881,8 @@ async function executeResearchRun(db, registry, runId, projectId, options = {})
|
|
|
18753
18881
|
}
|
|
18754
18882
|
reserved = run.totalQueries;
|
|
18755
18883
|
reservation = { scope, period };
|
|
18756
|
-
const rows = db.select().from(researchRunQueries).where(
|
|
18757
|
-
const competitorRows = db.select(competitorIdentityColumns).from(competitors).where(
|
|
18884
|
+
const rows = db.select().from(researchRunQueries).where(eq32(researchRunQueries.researchRunId, runId)).orderBy(researchRunQueries.position).all();
|
|
18885
|
+
const competitorRows = db.select(competitorIdentityColumns).from(competitors).where(eq32(competitors.projectId, projectId)).orderBy(competitors.domain).all();
|
|
18758
18886
|
const competitorDomains = competitorRows.map((row) => row.domain);
|
|
18759
18887
|
const competitorAliases = new Map(competitorRows.map((row) => [row.domain, competitorIdentityAliases(row)]).filter(([, aliases]) => aliases.length > 0));
|
|
18760
18888
|
const domains = effectiveDomains(project);
|
|
@@ -18764,7 +18892,7 @@ async function executeResearchRun(db, registry, runId, projectId, options = {})
|
|
|
18764
18892
|
await mapWithConcurrency(rows, Math.max(1, provider.config.quotaPolicy.maxConcurrency), async (row) => {
|
|
18765
18893
|
try {
|
|
18766
18894
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
18767
|
-
db.update(researchRunQueries).set({ status: ResearchQueryStatuses.running, startedAt }).where(
|
|
18895
|
+
db.update(researchRunQueries).set({ status: ResearchQueryStatuses.running, startedAt }).where(and26(eq32(researchRunQueries.id, row.id), eq32(researchRunQueries.status, ResearchQueryStatuses.queued))).run();
|
|
18768
18896
|
const raw = await gate.run(async () => {
|
|
18769
18897
|
dispatched++;
|
|
18770
18898
|
return provider.adapter.executeTrackedQuery({ query: row.queryText, canonicalDomains: domains, competitorDomains, ...run.location ? { location: run.location } : {} }, config);
|
|
@@ -18795,7 +18923,7 @@ async function executeResearchRun(db, registry, runId, projectId, options = {})
|
|
|
18795
18923
|
rawResponse: raw.rawResponse,
|
|
18796
18924
|
answerEvidence: raw.evidence ?? null,
|
|
18797
18925
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
18798
|
-
}).where(
|
|
18926
|
+
}).where(and26(eq32(researchRunQueries.id, row.id), eq32(researchRunQueries.status, ResearchQueryStatuses.running))).run();
|
|
18799
18927
|
if (completed.changes === 1) incrementResearchProgress(db, runId, "completedQueries");
|
|
18800
18928
|
} catch (error) {
|
|
18801
18929
|
if (!(error instanceof IncompleteAnswerError && error.resumable)) queryFailure ??= error;
|
|
@@ -18846,11 +18974,11 @@ function researchOutcomeStatus(status) {
|
|
|
18846
18974
|
}
|
|
18847
18975
|
}
|
|
18848
18976
|
function incrementResearchProgress(db, runId, column) {
|
|
18849
|
-
db.update(researchRuns).set({ [column]:
|
|
18977
|
+
db.update(researchRuns).set({ [column]: sql12`${researchRuns[column]} + 1` }).where(eq32(researchRuns.id, runId)).run();
|
|
18850
18978
|
}
|
|
18851
18979
|
function markResearchQueryFailed(db, runId, queryId, error) {
|
|
18852
18980
|
const message = describeError(error);
|
|
18853
|
-
const unfinished =
|
|
18981
|
+
const unfinished = and26(eq32(researchRunQueries.id, queryId), inArray13(researchRunQueries.status, unfinishedResearchQueryStatuses));
|
|
18854
18982
|
if (error instanceof IncompleteAnswerError && error.resumable) {
|
|
18855
18983
|
db.update(researchRunQueries).set({
|
|
18856
18984
|
status: ResearchQueryStatuses["waiting-upstream"],
|
|
@@ -18866,10 +18994,10 @@ function markResearchQueryFailed(db, runId, queryId, error) {
|
|
|
18866
18994
|
if (failed.changes === 1) incrementResearchProgress(db, runId, "failedQueries");
|
|
18867
18995
|
}
|
|
18868
18996
|
function markUnfinishedResearchQueriesFailed(db, runId, error) {
|
|
18869
|
-
db.update(researchRunQueries).set({ status: ResearchQueryStatuses.failed, error, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(
|
|
18997
|
+
db.update(researchRunQueries).set({ status: ResearchQueryStatuses.failed, error, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(and26(eq32(researchRunQueries.researchRunId, runId), inArray13(researchRunQueries.status, unfinishedResearchQueryStatuses))).run();
|
|
18870
18998
|
}
|
|
18871
18999
|
function finalizeResearchRun(db, runId, fatalError) {
|
|
18872
|
-
const rows = db.select({ status: researchRunQueries.status }).from(researchRunQueries).where(
|
|
19000
|
+
const rows = db.select({ status: researchRunQueries.status }).from(researchRunQueries).where(eq32(researchRunQueries.researchRunId, runId)).all();
|
|
18873
19001
|
const completed = rows.filter((row) => row.status === ResearchQueryStatuses.completed).length;
|
|
18874
19002
|
const failed = rows.filter((row) => row.status === ResearchQueryStatuses.failed).length;
|
|
18875
19003
|
const waiting = rows.filter((row) => row.status === ResearchQueryStatuses["waiting-upstream"]).length;
|
|
@@ -18881,12 +19009,12 @@ function finalizeResearchRun(db, runId, fatalError) {
|
|
|
18881
19009
|
error: fatalError ?? (failed === rows.length ? "Every research query failed." : null),
|
|
18882
19010
|
// A run waiting on upstream jobs has not finished.
|
|
18883
19011
|
finishedAt: waiting > 0 ? null : (/* @__PURE__ */ new Date()).toISOString()
|
|
18884
|
-
}).where(
|
|
19012
|
+
}).where(eq32(researchRuns.id, runId)).run();
|
|
18885
19013
|
return { status, total: rows.length, completed, failed };
|
|
18886
19014
|
}
|
|
18887
19015
|
|
|
18888
19016
|
// src/google-marketing-sync.ts
|
|
18889
|
-
import { and as
|
|
19017
|
+
import { and as and27, eq as eq33, isNull as isNull7, sql as sql13 } from "drizzle-orm";
|
|
18890
19018
|
var RUNTIME_FAILURE_REASONS = {
|
|
18891
19019
|
INVALID_INPUT: OutcomeReasonCodes.VALIDATION,
|
|
18892
19020
|
NOT_CONFIGURED: OutcomeReasonCodes.NOT_CONNECTED,
|
|
@@ -18923,10 +19051,10 @@ function assertSnapshotContext(metadata, expected) {
|
|
|
18923
19051
|
}
|
|
18924
19052
|
}
|
|
18925
19053
|
function requireSyncRun(db, runId, projectId, kind) {
|
|
18926
|
-
const run = db.select().from(runs).where(
|
|
18927
|
-
|
|
18928
|
-
|
|
18929
|
-
|
|
19054
|
+
const run = db.select().from(runs).where(and27(
|
|
19055
|
+
eq33(runs.id, runId),
|
|
19056
|
+
eq33(runs.projectId, projectId),
|
|
19057
|
+
eq33(runs.kind, kind)
|
|
18930
19058
|
)).get();
|
|
18931
19059
|
if (!run) throw withOutcomeReason(new Error(`Run ${runId} is not a ${kind} run for this project.`), OutcomeReasonCodes.NOT_FOUND);
|
|
18932
19060
|
if (run.status !== RunStatuses.queued) {
|
|
@@ -18942,19 +19070,19 @@ function markFailed(db, runId, projectId, kind, expectedStatus, error) {
|
|
|
18942
19070
|
status: RunStatuses.failed,
|
|
18943
19071
|
error: serializeRunError({ message: safeErrorMessage(error) }),
|
|
18944
19072
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
18945
|
-
}).where(
|
|
18946
|
-
|
|
18947
|
-
|
|
18948
|
-
|
|
18949
|
-
|
|
19073
|
+
}).where(and27(
|
|
19074
|
+
eq33(runs.id, runId),
|
|
19075
|
+
eq33(runs.projectId, projectId),
|
|
19076
|
+
eq33(runs.kind, kind),
|
|
19077
|
+
eq33(runs.status, expectedStatus)
|
|
18950
19078
|
)).run();
|
|
18951
19079
|
}
|
|
18952
19080
|
function claimSyncRun(db, runId, projectId, kind, startedAt) {
|
|
18953
|
-
const claim = db.update(runs).set({ status: RunStatuses.running, startedAt, error: null }).where(
|
|
18954
|
-
|
|
18955
|
-
|
|
18956
|
-
|
|
18957
|
-
|
|
19081
|
+
const claim = db.update(runs).set({ status: RunStatuses.running, startedAt, error: null }).where(and27(
|
|
19082
|
+
eq33(runs.id, runId),
|
|
19083
|
+
eq33(runs.projectId, projectId),
|
|
19084
|
+
eq33(runs.kind, kind),
|
|
19085
|
+
eq33(runs.status, RunStatuses.queued)
|
|
18958
19086
|
)).run();
|
|
18959
19087
|
return claim.changes === 1;
|
|
18960
19088
|
}
|
|
@@ -18964,9 +19092,9 @@ async function executeGoogleAdsMarketingSync(db, runtime, runId, projectId) {
|
|
|
18964
19092
|
let evidenceCollected = false;
|
|
18965
19093
|
try {
|
|
18966
19094
|
requireSyncRun(db, runId, projectId, RunKinds["google-ads-sync"]);
|
|
18967
|
-
const project = db.select().from(projects).where(
|
|
19095
|
+
const project = db.select().from(projects).where(eq33(projects.id, projectId)).get();
|
|
18968
19096
|
if (!project) throw withOutcomeReason(new Error(`Project ${projectId} was not found.`), OutcomeReasonCodes.NOT_FOUND);
|
|
18969
|
-
const connection = db.select().from(googleAdsConnections).where(
|
|
19097
|
+
const connection = db.select().from(googleAdsConnections).where(eq33(googleAdsConnections.projectId, projectId)).get();
|
|
18970
19098
|
if (!connection?.selectedCustomerId) {
|
|
18971
19099
|
throw withOutcomeReason(new Error("Google Ads needs a selected customer before it can sync."), OutcomeReasonCodes.ACCOUNT_NOT_FOUND);
|
|
18972
19100
|
}
|
|
@@ -19018,11 +19146,11 @@ async function executeGoogleAdsMarketingSync(db, runtime, runId, projectId) {
|
|
|
19018
19146
|
status: RunStatuses.completed,
|
|
19019
19147
|
finishedAt,
|
|
19020
19148
|
error: null
|
|
19021
|
-
}).where(
|
|
19022
|
-
|
|
19023
|
-
|
|
19024
|
-
|
|
19025
|
-
|
|
19149
|
+
}).where(and27(
|
|
19150
|
+
eq33(runs.id, runId),
|
|
19151
|
+
eq33(runs.projectId, projectId),
|
|
19152
|
+
eq33(runs.kind, RunKinds["google-ads-sync"]),
|
|
19153
|
+
eq33(runs.status, RunStatuses.running)
|
|
19026
19154
|
)).run();
|
|
19027
19155
|
if (terminalClaim.changes !== 1) {
|
|
19028
19156
|
throw withOutcomeReason(
|
|
@@ -19042,12 +19170,12 @@ async function executeGoogleAdsMarketingSync(db, runtime, runId, projectId) {
|
|
|
19042
19170
|
lastMetricsSnapshotAt: result.metrics?.metadata.capturedAt ?? connection.lastMetricsSnapshotAt,
|
|
19043
19171
|
lastMetricsSnapshotId: result.metrics?.metadata.id ?? connection.lastMetricsSnapshotId,
|
|
19044
19172
|
updatedAt: finishedAt
|
|
19045
|
-
}).where(
|
|
19046
|
-
|
|
19047
|
-
|
|
19048
|
-
|
|
19049
|
-
connection.selectedLoginCustomerId === null ? isNull7(googleAdsConnections.selectedLoginCustomerId) :
|
|
19050
|
-
|
|
19173
|
+
}).where(and27(
|
|
19174
|
+
eq33(googleAdsConnections.id, connection.id),
|
|
19175
|
+
eq33(googleAdsConnections.projectId, projectId),
|
|
19176
|
+
eq33(googleAdsConnections.selectedCustomerId, connection.selectedCustomerId),
|
|
19177
|
+
connection.selectedLoginCustomerId === null ? isNull7(googleAdsConnections.selectedLoginCustomerId) : eq33(googleAdsConnections.selectedLoginCustomerId, connection.selectedLoginCustomerId),
|
|
19178
|
+
eq33(googleAdsConnections.selectionGeneration, connection.selectionGeneration)
|
|
19051
19179
|
)).run();
|
|
19052
19180
|
if (connectionClaim.changes !== 1) {
|
|
19053
19181
|
throw withOutcomeReason(
|
|
@@ -19087,9 +19215,9 @@ async function executeGtmMarketingSync(db, runtime, runId, projectId) {
|
|
|
19087
19215
|
let evidenceCollected = false;
|
|
19088
19216
|
try {
|
|
19089
19217
|
requireSyncRun(db, runId, projectId, RunKinds["gtm-sync"]);
|
|
19090
|
-
const project = db.select().from(projects).where(
|
|
19218
|
+
const project = db.select().from(projects).where(eq33(projects.id, projectId)).get();
|
|
19091
19219
|
if (!project) throw withOutcomeReason(new Error(`Project ${projectId} was not found.`), OutcomeReasonCodes.NOT_FOUND);
|
|
19092
|
-
const connection = db.select().from(gtmConnections).where(
|
|
19220
|
+
const connection = db.select().from(gtmConnections).where(eq33(gtmConnections.projectId, projectId)).get();
|
|
19093
19221
|
if (!connection?.selectedAccountId || !connection.selectedContainerId) {
|
|
19094
19222
|
throw withOutcomeReason(new Error("GTM needs a selected account and container before it can sync."), OutcomeReasonCodes.ACCOUNT_NOT_FOUND);
|
|
19095
19223
|
}
|
|
@@ -19127,11 +19255,11 @@ async function executeGtmMarketingSync(db, runtime, runId, projectId) {
|
|
|
19127
19255
|
status: RunStatuses.completed,
|
|
19128
19256
|
finishedAt,
|
|
19129
19257
|
error: null
|
|
19130
|
-
}).where(
|
|
19131
|
-
|
|
19132
|
-
|
|
19133
|
-
|
|
19134
|
-
|
|
19258
|
+
}).where(and27(
|
|
19259
|
+
eq33(runs.id, runId),
|
|
19260
|
+
eq33(runs.projectId, projectId),
|
|
19261
|
+
eq33(runs.kind, RunKinds["gtm-sync"]),
|
|
19262
|
+
eq33(runs.status, RunStatuses.running)
|
|
19135
19263
|
)).run();
|
|
19136
19264
|
if (terminalClaim.changes !== 1) {
|
|
19137
19265
|
throw withOutcomeReason(
|
|
@@ -19146,19 +19274,19 @@ async function executeGtmMarketingSync(db, runtime, runId, projectId) {
|
|
|
19146
19274
|
selectedWorkspaceName: selectedWorkspace?.name ?? null,
|
|
19147
19275
|
...workspaceReplaced ? {
|
|
19148
19276
|
selectedWorkspaceId: workspaceId,
|
|
19149
|
-
selectionGeneration:
|
|
19277
|
+
selectionGeneration: sql13`${gtmConnections.selectionGeneration} + 1`
|
|
19150
19278
|
} : {},
|
|
19151
19279
|
lastValidatedAt: snapshot.metadata.capturedAt,
|
|
19152
19280
|
lastSnapshotAt: snapshot.metadata.capturedAt,
|
|
19153
19281
|
lastSnapshotId: snapshot.metadata.id,
|
|
19154
19282
|
updatedAt: finishedAt
|
|
19155
|
-
}).where(
|
|
19156
|
-
|
|
19157
|
-
|
|
19158
|
-
|
|
19159
|
-
|
|
19160
|
-
connection.selectedWorkspaceId === null ? isNull7(gtmConnections.selectedWorkspaceId) :
|
|
19161
|
-
|
|
19283
|
+
}).where(and27(
|
|
19284
|
+
eq33(gtmConnections.id, connection.id),
|
|
19285
|
+
eq33(gtmConnections.projectId, projectId),
|
|
19286
|
+
eq33(gtmConnections.selectedAccountId, connection.selectedAccountId),
|
|
19287
|
+
eq33(gtmConnections.selectedContainerId, connection.selectedContainerId),
|
|
19288
|
+
connection.selectedWorkspaceId === null ? isNull7(gtmConnections.selectedWorkspaceId) : eq33(gtmConnections.selectedWorkspaceId, connection.selectedWorkspaceId),
|
|
19289
|
+
eq33(gtmConnections.selectionGeneration, connection.selectionGeneration)
|
|
19162
19290
|
)).run();
|
|
19163
19291
|
if (connectionClaim.changes !== 1) {
|
|
19164
19292
|
throw withOutcomeReason(
|
|
@@ -19196,7 +19324,7 @@ async function executeGtmMarketingSync(db, runtime, runId, projectId) {
|
|
|
19196
19324
|
// src/server.ts
|
|
19197
19325
|
var _require = createRequire(import.meta.url);
|
|
19198
19326
|
var { version: PKG_VERSION } = _require("../package.json");
|
|
19199
|
-
var
|
|
19327
|
+
var log28 = createLogger("Server");
|
|
19200
19328
|
var runtimeStartupByServer = /* @__PURE__ */ new WeakMap();
|
|
19201
19329
|
function waitForServerRuntimeStartup(app) {
|
|
19202
19330
|
const startup = runtimeStartupByServer.get(app);
|
|
@@ -19446,7 +19574,7 @@ function applyLegacyCredentials(rows, config, persistConfigPatch = saveConfigPat
|
|
|
19446
19574
|
}
|
|
19447
19575
|
if (migratedGoogle > 0) {
|
|
19448
19576
|
persistConfigPatch({ google: config.google });
|
|
19449
|
-
|
|
19577
|
+
log28.info("credentials.migrated", { type: "google", count: migratedGoogle });
|
|
19450
19578
|
}
|
|
19451
19579
|
let migratedGa4 = 0;
|
|
19452
19580
|
for (const row of rows.ga4) {
|
|
@@ -19464,7 +19592,7 @@ function applyLegacyCredentials(rows, config, persistConfigPatch = saveConfigPat
|
|
|
19464
19592
|
}
|
|
19465
19593
|
if (migratedGa4 > 0) {
|
|
19466
19594
|
persistConfigPatch({ ga4: config.ga4 });
|
|
19467
|
-
|
|
19595
|
+
log28.info("credentials.migrated", { type: "ga4", count: migratedGa4 });
|
|
19468
19596
|
}
|
|
19469
19597
|
}
|
|
19470
19598
|
function isLoopbackPeerAddress(address) {
|
|
@@ -19747,17 +19875,17 @@ async function createServer(opts) {
|
|
|
19747
19875
|
applyLegacyCredentials(legacyRows, opts.config, persistConfigPatch);
|
|
19748
19876
|
dropLegacyCredentialColumns(opts.db);
|
|
19749
19877
|
} catch (err) {
|
|
19750
|
-
|
|
19878
|
+
log28.warn("credentials.migration.failed", {
|
|
19751
19879
|
error: describeError(err)
|
|
19752
19880
|
});
|
|
19753
19881
|
}
|
|
19754
19882
|
}
|
|
19755
|
-
|
|
19883
|
+
log28.info("providers.configured", {
|
|
19756
19884
|
providers: registeredProviderNames({ providers, cdp: opts.config.cdp })
|
|
19757
19885
|
});
|
|
19758
19886
|
registry.replace(configuredProviderEntries(opts.config, API_ADAPTERS, cdpChatgptAdapter));
|
|
19759
19887
|
for (const name of providersWithUnsupportedBatch(providers, (provider) => adapterMap[provider])) {
|
|
19760
|
-
|
|
19888
|
+
log28.warn("provider.batch.unsupported", {
|
|
19761
19889
|
provider: name,
|
|
19762
19890
|
message: `providers.${name}.batch.enabled is true, but the ${name} adapter has no batch API; its sweeps run sync.`
|
|
19763
19891
|
});
|
|
@@ -19814,7 +19942,7 @@ async function createServer(opts) {
|
|
|
19814
19942
|
async (ctx) => {
|
|
19815
19943
|
if (!sessionRegistry) return;
|
|
19816
19944
|
if (!agentProactive) return;
|
|
19817
|
-
const project = opts.db.select({ name: projects.name }).from(projects).where(
|
|
19945
|
+
const project = opts.db.select({ name: projects.name }).from(projects).where(eq34(projects.id, ctx.projectId)).get();
|
|
19818
19946
|
if (!project) return;
|
|
19819
19947
|
let content;
|
|
19820
19948
|
if (ctx.kind === RunKinds["aeo-discover-probe"]) {
|
|
@@ -20243,6 +20371,7 @@ async function createServer(opts) {
|
|
|
20243
20371
|
};
|
|
20244
20372
|
let stopSiteLiveness = null;
|
|
20245
20373
|
let stopReviewDeliveries = null;
|
|
20374
|
+
let stopUnrecognizedAgentAlerts = null;
|
|
20246
20375
|
let stopProviderBatchPoller = null;
|
|
20247
20376
|
let stopInstallState = null;
|
|
20248
20377
|
const scheduler = new Scheduler(opts.db, {
|
|
@@ -20286,7 +20415,7 @@ async function createServer(opts) {
|
|
|
20286
20415
|
void (async () => {
|
|
20287
20416
|
try {
|
|
20288
20417
|
const report = await schedulerClient.runDoctor({ project: projectName, checkIds: scheduledHealthCheckIds() });
|
|
20289
|
-
const project = opts.db.select().from(projects).where(
|
|
20418
|
+
const project = opts.db.select().from(projects).where(eq34(projects.name, projectName)).get();
|
|
20290
20419
|
if (!project) {
|
|
20291
20420
|
app.log.warn({ projectName }, "doctor schedule fired for an unknown project");
|
|
20292
20421
|
return;
|
|
@@ -20578,7 +20707,7 @@ async function createServer(opts) {
|
|
|
20578
20707
|
})();
|
|
20579
20708
|
if (opts.config.apiKey) {
|
|
20580
20709
|
const keyHash = hashApiKey(opts.config.apiKey);
|
|
20581
|
-
const existing = opts.db.select().from(apiKeys).where(
|
|
20710
|
+
const existing = opts.db.select().from(apiKeys).where(eq34(apiKeys.keyHash, keyHash)).get();
|
|
20582
20711
|
if (!existing) {
|
|
20583
20712
|
const prefix = opts.config.apiKey.slice(0, 12);
|
|
20584
20713
|
opts.db.insert(apiKeys).values({
|
|
@@ -20597,14 +20726,14 @@ async function createServer(opts) {
|
|
|
20597
20726
|
);
|
|
20598
20727
|
const currentPasswordFingerprint = () => hashSessionToken(opts.config.dashboardPasswordHash ?? "");
|
|
20599
20728
|
opts.db.delete(dashboardSessions).where(or4(
|
|
20600
|
-
|
|
20601
|
-
|
|
20729
|
+
eq34(dashboardSessions.apiKeyHash, ""),
|
|
20730
|
+
and28(
|
|
20602
20731
|
isNotNull5(dashboardSessions.passwordFingerprint),
|
|
20603
20732
|
ne4(dashboardSessions.passwordFingerprint, currentPasswordFingerprint())
|
|
20604
20733
|
)
|
|
20605
20734
|
)).run();
|
|
20606
20735
|
if (!opts.config.dashboardPasswordHash) {
|
|
20607
|
-
const lastPasswordEventAt = (action) => opts.db.select({ at: max(auditLog.createdAt) }).from(auditLog).where(
|
|
20736
|
+
const lastPasswordEventAt = (action) => opts.db.select({ at: max(auditLog.createdAt) }).from(auditLog).where(and28(eq34(auditLog.entityType, "dashboard-password"), eq34(auditLog.action, action))).get()?.at ?? null;
|
|
20608
20737
|
const createdAt = lastPasswordEventAt("dashboard-password.created");
|
|
20609
20738
|
const clearedAt = lastPasswordEventAt("dashboard-password.cleared");
|
|
20610
20739
|
if (createdAt !== null && (clearedAt === null || clearedAt < createdAt)) {
|
|
@@ -20638,23 +20767,23 @@ async function createServer(opts) {
|
|
|
20638
20767
|
currentApiKeyHash: apiKeys.keyHash,
|
|
20639
20768
|
passwordFingerprint: dashboardSessions.passwordFingerprint,
|
|
20640
20769
|
expiresAt: dashboardSessions.expiresAt
|
|
20641
|
-
}).from(dashboardSessions).innerJoin(apiKeys,
|
|
20770
|
+
}).from(dashboardSessions).innerJoin(apiKeys, eq34(apiKeys.id, dashboardSessions.apiKeyId)).where(and28(eq34(dashboardSessions.tokenHash, tokenHash), isNull8(apiKeys.revokedAt))).get();
|
|
20642
20771
|
if (!session) return null;
|
|
20643
20772
|
const expiresAt = Date.parse(session.expiresAt);
|
|
20644
20773
|
if (!Number.isFinite(expiresAt) || expiresAt <= Date.now() || session.apiKeyHash !== session.currentApiKeyHash || session.passwordFingerprint !== null && session.passwordFingerprint !== currentPasswordFingerprint()) {
|
|
20645
|
-
opts.db.delete(dashboardSessions).where(
|
|
20774
|
+
opts.db.delete(dashboardSessions).where(eq34(dashboardSessions.tokenHash, tokenHash)).run();
|
|
20646
20775
|
return null;
|
|
20647
20776
|
}
|
|
20648
20777
|
return session.apiKeyId;
|
|
20649
20778
|
};
|
|
20650
20779
|
const clearSession = (sessionId) => {
|
|
20651
20780
|
if (sessionId) {
|
|
20652
|
-
opts.db.delete(dashboardSessions).where(
|
|
20781
|
+
opts.db.delete(dashboardSessions).where(eq34(dashboardSessions.tokenHash, hashSessionToken(sessionId))).run();
|
|
20653
20782
|
}
|
|
20654
20783
|
};
|
|
20655
20784
|
const getDefaultApiKey = () => {
|
|
20656
20785
|
if (!opts.config.apiKey) return void 0;
|
|
20657
|
-
return opts.db.select().from(apiKeys).where(
|
|
20786
|
+
return opts.db.select().from(apiKeys).where(eq34(apiKeys.keyHash, hashApiKey(opts.config.apiKey))).get();
|
|
20658
20787
|
};
|
|
20659
20788
|
const setSessionCookie = (reply, sessionId) => {
|
|
20660
20789
|
reply.header(
|
|
@@ -20815,12 +20944,12 @@ async function createServer(opts) {
|
|
|
20815
20944
|
return reply.send({ authenticated: true });
|
|
20816
20945
|
}
|
|
20817
20946
|
if (apiKey) {
|
|
20818
|
-
const key = opts.db.select().from(apiKeys).where(
|
|
20947
|
+
const key = opts.db.select().from(apiKeys).where(eq34(apiKeys.keyHash, hashApiKey(apiKey))).get();
|
|
20819
20948
|
if (!key || key.revokedAt) {
|
|
20820
20949
|
const err2 = authInvalid();
|
|
20821
20950
|
return reply.status(err2.statusCode).send(err2.toJSON());
|
|
20822
20951
|
}
|
|
20823
|
-
opts.db.update(apiKeys).set({ lastUsedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(
|
|
20952
|
+
opts.db.update(apiKeys).set({ lastUsedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq34(apiKeys.id, key.id)).run();
|
|
20824
20953
|
setSessionCookie(reply, createSession(key));
|
|
20825
20954
|
return reply.send({ authenticated: true });
|
|
20826
20955
|
}
|
|
@@ -20886,7 +21015,7 @@ async function createServer(opts) {
|
|
|
20886
21015
|
const sentimentPoller = createSentimentPoller(
|
|
20887
21016
|
() => sentimentWorker.tick(),
|
|
20888
21017
|
() => {
|
|
20889
|
-
|
|
21018
|
+
log28.error("sentiment.worker-failed", { reason: "Stored sentiment worker could not finish its tick." });
|
|
20890
21019
|
}
|
|
20891
21020
|
);
|
|
20892
21021
|
const pollSentiment = sentimentPoller.poll;
|
|
@@ -20898,7 +21027,7 @@ async function createServer(opts) {
|
|
|
20898
21027
|
const recomputeDebts = /* @__PURE__ */ new Map();
|
|
20899
21028
|
let recomputeSequence = 0;
|
|
20900
21029
|
const markAnswerFieldRecompute = (projectId, full) => {
|
|
20901
|
-
opts.db.update(projects).set({ answerFieldsRecompute: full ? "full" :
|
|
21030
|
+
opts.db.update(projects).set({ answerFieldsRecompute: full ? "full" : sql14`CASE WHEN ${projects.answerFieldsRecompute} = 'full' THEN 'full' ELSE 'competitors' END` }).where(eq34(projects.id, projectId)).run();
|
|
20902
21031
|
};
|
|
20903
21032
|
const requestAnswerFieldRecompute = (projectId, projectName, scope, reason) => {
|
|
20904
21033
|
const sequence = ++recomputeSequence;
|
|
@@ -20916,10 +21045,10 @@ async function createServer(opts) {
|
|
|
20916
21045
|
if (pass.full && debt.latestFull <= sequence) debt.latestFull = 0;
|
|
20917
21046
|
try {
|
|
20918
21047
|
if (debt.latest === sequence && debt.latestFull === 0) {
|
|
20919
|
-
opts.db.update(projects).set({ answerFieldsRecompute: null }).where(
|
|
21048
|
+
opts.db.update(projects).set({ answerFieldsRecompute: null }).where(eq34(projects.id, projectId)).run();
|
|
20920
21049
|
if (recomputeDebts.get(projectId) === debt) recomputeDebts.delete(projectId);
|
|
20921
21050
|
} else if (debt.latestFull === 0) {
|
|
20922
|
-
opts.db.update(projects).set({ answerFieldsRecompute: "competitors" }).where(
|
|
21051
|
+
opts.db.update(projects).set({ answerFieldsRecompute: "competitors" }).where(and28(eq34(projects.id, projectId), eq34(projects.answerFieldsRecompute, "full"))).run();
|
|
20923
21052
|
}
|
|
20924
21053
|
} catch (err) {
|
|
20925
21054
|
app.log.error({ err, projectId }, "could not clear the owed answer field recompute");
|
|
@@ -20956,7 +21085,7 @@ async function createServer(opts) {
|
|
|
20956
21085
|
await yieldToEventLoop4();
|
|
20957
21086
|
const reason = (competitorAutoAliasReasons.get(projectId) ?? []).join(",");
|
|
20958
21087
|
competitorAutoAliasReasons.delete(projectId);
|
|
20959
|
-
const project = opts.db.select().from(projects).where(
|
|
21088
|
+
const project = opts.db.select().from(projects).where(eq34(projects.id, projectId)).get();
|
|
20960
21089
|
if (!project || project.competitorAutoAliases === "off") return;
|
|
20961
21090
|
if (project.competitorAutoAliases === "apply") {
|
|
20962
21091
|
await competitorAutoAliases.request(projectId, reason).catch(() => void 0);
|
|
@@ -21156,8 +21285,8 @@ async function createServer(opts) {
|
|
|
21156
21285
|
getAgentPin: () => describeAgentPin(opts.config),
|
|
21157
21286
|
getGoogleMarketingDoctorInput: (ctx) => {
|
|
21158
21287
|
if (!ctx.project) return null;
|
|
21159
|
-
const googleAds = opts.db.select().from(googleAdsConnections).where(
|
|
21160
|
-
const gtm = opts.db.select().from(gtmConnections).where(
|
|
21288
|
+
const googleAds = opts.db.select().from(googleAdsConnections).where(eq34(googleAdsConnections.projectId, ctx.project.id)).get();
|
|
21289
|
+
const gtm = opts.db.select().from(gtmConnections).where(eq34(gtmConnections.projectId, ctx.project.id)).get();
|
|
21161
21290
|
const googleAdsCredential = getGoogleAdsConnection(opts.config, ctx.project.id);
|
|
21162
21291
|
const gtmCredential = getGtmConnection(opts.config, ctx.project.id);
|
|
21163
21292
|
const googleAdsConnected = Boolean(googleAdsCredential?.accessToken);
|
|
@@ -21407,7 +21536,7 @@ async function createServer(opts) {
|
|
|
21407
21536
|
...inspectOpts,
|
|
21408
21537
|
config: opts.config
|
|
21409
21538
|
}).then(() => {
|
|
21410
|
-
const finished = opts.db.select({ status: runs.status }).from(runs).where(
|
|
21539
|
+
const finished = opts.db.select({ status: runs.status }).from(runs).where(eq34(runs.id, runId)).get();
|
|
21411
21540
|
if (finished?.status === RunStatuses.completed || finished?.status === RunStatuses.partial) {
|
|
21412
21541
|
return maybeRefreshGscCoverage(
|
|
21413
21542
|
opts.db,
|
|
@@ -21897,6 +22026,7 @@ async function createServer(opts) {
|
|
|
21897
22026
|
notify: (projectId, result) => notifier.onSiteLivenessChecked(projectId, result)
|
|
21898
22027
|
});
|
|
21899
22028
|
stopReviewDeliveries = startReviewDeliveryLoop(() => notifier.drainDueReviewDeliveries());
|
|
22029
|
+
stopUnrecognizedAgentAlerts = startUnrecognizedAgentAlertLoop(() => notifier.dispatchUnrecognizedAgentAlerts());
|
|
21900
22030
|
stopProviderBatchPoller = startProviderBatchPoller({ db: opts.db, registry, runner: jobRunner });
|
|
21901
22031
|
if (!embed.enabled) {
|
|
21902
22032
|
stopInstallState = startInstallStateTelemetry({
|
|
@@ -21906,7 +22036,7 @@ async function createServer(opts) {
|
|
|
21906
22036
|
isBacklinksInstalled: () => isDuckdbInstalled()
|
|
21907
22037
|
});
|
|
21908
22038
|
}
|
|
21909
|
-
for (const run of opts.db.select({ id: researchRuns.id, projectId: researchRuns.projectId }).from(researchRuns).where(
|
|
22039
|
+
for (const run of opts.db.select({ id: researchRuns.id, projectId: researchRuns.projectId }).from(researchRuns).where(eq34(researchRuns.status, ResearchRunStatuses.queued)).all()) {
|
|
21910
22040
|
dispatchResearchRun(run.id, run.projectId, OutcomeTriggers.startup);
|
|
21911
22041
|
}
|
|
21912
22042
|
runtimeStartupSettled = true;
|
|
@@ -21923,6 +22053,8 @@ async function createServer(opts) {
|
|
|
21923
22053
|
stopSiteLiveness = null;
|
|
21924
22054
|
stopReviewDeliveries?.();
|
|
21925
22055
|
stopReviewDeliveries = null;
|
|
22056
|
+
stopUnrecognizedAgentAlerts?.();
|
|
22057
|
+
stopUnrecognizedAgentAlerts = null;
|
|
21926
22058
|
stopProviderBatchPoller?.();
|
|
21927
22059
|
stopProviderBatchPoller = null;
|
|
21928
22060
|
stopInstallState?.();
|