thumbgate 1.27.18 → 1.27.20
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/.claude/commands/dashboard.md +15 -0
- package/.claude/commands/thumbgate-blocked.md +27 -0
- package/.claude/commands/thumbgate-dashboard.md +15 -0
- package/.claude/commands/thumbgate-doctor.md +30 -0
- package/.claude/commands/thumbgate-guard.md +36 -0
- package/.claude/commands/thumbgate-protect.md +30 -0
- package/.claude/commands/thumbgate-rules.md +30 -0
- package/.claude-plugin/plugin.json +4 -3
- package/.well-known/agentic-verify.txt +1 -0
- package/.well-known/llms.txt +33 -12
- package/.well-known/mcp/server-card.json +8 -8
- package/README.md +246 -30
- package/adapters/claude/.mcp.json +2 -2
- package/adapters/codex/config.toml +2 -2
- package/adapters/gcp/dfcx-webhook-gate.js +295 -0
- package/adapters/gemini/function-declarations.json +1 -0
- package/adapters/letta/README.md +41 -0
- package/adapters/letta/thumbgate-letta-adapter.js +133 -0
- package/adapters/mcp/server-stdio.js +263 -11
- package/adapters/opencode/opencode.json +1 -1
- package/adapters/policy-engine/ethicore-guardian-client.js +68 -0
- package/adapters/policy-engine/thumbgate-policy-engine-adapter.js +260 -0
- package/bench/observability-eval-suite.json +26 -0
- package/bench/thumbgate-bench.json +2 -2
- package/bin/cli.js +1456 -122
- package/bin/dashboard-cli.js +7 -0
- package/bin/postinstall.js +11 -27
- package/commands/dashboard.md +15 -0
- package/commands/thumbgate-dashboard.md +15 -0
- package/config/gate-classifier-routing.json +98 -0
- package/config/gate-templates.json +216 -0
- package/config/gates/claim-verification.json +12 -0
- package/config/gates/default.json +31 -2
- package/config/github-about.json +2 -2
- package/config/mcp-allowlists.json +23 -13
- package/config/merge-quality-checks.json +0 -1
- package/config/model-candidates.json +121 -6
- package/config/post-deploy-marketing-pages.json +80 -0
- package/config/tessl-tiles.json +1 -3
- package/openapi/openapi.yaml +12 -0
- package/package.json +225 -100
- package/public/about.html +162 -0
- package/public/agent-manager.html +179 -0
- package/public/agents-cost-savings.html +153 -0
- package/public/ai-malpractice-prevention.html +818 -0
- package/public/assets/brand/github-social-preview.png +0 -0
- package/public/assets/brand/thumbgate-icon-512.png +0 -0
- package/public/assets/brand/thumbgate-icon-pro-512.png +0 -0
- package/public/assets/brand/thumbgate-icon-team-512.png +0 -0
- package/public/assets/brand/thumbgate-logo-1200x360.png +0 -0
- package/public/assets/brand/thumbgate-logo-transparent.svg +28 -0
- package/public/assets/brand/thumbgate-mark-inline-v3.svg +18 -0
- package/public/assets/brand/thumbgate-mark-pro.svg +23 -0
- package/public/assets/brand/thumbgate-mark-team.svg +26 -0
- package/public/assets/brand/thumbgate-mark.svg +21 -0
- package/public/assets/brand/thumbgate-wordmark.svg +20 -0
- package/public/assets/claude-thumbgate-statusbar.svg +8 -0
- package/public/assets/codex-thumbgate-statusbar-test.svg +9 -0
- package/public/assets/legal-intake-control-flow.svg +66 -0
- package/public/blog.html +4 -4
- package/public/brand/thumbgate-mark.svg +19 -0
- package/public/brand/thumbgate-og.svg +16 -0
- package/public/chatgpt-app.html +330 -0
- package/public/codex-enterprise.html +123 -0
- package/public/codex-plugin.html +72 -20
- package/public/compare.html +31 -8
- package/public/dashboard.html +930 -166
- package/public/diagnostic.html +345 -0
- package/public/federal.html +2 -2
- package/public/guide.html +33 -13
- package/public/index.html +469 -111
- package/public/install.html +193 -0
- package/public/js/buyer-intent.js +672 -0
- package/public/learn.html +183 -18
- package/public/lessons.html +168 -10
- package/public/numbers.html +7 -7
- package/public/pricing.html +399 -0
- package/public/pro.html +34 -11
- package/scripts/action-receipts.js +324 -0
- package/scripts/activation-quickstart.js +187 -0
- package/scripts/agent-memory-lifecycle.js +211 -0
- package/scripts/agent-operations-planner.js +621 -0
- package/scripts/agent-readiness.js +20 -3
- package/scripts/agent-reward-model.js +53 -1
- package/scripts/ai-component-inventory.js +367 -0
- package/scripts/async-eval-observability.js +236 -0
- package/scripts/audit.js +65 -0
- package/scripts/auto-promote-gates.js +82 -10
- package/scripts/auto-wire-hooks.js +14 -0
- package/scripts/aws-blocks-guardrails.js +272 -0
- package/scripts/billing.js +93 -1
- package/scripts/bot-detection.js +61 -3
- package/scripts/build-metadata.js +50 -10
- package/scripts/classifier-routing.js +130 -0
- package/scripts/cli-feedback.js +4 -2
- package/scripts/cli-schema.js +97 -0
- package/scripts/cli-telemetry.js +6 -1
- package/scripts/commercial-offer.js +82 -2
- package/scripts/context-manager.js +74 -6
- package/scripts/dashboard-chat.js +332 -0
- package/scripts/dashboard.js +68 -2
- package/scripts/export-databricks-bundle.js +5 -1
- package/scripts/export-dpo-pairs.js +7 -2
- package/scripts/feedback-aggregate.js +281 -0
- package/scripts/feedback-loop.js +123 -1
- package/scripts/feedback-quality.js +87 -0
- package/scripts/feedback-sanitizer.js +105 -0
- package/scripts/filesystem-search.js +35 -10
- package/scripts/gate-stats.js +89 -0
- package/scripts/gates-engine.js +1176 -85
- package/scripts/gemini-embedding-policy.js +2 -1
- package/scripts/hook-runtime.js +20 -14
- package/scripts/hook-stop-anti-claim.js +301 -0
- package/scripts/hook-thumbgate-cache-updater.js +18 -2
- package/scripts/hybrid-feedback-context.js +142 -7
- package/scripts/install-shim.js +87 -0
- package/scripts/lesson-inference.js +8 -3
- package/scripts/lesson-search.js +17 -1
- package/scripts/license.js +10 -10
- package/scripts/llm-client.js +169 -4
- package/scripts/local-model-profile.js +15 -8
- package/scripts/mcp-config.js +7 -1
- package/scripts/mcp-oauth.js +293 -0
- package/scripts/memory-scope-readiness.js +159 -0
- package/scripts/meta-agent-loop.js +36 -0
- package/scripts/noop-detect.js +285 -0
- package/scripts/operational-integrity.js +39 -5
- package/scripts/oss-pr-opportunity-scout.js +35 -5
- package/scripts/parallel-workflow-orchestrator.js +293 -0
- package/scripts/plan-gate.js +243 -0
- package/scripts/plausible-domain-config.js +99 -0
- package/scripts/plausible-server-events.js +9 -6
- package/scripts/pro-local-dashboard.js +4 -4
- package/scripts/proxy-pointer-rag-guardrails.js +42 -1
- package/scripts/published-cli.js +0 -8
- package/scripts/qa-scenario-planner.js +136 -0
- package/scripts/rate-limiter.js +64 -13
- package/scripts/repeat-metric.js +137 -0
- package/scripts/secret-fixture-tokens.js +61 -0
- package/scripts/secret-redaction.js +166 -0
- package/scripts/secret-scanner.js +44 -5
- package/scripts/security-scanner.js +260 -10
- package/scripts/self-distill-agent.js +3 -1
- package/scripts/self-harness-optimizer.js +141 -0
- package/scripts/self-healing-check.js +193 -0
- package/scripts/self-protection.js +90 -0
- package/scripts/seo-gsd.js +916 -7
- package/scripts/silent-failure-cluster.js +531 -0
- package/scripts/statusline-cache-path.js +17 -2
- package/scripts/statusline-cache-read.js +57 -0
- package/scripts/statusline-local-stats.js +9 -1
- package/scripts/statusline-meta.js +28 -2
- package/scripts/statusline.sh +20 -4
- package/scripts/sync-telemetry-from-prod.js +374 -0
- package/scripts/telemetry-analytics.js +357 -0
- package/scripts/thompson-sampling.js +31 -10
- package/scripts/thumbgate-bench.js +16 -1
- package/scripts/thumbgate-search.js +85 -19
- package/scripts/tool-contract-validator.js +76 -0
- package/scripts/tool-registry.js +169 -1
- package/scripts/trajectory-scorer.js +63 -0
- package/scripts/vector-store.js +45 -0
- package/scripts/verify-marketing-pages-deployed.js +212 -0
- package/scripts/visitor-journey.js +172 -0
- package/scripts/workflow-sentinel.js +286 -53
- package/scripts/workspace-evolver.js +62 -2
- package/src/api/server.js +2683 -319
- package/.claude-plugin/marketplace.json +0 -85
- package/adapters/chatgpt/openapi.yaml +0 -1695
- package/scripts/bot-detector.js +0 -50
|
@@ -75,6 +75,13 @@ function normalizeInteger(value) {
|
|
|
75
75
|
return Number.isFinite(parsed) ? Math.trunc(parsed) : null;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
function normalizeRate(value) {
|
|
79
|
+
if (value === undefined || value === null || value === '') return null;
|
|
80
|
+
const parsed = Number(value);
|
|
81
|
+
if (!Number.isFinite(parsed)) return null;
|
|
82
|
+
return Math.max(0, Math.min(parsed, 1));
|
|
83
|
+
}
|
|
84
|
+
|
|
78
85
|
function safeRate(num, den) {
|
|
79
86
|
if (!den) return 0;
|
|
80
87
|
return Number((num / den).toFixed(4));
|
|
@@ -255,6 +262,149 @@ function inferTrafficChannel(raw = {}, referrerHost = null) {
|
|
|
255
262
|
return 'referral';
|
|
256
263
|
}
|
|
257
264
|
|
|
265
|
+
function normalizeBoolean(value) {
|
|
266
|
+
if (value === true || value === false) return value;
|
|
267
|
+
const text = normalizeText(value, 32);
|
|
268
|
+
if (!text) return false;
|
|
269
|
+
return ['1', 'true', 'yes', 'y', 'bot'].includes(text.toLowerCase());
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function includesAnyToken(values, tokens) {
|
|
273
|
+
const haystack = values
|
|
274
|
+
.map((value) => normalizeText(value, 512))
|
|
275
|
+
.filter(Boolean)
|
|
276
|
+
.join(' ')
|
|
277
|
+
.toLowerCase();
|
|
278
|
+
if (!haystack) return false;
|
|
279
|
+
return tokens.some((token) => haystack.includes(token));
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function addAudienceReason(reasons, code) {
|
|
283
|
+
if (!reasons.includes(code)) reasons.push(code);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function classifyTelemetryAudience(entry = {}, raw = {}) {
|
|
287
|
+
const reasons = [];
|
|
288
|
+
const identityValues = [
|
|
289
|
+
raw.email,
|
|
290
|
+
raw.customerEmail,
|
|
291
|
+
raw.buyerEmail,
|
|
292
|
+
raw.userEmail,
|
|
293
|
+
entry.visitorId,
|
|
294
|
+
entry.sessionId,
|
|
295
|
+
entry.traceId,
|
|
296
|
+
entry.acquisitionId,
|
|
297
|
+
entry.installId,
|
|
298
|
+
];
|
|
299
|
+
const attributionValues = [
|
|
300
|
+
entry.source,
|
|
301
|
+
entry.utmSource,
|
|
302
|
+
entry.utmMedium,
|
|
303
|
+
entry.utmCampaign,
|
|
304
|
+
entry.utmContent,
|
|
305
|
+
entry.creator,
|
|
306
|
+
entry.offerCode,
|
|
307
|
+
entry.campaignVariant,
|
|
308
|
+
entry.referrerHost,
|
|
309
|
+
];
|
|
310
|
+
const eventValues = [
|
|
311
|
+
entry.eventType,
|
|
312
|
+
entry.event,
|
|
313
|
+
entry.page,
|
|
314
|
+
entry.landingPath,
|
|
315
|
+
entry.ctaId,
|
|
316
|
+
entry.reasonCode,
|
|
317
|
+
entry.reasonDetail,
|
|
318
|
+
];
|
|
319
|
+
const userAgent = normalizeText(entry.userAgent || raw.userAgent, 512) || '';
|
|
320
|
+
const hostValues = [raw.host, raw.hostname, raw.origin, raw.url, raw.pageUrl, entry.referrerHost];
|
|
321
|
+
|
|
322
|
+
if (normalizeBoolean(entry.isBot || raw.isBot) || includesAnyToken([userAgent], [
|
|
323
|
+
'bot',
|
|
324
|
+
'crawler',
|
|
325
|
+
'spider',
|
|
326
|
+
'headless',
|
|
327
|
+
'playwright',
|
|
328
|
+
'puppeteer',
|
|
329
|
+
'curl/',
|
|
330
|
+
'wget/',
|
|
331
|
+
'lighthouse',
|
|
332
|
+
])) {
|
|
333
|
+
addAudienceReason(reasons, 'bot_or_automation_user_agent');
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (includesAnyToken(identityValues, [
|
|
337
|
+
'@example.com',
|
|
338
|
+
'buyer@example.com',
|
|
339
|
+
'test@example.com',
|
|
340
|
+
'codex-verification',
|
|
341
|
+
'audit@thumbgate.ai',
|
|
342
|
+
])) {
|
|
343
|
+
addAudienceReason(reasons, 'test_identity');
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (includesAnyToken([...attributionValues, ...eventValues], [
|
|
347
|
+
'codex',
|
|
348
|
+
'audit',
|
|
349
|
+
'verification',
|
|
350
|
+
'synthetic',
|
|
351
|
+
'smoke',
|
|
352
|
+
'probe',
|
|
353
|
+
'test',
|
|
354
|
+
])) {
|
|
355
|
+
addAudienceReason(reasons, 'test_or_audit_attribution');
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (includesAnyToken(hostValues, [
|
|
359
|
+
'localhost',
|
|
360
|
+
'127.0.0.1',
|
|
361
|
+
'::1',
|
|
362
|
+
'thumbgate-production.up.railway.app',
|
|
363
|
+
])) {
|
|
364
|
+
addAudienceReason(reasons, 'internal_host');
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (includesAnyToken([userAgent, ...identityValues], [
|
|
368
|
+
'codex',
|
|
369
|
+
'github-actions',
|
|
370
|
+
'node-fetch',
|
|
371
|
+
'thumbgate-analytics',
|
|
372
|
+
'thumbgate-verification',
|
|
373
|
+
])) {
|
|
374
|
+
addAudienceReason(reasons, 'internal_operator_or_ci');
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (
|
|
378
|
+
(entry.clientType || entry.client) === 'unknown' &&
|
|
379
|
+
!pickFirstText(entry.visitorId, entry.sessionId, entry.acquisitionId, entry.installId, entry.traceId) &&
|
|
380
|
+
!userAgent &&
|
|
381
|
+
includesAnyToken([entry.source, entry.utmSource], ['direct', 'website'])
|
|
382
|
+
) {
|
|
383
|
+
addAudienceReason(reasons, 'unknown_direct_no_identity');
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const audience = reasons.includes('test_identity') || reasons.includes('test_or_audit_attribution')
|
|
387
|
+
? 'test'
|
|
388
|
+
: (reasons.includes('bot_or_automation_user_agent')
|
|
389
|
+
? 'bot'
|
|
390
|
+
: (
|
|
391
|
+
reasons.includes('internal_host') ||
|
|
392
|
+
reasons.includes('internal_operator_or_ci') ||
|
|
393
|
+
reasons.includes('unknown_direct_no_identity')
|
|
394
|
+
? 'internal'
|
|
395
|
+
: 'external'
|
|
396
|
+
));
|
|
397
|
+
|
|
398
|
+
return {
|
|
399
|
+
audience,
|
|
400
|
+
reasons,
|
|
401
|
+
isExternal: audience === 'external',
|
|
402
|
+
isInternal: audience === 'internal',
|
|
403
|
+
isTest: audience === 'test',
|
|
404
|
+
isBot: audience === 'bot',
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
|
|
258
408
|
function sanitizeTelemetryPayload(payload = {}, headers = {}) {
|
|
259
409
|
const raw = payload && typeof payload === 'object' ? payload : {};
|
|
260
410
|
const clientType = inferClientType(raw);
|
|
@@ -313,6 +463,12 @@ function sanitizeTelemetryPayload(payload = {}, headers = {}) {
|
|
|
313
463
|
pipelineStatus: pickFirstText(raw.pipelineStatus, raw.workflowSprintStatus, raw.status),
|
|
314
464
|
reasonCode,
|
|
315
465
|
reasonDetail: pickFirstText(raw.reasonDetail, raw.reasonText, raw.otherReason, raw.notes),
|
|
466
|
+
integration: pickFirstText(raw.integration, raw.actionIntegration),
|
|
467
|
+
actionOperation: pickFirstText(raw.actionOperation, raw.operationId),
|
|
468
|
+
endpoint: pickFirstText(raw.endpoint, raw.apiPath),
|
|
469
|
+
decisionMode: pickFirstText(raw.decisionMode, raw.executionMode),
|
|
470
|
+
actionStatus: pickFirstText(raw.actionStatus, raw.status),
|
|
471
|
+
accepted: raw.accepted === undefined || raw.accepted === null ? null : Boolean(raw.accepted),
|
|
316
472
|
pricingInterest: pickFirstText(raw.pricingInterest, raw.interestLevel),
|
|
317
473
|
seoQuery: pickFirstText(raw.seoQuery, raw.query),
|
|
318
474
|
seoSurface: pickFirstText(raw.seoSurface, raw.searchSurface, raw.surface),
|
|
@@ -331,11 +487,22 @@ function sanitizeTelemetryPayload(payload = {}, headers = {}) {
|
|
|
331
487
|
httpStatus: normalizeInteger(raw.httpStatus),
|
|
332
488
|
userAgent: pickFirstText(raw.userAgent, headers['user-agent']),
|
|
333
489
|
isBot: pickFirstText(raw.isBot),
|
|
490
|
+
interstitialSampled: pickFirstText(raw.interstitialSampled),
|
|
491
|
+
interstitialSampleRate: normalizeRate(raw.interstitialSampleRate),
|
|
334
492
|
attributionTagged: Boolean(
|
|
335
493
|
pickFirstText(raw.utmSource, raw.utmMedium, raw.utmCampaign, raw.utmContent, raw.utmTerm)
|
|
336
494
|
),
|
|
337
495
|
};
|
|
338
496
|
|
|
497
|
+
const audience = classifyTelemetryAudience(entry, raw);
|
|
498
|
+
entry.audience = audience.audience;
|
|
499
|
+
entry.trafficAudience = audience.audience;
|
|
500
|
+
entry.audienceReasons = audience.reasons;
|
|
501
|
+
entry.isExternal = audience.isExternal;
|
|
502
|
+
entry.isInternal = audience.isInternal;
|
|
503
|
+
entry.isTest = audience.isTest;
|
|
504
|
+
entry.isBotTraffic = audience.isBot;
|
|
505
|
+
|
|
339
506
|
return entry;
|
|
340
507
|
}
|
|
341
508
|
|
|
@@ -426,9 +593,162 @@ function summarizeRecentEvents(events) {
|
|
|
426
593
|
community: entry.community || null,
|
|
427
594
|
offerCode: entry.offerCode || null,
|
|
428
595
|
campaignVariant: entry.campaignVariant || null,
|
|
596
|
+
audience: entry.audience || 'external',
|
|
597
|
+
audienceReasons: entry.audienceReasons || [],
|
|
429
598
|
}));
|
|
430
599
|
}
|
|
431
600
|
|
|
601
|
+
function summarizeExternalTrafficQuality(events) {
|
|
602
|
+
const byAudience = {};
|
|
603
|
+
const byExclusionReason = {};
|
|
604
|
+
const externalVisitors = new Set();
|
|
605
|
+
const externalSessions = new Set();
|
|
606
|
+
const externalCheckoutStarters = new Set();
|
|
607
|
+
const pageViewsBySource = {};
|
|
608
|
+
const pageViewsByPath = {};
|
|
609
|
+
const pageViewsByTrafficChannel = {};
|
|
610
|
+
const checkoutStartsBySource = {};
|
|
611
|
+
const checkoutStartsByTrafficChannel = {};
|
|
612
|
+
const buyerLossReasons = {};
|
|
613
|
+
const visitorPaths = new Map();
|
|
614
|
+
let externalEvents = 0;
|
|
615
|
+
let excludedEvents = 0;
|
|
616
|
+
let externalWebEvents = 0;
|
|
617
|
+
let externalPageViews = 0;
|
|
618
|
+
let externalCtaClicks = 0;
|
|
619
|
+
let externalCheckoutStarts = 0;
|
|
620
|
+
let externalBuyerLossSignals = 0;
|
|
621
|
+
|
|
622
|
+
for (const entry of events) {
|
|
623
|
+
const audience = entry.audience || 'external';
|
|
624
|
+
incrementCounter(byAudience, audience);
|
|
625
|
+
if (audience !== 'external') {
|
|
626
|
+
excludedEvents += 1;
|
|
627
|
+
const reasons = Array.isArray(entry.audienceReasons) && entry.audienceReasons.length > 0
|
|
628
|
+
? entry.audienceReasons
|
|
629
|
+
: [`${audience}_traffic`];
|
|
630
|
+
for (const reason of reasons) incrementCounter(byExclusionReason, reason);
|
|
631
|
+
continue;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
externalEvents += 1;
|
|
635
|
+
if ((entry.clientType || entry.client) !== 'web') continue;
|
|
636
|
+
|
|
637
|
+
externalWebEvents += 1;
|
|
638
|
+
const visitorKey = pickFirstText(entry.visitorId, entry.installId, entry.sessionId);
|
|
639
|
+
if (visitorKey) externalVisitors.add(visitorKey);
|
|
640
|
+
if (entry.sessionId) externalSessions.add(entry.sessionId);
|
|
641
|
+
|
|
642
|
+
const eventType = entry.eventType || entry.event;
|
|
643
|
+
if (visitorKey) {
|
|
644
|
+
const pathRow = visitorPaths.get(visitorKey) || {
|
|
645
|
+
visitorKey,
|
|
646
|
+
firstSeenAt: entry.receivedAt || null,
|
|
647
|
+
lastSeenAt: entry.receivedAt || null,
|
|
648
|
+
source: entry.source || null,
|
|
649
|
+
trafficChannel: entry.trafficChannel || null,
|
|
650
|
+
events: [],
|
|
651
|
+
pages: [],
|
|
652
|
+
checkoutStarts: 0,
|
|
653
|
+
};
|
|
654
|
+
if (!pathRow.firstSeenAt || String(entry.receivedAt || '') < pathRow.firstSeenAt) {
|
|
655
|
+
pathRow.firstSeenAt = entry.receivedAt || null;
|
|
656
|
+
}
|
|
657
|
+
if (!pathRow.lastSeenAt || String(entry.receivedAt || '') > pathRow.lastSeenAt) {
|
|
658
|
+
pathRow.lastSeenAt = entry.receivedAt || null;
|
|
659
|
+
}
|
|
660
|
+
pathRow.events.push({
|
|
661
|
+
at: entry.receivedAt || null,
|
|
662
|
+
eventType,
|
|
663
|
+
page: entry.page || entry.landingPath || null,
|
|
664
|
+
ctaId: entry.ctaId || null,
|
|
665
|
+
});
|
|
666
|
+
if ((entry.page || entry.landingPath) && !pathRow.pages.includes(entry.page || entry.landingPath)) {
|
|
667
|
+
pathRow.pages.push(entry.page || entry.landingPath);
|
|
668
|
+
}
|
|
669
|
+
if (eventType === 'checkout_start' || eventType === 'checkout_bootstrap') {
|
|
670
|
+
pathRow.checkoutStarts += 1;
|
|
671
|
+
}
|
|
672
|
+
visitorPaths.set(visitorKey, pathRow);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
if (eventType === 'landing_page_view') {
|
|
676
|
+
externalPageViews += 1;
|
|
677
|
+
incrementCounter(pageViewsBySource, entry.source);
|
|
678
|
+
incrementCounter(pageViewsByPath, entry.page);
|
|
679
|
+
incrementCounter(pageViewsByTrafficChannel, entry.trafficChannel);
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
if (isMarketingClickEvent(eventType)) {
|
|
683
|
+
externalCtaClicks += 1;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
if (eventType === 'checkout_start' || eventType === 'checkout_bootstrap') {
|
|
687
|
+
externalCheckoutStarts += 1;
|
|
688
|
+
incrementCounter(checkoutStartsBySource, entry.source);
|
|
689
|
+
incrementCounter(checkoutStartsByTrafficChannel, entry.trafficChannel);
|
|
690
|
+
const starterKey = pickFirstText(
|
|
691
|
+
entry.acquisitionId,
|
|
692
|
+
entry.visitorId,
|
|
693
|
+
entry.sessionId,
|
|
694
|
+
entry.installId,
|
|
695
|
+
entry.traceId
|
|
696
|
+
);
|
|
697
|
+
if (starterKey) externalCheckoutStarters.add(starterKey);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
if (eventType === 'checkout_cancelled' || eventType === 'checkout_abandoned' || eventType === 'reason_not_buying') {
|
|
701
|
+
externalBuyerLossSignals += 1;
|
|
702
|
+
incrementCounter(buyerLossReasons, entry.reasonCode);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
const topPath = getTopCounterEntry(pageViewsByPath);
|
|
707
|
+
const topSource = getTopCounterEntry(pageViewsBySource);
|
|
708
|
+
const topTrafficChannel = getTopCounterEntry(pageViewsByTrafficChannel);
|
|
709
|
+
const topExclusionReason = getTopCounterEntry(byExclusionReason);
|
|
710
|
+
|
|
711
|
+
return {
|
|
712
|
+
rawEvents: events.length,
|
|
713
|
+
externalEvents,
|
|
714
|
+
excludedEvents,
|
|
715
|
+
internalEvents: byAudience.internal || 0,
|
|
716
|
+
testEvents: byAudience.test || 0,
|
|
717
|
+
botEvents: byAudience.bot || 0,
|
|
718
|
+
exclusionRate: safeRate(excludedEvents, events.length),
|
|
719
|
+
byAudience,
|
|
720
|
+
byExclusionReason,
|
|
721
|
+
topExclusionReason: topExclusionReason ? { key: topExclusionReason[0], count: topExclusionReason[1] } : null,
|
|
722
|
+
external: {
|
|
723
|
+
totalEvents: externalWebEvents,
|
|
724
|
+
uniqueVisitors: externalVisitors.size,
|
|
725
|
+
uniqueSessions: externalSessions.size,
|
|
726
|
+
uniqueCheckoutStarters: externalCheckoutStarters.size,
|
|
727
|
+
pageViews: externalPageViews,
|
|
728
|
+
ctaClicks: externalCtaClicks,
|
|
729
|
+
checkoutStarts: externalCheckoutStarts,
|
|
730
|
+
buyerLossSignals: externalBuyerLossSignals,
|
|
731
|
+
pageViewToCheckoutRate: safeRate(externalCheckoutStarts, externalPageViews),
|
|
732
|
+
visitorToCheckoutRate: safeRate(externalCheckoutStarts, externalVisitors.size),
|
|
733
|
+
bySource: pageViewsBySource,
|
|
734
|
+
byPath: pageViewsByPath,
|
|
735
|
+
byTrafficChannel: pageViewsByTrafficChannel,
|
|
736
|
+
checkoutStartsBySource,
|
|
737
|
+
checkoutStartsByTrafficChannel,
|
|
738
|
+
buyerLossReasons,
|
|
739
|
+
topSource: topSource ? { key: topSource[0], count: topSource[1] } : null,
|
|
740
|
+
topPath: topPath ? { key: topPath[0], count: topPath[1] } : null,
|
|
741
|
+
topTrafficChannel: topTrafficChannel ? { key: topTrafficChannel[0], count: topTrafficChannel[1] } : null,
|
|
742
|
+
visitorPaths: Array.from(visitorPaths.values())
|
|
743
|
+
.sort((a, b) => String(a.firstSeenAt || '').localeCompare(String(b.firstSeenAt || '')))
|
|
744
|
+
.slice(0, 50),
|
|
745
|
+
},
|
|
746
|
+
verdict: externalEvents > 0
|
|
747
|
+
? (excludedEvents > externalEvents ? 'polluted_but_usable' : 'usable')
|
|
748
|
+
: (events.length > 0 ? 'polluted_no_external_signal' : 'missing'),
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
|
|
432
752
|
function getTelemetrySummary(feedbackDir, options = {}) {
|
|
433
753
|
const analyticsWindow = resolveAnalyticsWindow(options);
|
|
434
754
|
const telemetryLoadOptions = analyticsWindow.bounded
|
|
@@ -439,6 +759,7 @@ function getTelemetrySummary(feedbackDir, options = {}) {
|
|
|
439
759
|
analyticsWindow,
|
|
440
760
|
(entry) => entry && (entry.receivedAt || entry.timestamp)
|
|
441
761
|
);
|
|
762
|
+
const trafficQuality = summarizeExternalTrafficQuality(events);
|
|
442
763
|
const byClientType = {};
|
|
443
764
|
const byEventType = {};
|
|
444
765
|
const webVisitors = new Set();
|
|
@@ -526,6 +847,12 @@ function getTelemetrySummary(feedbackDir, options = {}) {
|
|
|
526
847
|
let exitEngagementMsCount = 0;
|
|
527
848
|
let exitScrollPercentTotal = 0;
|
|
528
849
|
let exitScrollPercentCount = 0;
|
|
850
|
+
let chatgptActionCalls = 0;
|
|
851
|
+
let chatgptActionAccepted = 0;
|
|
852
|
+
const chatgptActionsByOperation = {};
|
|
853
|
+
const chatgptActionsByEndpoint = {};
|
|
854
|
+
const chatgptActionsByStatus = {};
|
|
855
|
+
const chatgptActionsByDecisionMode = {};
|
|
529
856
|
|
|
530
857
|
for (const entry of events) {
|
|
531
858
|
incrementCounter(byClientType, entry.clientType || entry.client || 'unknown');
|
|
@@ -714,6 +1041,18 @@ function getTelemetrySummary(feedbackDir, options = {}) {
|
|
|
714
1041
|
}
|
|
715
1042
|
}
|
|
716
1043
|
|
|
1044
|
+
if (
|
|
1045
|
+
String(entry.eventType || entry.event || '').startsWith('chatgpt_action_') ||
|
|
1046
|
+
entry.integration === 'chatgpt_gpt'
|
|
1047
|
+
) {
|
|
1048
|
+
chatgptActionCalls += 1;
|
|
1049
|
+
if (entry.accepted === true) chatgptActionAccepted += 1;
|
|
1050
|
+
incrementCounter(chatgptActionsByOperation, entry.actionOperation);
|
|
1051
|
+
incrementCounter(chatgptActionsByEndpoint, entry.endpoint);
|
|
1052
|
+
incrementCounter(chatgptActionsByStatus, entry.actionStatus);
|
|
1053
|
+
incrementCounter(chatgptActionsByDecisionMode, entry.decisionMode);
|
|
1054
|
+
}
|
|
1055
|
+
|
|
717
1056
|
if ((entry.clientType || entry.client) === 'cli') {
|
|
718
1057
|
if (entry.installId) cliInstalls.add(entry.installId);
|
|
719
1058
|
incrementCounter(cliByPlatform, entry.platform);
|
|
@@ -764,12 +1103,14 @@ function getTelemetrySummary(feedbackDir, options = {}) {
|
|
|
764
1103
|
window: serializeAnalyticsWindow(analyticsWindow),
|
|
765
1104
|
totalEvents: events.length,
|
|
766
1105
|
latestSeenAt,
|
|
1106
|
+
trafficQuality,
|
|
767
1107
|
byClientType,
|
|
768
1108
|
byEventType,
|
|
769
1109
|
conversionFunnel: {
|
|
770
1110
|
landingViews: pageViews,
|
|
771
1111
|
installCopies,
|
|
772
1112
|
gptOpens,
|
|
1113
|
+
gptActionCalls: chatgptActionCalls,
|
|
773
1114
|
checkoutStarts,
|
|
774
1115
|
checkoutInterstitialViews,
|
|
775
1116
|
checkoutInterstitialClicks,
|
|
@@ -777,6 +1118,7 @@ function getTelemetrySummary(feedbackDir, options = {}) {
|
|
|
777
1118
|
proConversions,
|
|
778
1119
|
landingToInstallCopyRate: safeRate(installCopies, pageViews),
|
|
779
1120
|
landingToGptOpenRate: safeRate(gptOpens, pageViews),
|
|
1121
|
+
gptOpenToActionRate: safeRate(chatgptActionCalls, gptOpens),
|
|
780
1122
|
landingToCheckoutRate: safeRate(checkoutStarts, pageViews),
|
|
781
1123
|
checkoutInterstitialClickRate: safeRate(checkoutInterstitialClicks, checkoutInterstitialViews),
|
|
782
1124
|
checkoutInterstitialProConfirmRate: safeRate(checkoutInterstitialProConfirms, checkoutInterstitialViews),
|
|
@@ -835,6 +1177,17 @@ function getTelemetrySummary(feedbackDir, options = {}) {
|
|
|
835
1177
|
byPlatform: cliByPlatform,
|
|
836
1178
|
byVersion: cliByVersion,
|
|
837
1179
|
},
|
|
1180
|
+
chatgpt: {
|
|
1181
|
+
gptOpens,
|
|
1182
|
+
actionCalls: chatgptActionCalls,
|
|
1183
|
+
acceptedActionCalls: chatgptActionAccepted,
|
|
1184
|
+
openToActionRate: safeRate(chatgptActionCalls, gptOpens),
|
|
1185
|
+
acceptedActionRate: safeRate(chatgptActionAccepted, chatgptActionCalls),
|
|
1186
|
+
byOperation: chatgptActionsByOperation,
|
|
1187
|
+
byEndpoint: chatgptActionsByEndpoint,
|
|
1188
|
+
byStatus: chatgptActionsByStatus,
|
|
1189
|
+
byDecisionMode: chatgptActionsByDecisionMode,
|
|
1190
|
+
},
|
|
838
1191
|
marketing: {
|
|
839
1192
|
pageViewsBySource,
|
|
840
1193
|
pageViewsByCampaign,
|
|
@@ -926,9 +1279,12 @@ function getTelemetryAnalytics(feedbackDir, options = {}) {
|
|
|
926
1279
|
window: summary.window,
|
|
927
1280
|
totalEvents: summary.totalEvents,
|
|
928
1281
|
latestSeenAt: summary.latestSeenAt,
|
|
1282
|
+
trafficQuality: summary.trafficQuality,
|
|
1283
|
+
qualified: summary.trafficQuality.external,
|
|
929
1284
|
byClientType: summary.byClientType,
|
|
930
1285
|
byEventType: summary.byEventType,
|
|
931
1286
|
conversionFunnel: summary.conversionFunnel,
|
|
1287
|
+
chatgpt: summary.chatgpt,
|
|
932
1288
|
visitors: {
|
|
933
1289
|
totalEvents: summary.web.totalEvents,
|
|
934
1290
|
uniqueVisitors: summary.web.uniqueVisitors,
|
|
@@ -1104,6 +1460,7 @@ const appendTelemetryPing = appendTelemetryEvent;
|
|
|
1104
1460
|
module.exports = {
|
|
1105
1461
|
TELEMETRY_FILE_NAME,
|
|
1106
1462
|
sanitizeTelemetryPayload,
|
|
1463
|
+
classifyTelemetryAudience,
|
|
1107
1464
|
appendTelemetryPing,
|
|
1108
1465
|
appendTelemetryEvent,
|
|
1109
1466
|
getTelemetrySourceDiagnostics,
|
|
@@ -302,24 +302,44 @@ function getCalibration(model) {
|
|
|
302
302
|
// ---------------------------------------------------------------------------
|
|
303
303
|
|
|
304
304
|
/**
|
|
305
|
-
*
|
|
306
|
-
*
|
|
305
|
+
* Return the Beta posterior parameters after applying Thompson temperature
|
|
306
|
+
* scaling. The posterior mean is preserved while precision changes:
|
|
307
|
+
* lower temperatures sharpen the posterior, higher temperatures flatten it.
|
|
307
308
|
*
|
|
308
|
-
*
|
|
309
|
+
* @param {Object} params - Category posterior parameters
|
|
310
|
+
* @param {number} temperature - Scaling factor (default 1.0)
|
|
311
|
+
* @returns {{ alpha: number, beta: number }}
|
|
312
|
+
*/
|
|
313
|
+
function getTemperatureScaledPosteriorParams(params, temperature = 1.0) {
|
|
314
|
+
const T = Math.max(0.01, Number(temperature) || 1.0);
|
|
315
|
+
const invT = 1.0 / T;
|
|
316
|
+
return {
|
|
317
|
+
alpha: Math.max(params.alpha * invT, 0.01),
|
|
318
|
+
beta: Math.max(params.beta * invT, 0.01),
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Draw one sample from the Beta posterior for each category.
|
|
324
|
+
* Supports temperature scaling to adjust exploitation vs exploration.
|
|
309
325
|
*
|
|
310
|
-
*
|
|
311
|
-
*
|
|
326
|
+
* Temperature (T):
|
|
327
|
+
* T = 1.0 (default) — Standard Thompson Sampling.
|
|
328
|
+
* T < 1.0 — Sharper distribution, favors high-reliability categories (exploit).
|
|
329
|
+
* T > 1.0 — Flatter distribution, increases uncertainty and exploration.
|
|
330
|
+
*
|
|
331
|
+
* Implementation: Scales alpha and beta by 1/T.
|
|
312
332
|
*
|
|
313
333
|
* @param {Object} model - Model object containing categories
|
|
334
|
+
* @param {number} temperature - Scaling factor (default 1.0)
|
|
314
335
|
* @returns {Object} Map of category → float sample in [0, 1]
|
|
315
336
|
*/
|
|
316
|
-
function samplePosteriors(model) {
|
|
337
|
+
function samplePosteriors(model, temperature = 1.0) {
|
|
317
338
|
const samples = {};
|
|
339
|
+
|
|
318
340
|
for (const [cat, params] of Object.entries(model.categories || {})) {
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
Math.max(params.beta, 0.01),
|
|
322
|
-
);
|
|
341
|
+
const { alpha, beta } = getTemperatureScaledPosteriorParams(params, temperature);
|
|
342
|
+
samples[cat] = betaSample(alpha, beta);
|
|
323
343
|
}
|
|
324
344
|
return samples;
|
|
325
345
|
}
|
|
@@ -451,6 +471,7 @@ module.exports = {
|
|
|
451
471
|
getReliability,
|
|
452
472
|
isCalibrated,
|
|
453
473
|
getCalibration,
|
|
474
|
+
getTemperatureScaledPosteriorParams,
|
|
454
475
|
samplePosteriors,
|
|
455
476
|
argmaxPosteriors,
|
|
456
477
|
pickBestCategory,
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
const fs = require('node:fs');
|
|
5
5
|
const os = require('node:os');
|
|
6
6
|
const path = require('node:path');
|
|
7
|
+
const { expandFixturePlaceholders } = require('./secret-fixture-tokens');
|
|
7
8
|
|
|
8
9
|
const ROOT = path.join(__dirname, '..');
|
|
9
10
|
const DEFAULT_SUITE_PATH = path.join(ROOT, 'bench', 'thumbgate-bench.json');
|
|
@@ -180,6 +181,20 @@ function assertObject(value, label) {
|
|
|
180
181
|
}
|
|
181
182
|
}
|
|
182
183
|
|
|
184
|
+
function expandScenarioFixturePlaceholders(value) {
|
|
185
|
+
if (typeof value === 'string') return expandFixturePlaceholders(value);
|
|
186
|
+
if (Array.isArray(value)) return value.map(expandScenarioFixturePlaceholders);
|
|
187
|
+
if (value && typeof value === 'object') {
|
|
188
|
+
return Object.fromEntries(
|
|
189
|
+
Object.entries(value).map(([key, nestedValue]) => [
|
|
190
|
+
key,
|
|
191
|
+
expandScenarioFixturePlaceholders(nestedValue),
|
|
192
|
+
]),
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
return value;
|
|
196
|
+
}
|
|
197
|
+
|
|
183
198
|
function loadScenarioSuite(filePath = DEFAULT_SUITE_PATH) {
|
|
184
199
|
const suite = readJson(filePath);
|
|
185
200
|
assertObject(suite, 'Scenario suite');
|
|
@@ -202,7 +217,7 @@ function loadScenarioSuite(filePath = DEFAULT_SUITE_PATH) {
|
|
|
202
217
|
throw new Error(`Scenario ${id} has invalid expectedDecision`);
|
|
203
218
|
}
|
|
204
219
|
return {
|
|
205
|
-
...scenario,
|
|
220
|
+
...expandScenarioFixturePlaceholders(scenario),
|
|
206
221
|
id,
|
|
207
222
|
unsafe: Boolean(scenario.unsafe),
|
|
208
223
|
positivePattern: Boolean(scenario.positivePattern),
|
|
@@ -151,8 +151,67 @@ function sortResults(results) {
|
|
|
151
151
|
});
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
function
|
|
155
|
-
|
|
154
|
+
function extractFeedbackId(str) {
|
|
155
|
+
if (!str) return null;
|
|
156
|
+
const match = str.match(/fb[_-]\d+[_-][a-z0-9]+/i);
|
|
157
|
+
return match ? match[0].replace(/-/g, '_').toLowerCase() : null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function deduplicateResults(results) {
|
|
161
|
+
const bestByFeedbackId = new Map();
|
|
162
|
+
|
|
163
|
+
for (const r of results) {
|
|
164
|
+
const feedId = extractFeedbackId(r.id || r.title || r.file || '');
|
|
165
|
+
if (feedId) {
|
|
166
|
+
const existing = bestByFeedbackId.get(feedId);
|
|
167
|
+
if (!existing) {
|
|
168
|
+
bestByFeedbackId.set(feedId, r);
|
|
169
|
+
} else {
|
|
170
|
+
const sourceOrder = { feedback: 4, contextfs: 3, prevention_rule: 2, document: 1 };
|
|
171
|
+
const existingOrder = sourceOrder[existing.source] || 0;
|
|
172
|
+
const currentOrder = sourceOrder[r.source] || 0;
|
|
173
|
+
if (currentOrder > existingOrder) {
|
|
174
|
+
bestByFeedbackId.set(feedId, r);
|
|
175
|
+
} else if (currentOrder === existingOrder && (r.score || 0) > (existing.score || 0)) {
|
|
176
|
+
bestByFeedbackId.set(feedId, r);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const finalResults = [];
|
|
183
|
+
const seenContent = new Set();
|
|
184
|
+
const seenIds = new Set();
|
|
185
|
+
|
|
186
|
+
for (const r of results) {
|
|
187
|
+
const feedId = extractFeedbackId(r.id || r.title || r.file || '');
|
|
188
|
+
let recordToUse = r;
|
|
189
|
+
if (feedId) {
|
|
190
|
+
recordToUse = bestByFeedbackId.get(feedId);
|
|
191
|
+
if (seenIds.has(recordToUse.id)) continue;
|
|
192
|
+
} else {
|
|
193
|
+
if (r.id && r.id !== 'null' && String(r.id).trim() !== '') {
|
|
194
|
+
if (seenIds.has(r.id)) continue;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const normTitle = String(recordToUse.title || '').trim().toLowerCase();
|
|
199
|
+
const normContext = String(recordToUse.context || '').trim().toLowerCase();
|
|
200
|
+
const contentKey = `${normTitle}|${normContext}`;
|
|
201
|
+
if (seenContent.has(contentKey)) continue;
|
|
202
|
+
|
|
203
|
+
if (recordToUse.id && recordToUse.id !== 'null' && String(recordToUse.id).trim() !== '') {
|
|
204
|
+
seenIds.add(recordToUse.id);
|
|
205
|
+
}
|
|
206
|
+
seenContent.add(contentKey);
|
|
207
|
+
finalResults.push(recordToUse);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return finalResults;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function getFeedbackResults(query, limit, signal, feedbackDir) {
|
|
214
|
+
const results = searchFeedbackLog(query, Math.max(limit * 3, limit), { feedbackDir });
|
|
156
215
|
const normalizedSignal = normalizeSignal(signal);
|
|
157
216
|
const filtered = normalizedSignal
|
|
158
217
|
? results.filter((record) => normalizeRecordSignal(record.signal) === normalizedSignal)
|
|
@@ -160,19 +219,19 @@ function getFeedbackResults(query, limit, signal) {
|
|
|
160
219
|
return filtered.slice(0, limit).map(mapFeedbackResult);
|
|
161
220
|
}
|
|
162
221
|
|
|
163
|
-
function getContextResults(query, limit) {
|
|
164
|
-
return searchContextFs(query, limit).map(mapContextResult);
|
|
222
|
+
function getContextResults(query, limit, feedbackDir) {
|
|
223
|
+
return searchContextFs(query, limit, { feedbackDir }).map(mapContextResult);
|
|
165
224
|
}
|
|
166
225
|
|
|
167
|
-
function getRuleResults(query, limit) {
|
|
168
|
-
return searchPreventionRulesSync(query, limit).map(mapRuleResult);
|
|
226
|
+
function getRuleResults(query, limit, feedbackDir) {
|
|
227
|
+
return searchPreventionRulesSync(query, limit, { feedbackDir }).map(mapRuleResult);
|
|
169
228
|
}
|
|
170
229
|
|
|
171
|
-
function getDocumentResults(query, limit) {
|
|
172
|
-
return searchImportedDocuments({ query, limit }).map(mapDocumentResult);
|
|
230
|
+
function getDocumentResults(query, limit, feedbackDir) {
|
|
231
|
+
return searchImportedDocuments({ query, limit, feedbackDir }).map(mapDocumentResult);
|
|
173
232
|
}
|
|
174
233
|
|
|
175
|
-
function searchThumbgate({ query, source = 'all', limit = 10, signal = null } = {}) {
|
|
234
|
+
function searchThumbgate({ query, source = 'all', limit = 10, signal = null, feedbackDir = null } = {}) {
|
|
176
235
|
const trimmedQuery = String(query || '').trim();
|
|
177
236
|
if (!trimmedQuery) {
|
|
178
237
|
throw new Error('query is required');
|
|
@@ -182,22 +241,29 @@ function searchThumbgate({ query, source = 'all', limit = 10, signal = null } =
|
|
|
182
241
|
const normalizedSignal = normalizeSignal(signal);
|
|
183
242
|
const normalizedLimit = normalizeLimit(limit);
|
|
184
243
|
|
|
244
|
+
const fetchLimit = Math.max(100, normalizedLimit * 5);
|
|
245
|
+
|
|
185
246
|
let results = [];
|
|
186
247
|
if (normalizedSource === 'feedback') {
|
|
187
|
-
|
|
248
|
+
const raw = getFeedbackResults(trimmedQuery, fetchLimit, normalizedSignal, feedbackDir);
|
|
249
|
+
results = deduplicateResults(raw).slice(0, normalizedLimit);
|
|
188
250
|
} else if (normalizedSource === 'context') {
|
|
189
|
-
|
|
251
|
+
const raw = getContextResults(trimmedQuery, fetchLimit, feedbackDir);
|
|
252
|
+
results = deduplicateResults(raw).slice(0, normalizedLimit);
|
|
190
253
|
} else if (normalizedSource === 'rules') {
|
|
191
|
-
|
|
254
|
+
const raw = getRuleResults(trimmedQuery, fetchLimit, feedbackDir);
|
|
255
|
+
results = deduplicateResults(raw).slice(0, normalizedLimit);
|
|
192
256
|
} else if (normalizedSource === 'documents') {
|
|
193
|
-
|
|
257
|
+
const raw = getDocumentResults(trimmedQuery, fetchLimit, feedbackDir);
|
|
258
|
+
results = deduplicateResults(raw).slice(0, normalizedLimit);
|
|
194
259
|
} else {
|
|
195
|
-
|
|
196
|
-
...getFeedbackResults(trimmedQuery,
|
|
197
|
-
...getContextResults(trimmedQuery,
|
|
198
|
-
...getRuleResults(trimmedQuery,
|
|
199
|
-
...getDocumentResults(trimmedQuery,
|
|
200
|
-
]
|
|
260
|
+
const combined = [
|
|
261
|
+
...getFeedbackResults(trimmedQuery, fetchLimit, normalizedSignal, feedbackDir),
|
|
262
|
+
...getContextResults(trimmedQuery, fetchLimit, feedbackDir),
|
|
263
|
+
...getRuleResults(trimmedQuery, fetchLimit, feedbackDir),
|
|
264
|
+
...getDocumentResults(trimmedQuery, fetchLimit, feedbackDir),
|
|
265
|
+
];
|
|
266
|
+
results = deduplicateResults(sortResults(combined)).slice(0, normalizedLimit);
|
|
201
267
|
}
|
|
202
268
|
|
|
203
269
|
return {
|