fodda-mcp 1.38.0 → 1.46.97
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/README.md +14 -91
- package/dist/agents/fodda-researcher/skills.d.ts +1 -1
- package/dist/agents/fodda-researcher/skills.d.ts.map +1 -1
- package/dist/agents/fodda-researcher/skills.js +1 -1
- package/dist/brandTemplate.d.ts +1 -1
- package/dist/brandTemplate.d.ts.map +1 -1
- package/dist/brandTemplate.js +278 -83
- package/dist/brandTemplate.js.map +1 -1
- package/dist/catalogCache.d.ts +39 -0
- package/dist/catalogCache.d.ts.map +1 -1
- package/dist/catalogCache.js +293 -22
- package/dist/catalogCache.js.map +1 -1
- package/dist/coverageRelevance.d.ts +251 -0
- package/dist/coverageRelevance.d.ts.map +1 -0
- package/dist/coverageRelevance.js +1857 -0
- package/dist/coverageRelevance.js.map +1 -0
- package/dist/editorialFill.d.ts +5 -0
- package/dist/editorialFill.d.ts.map +1 -1
- package/dist/editorialFill.js +1 -1
- package/dist/editorialFill.js.map +1 -1
- package/dist/enrichment.d.ts +26 -0
- package/dist/enrichment.d.ts.map +1 -1
- package/dist/enrichment.js +442 -29
- package/dist/enrichment.js.map +1 -1
- package/dist/errorHandling.d.ts +35 -7
- package/dist/errorHandling.d.ts.map +1 -1
- package/dist/errorHandling.js +225 -155
- package/dist/errorHandling.js.map +1 -1
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +411 -106
- package/dist/index.js.map +1 -1
- package/dist/pricingCache.d.ts.map +1 -1
- package/dist/pricingCache.js +55 -3
- package/dist/pricingCache.js.map +1 -1
- package/dist/queryCache.d.ts.map +1 -1
- package/dist/queryCache.js +2 -0
- package/dist/queryCache.js.map +1 -1
- package/dist/reportBriefing.d.ts +89 -0
- package/dist/reportBriefing.d.ts.map +1 -0
- package/dist/reportBriefing.js +662 -0
- package/dist/reportBriefing.js.map +1 -0
- package/dist/searchTemplate.d.ts.map +1 -1
- package/dist/searchTemplate.js +65 -53
- package/dist/searchTemplate.js.map +1 -1
- package/dist/services/graphRouter.d.ts +7 -0
- package/dist/services/graphRouter.d.ts.map +1 -0
- package/dist/services/graphRouter.js +7 -0
- package/dist/services/graphRouter.js.map +1 -0
- package/dist/services/notifications.d.ts +2 -2
- package/dist/services/notifications.js +3 -3
- package/dist/services/notifications.js.map +1 -1
- package/dist/sessionTracker.d.ts +42 -3
- package/dist/sessionTracker.d.ts.map +1 -1
- package/dist/sessionTracker.js +261 -14
- package/dist/sessionTracker.js.map +1 -1
- package/dist/skillClient.d.ts.map +1 -1
- package/dist/skillClient.js +11 -6
- package/dist/skillClient.js.map +1 -1
- package/dist/svgVisuals.d.ts +17 -19
- package/dist/svgVisuals.d.ts.map +1 -1
- package/dist/svgVisuals.js +189 -233
- package/dist/svgVisuals.js.map +1 -1
- package/dist/systemPrompt.d.ts +7 -2
- package/dist/systemPrompt.d.ts.map +1 -1
- package/dist/systemPrompt.js +153 -81
- package/dist/systemPrompt.js.map +1 -1
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +15 -0
- package/dist/telemetry.js.map +1 -1
- package/dist/test_adjacent_trends_polymorphic.d.ts +2 -0
- package/dist/test_adjacent_trends_polymorphic.d.ts.map +1 -0
- package/dist/test_adjacent_trends_polymorphic.js +154 -0
- package/dist/test_adjacent_trends_polymorphic.js.map +1 -0
- package/dist/test_book_a_call_live.d.ts +2 -0
- package/dist/test_book_a_call_live.d.ts.map +1 -0
- package/dist/test_book_a_call_live.js +161 -0
- package/dist/test_book_a_call_live.js.map +1 -0
- package/dist/test_brand_intelligence_endpoint.js +2 -2
- package/dist/test_byo_mcp_submission_clarity.d.ts +2 -0
- package/dist/test_byo_mcp_submission_clarity.d.ts.map +1 -0
- package/dist/test_byo_mcp_submission_clarity.js +142 -0
- package/dist/test_byo_mcp_submission_clarity.js.map +1 -0
- package/dist/test_capabilities_guidance.d.ts +2 -0
- package/dist/test_capabilities_guidance.d.ts.map +1 -0
- package/dist/test_capabilities_guidance.js +372 -0
- package/dist/test_capabilities_guidance.js.map +1 -0
- package/dist/test_client_provenance.d.ts +2 -0
- package/dist/test_client_provenance.d.ts.map +1 -0
- package/dist/test_client_provenance.js +77 -0
- package/dist/test_client_provenance.js.map +1 -0
- package/dist/test_connection_surface_cleanup.d.ts +2 -0
- package/dist/test_connection_surface_cleanup.d.ts.map +1 -0
- package/dist/test_connection_surface_cleanup.js +203 -0
- package/dist/test_connection_surface_cleanup.js.map +1 -0
- package/dist/test_consult_routing.d.ts +2 -0
- package/dist/test_consult_routing.d.ts.map +1 -0
- package/dist/test_consult_routing.js +86 -0
- package/dist/test_consult_routing.js.map +1 -0
- package/dist/test_consult_split.d.ts +2 -0
- package/dist/test_consult_split.d.ts.map +1 -0
- package/dist/test_consult_split.js +30 -0
- package/dist/test_consult_split.js.map +1 -0
- package/dist/test_coverage_relevance.d.ts +12 -0
- package/dist/test_coverage_relevance.d.ts.map +1 -0
- package/dist/test_coverage_relevance.js +162 -0
- package/dist/test_coverage_relevance.js.map +1 -0
- package/dist/test_coverage_replay.d.ts +2 -0
- package/dist/test_coverage_replay.d.ts.map +1 -0
- package/dist/test_coverage_replay.js +170 -0
- package/dist/test_coverage_replay.js.map +1 -0
- package/dist/test_dcr_and_legacy_deprecation.d.ts +2 -0
- package/dist/test_dcr_and_legacy_deprecation.d.ts.map +1 -0
- package/dist/test_dcr_and_legacy_deprecation.js +143 -0
- package/dist/test_dcr_and_legacy_deprecation.js.map +1 -0
- package/dist/test_demand_signal_logging.d.ts +2 -0
- package/dist/test_demand_signal_logging.d.ts.map +1 -0
- package/dist/test_demand_signal_logging.js +92 -0
- package/dist/test_demand_signal_logging.js.map +1 -0
- package/dist/test_earnings_activity_scope.d.ts +5 -0
- package/dist/test_earnings_activity_scope.d.ts.map +1 -0
- package/dist/test_earnings_activity_scope.js +101 -0
- package/dist/test_earnings_activity_scope.js.map +1 -0
- package/dist/test_earnings_divergence_live.d.ts +5 -0
- package/dist/test_earnings_divergence_live.d.ts.map +1 -0
- package/dist/test_earnings_divergence_live.js +135 -0
- package/dist/test_earnings_divergence_live.js.map +1 -0
- package/dist/test_earnings_hygiene_and_binding.d.ts +5 -0
- package/dist/test_earnings_hygiene_and_binding.d.ts.map +1 -0
- package/dist/test_earnings_hygiene_and_binding.js +104 -0
- package/dist/test_earnings_hygiene_and_binding.js.map +1 -0
- package/dist/test_exec_quotes_render.d.ts +2 -0
- package/dist/test_exec_quotes_render.d.ts.map +1 -0
- package/dist/test_exec_quotes_render.js +86 -0
- package/dist/test_exec_quotes_render.js.map +1 -0
- package/dist/test_expert_intro.d.ts +2 -0
- package/dist/test_expert_intro.d.ts.map +1 -0
- package/dist/test_expert_intro.js +57 -0
- package/dist/test_expert_intro.js.map +1 -0
- package/dist/test_expert_layer.d.ts +11 -0
- package/dist/test_expert_layer.d.ts.map +1 -0
- package/dist/test_expert_layer.js +356 -0
- package/dist/test_expert_layer.js.map +1 -0
- package/dist/test_financial_snapshot_render.d.ts +2 -0
- package/dist/test_financial_snapshot_render.d.ts.map +1 -0
- package/dist/test_financial_snapshot_render.js +167 -0
- package/dist/test_financial_snapshot_render.js.map +1 -0
- package/dist/test_gap_alert.d.ts +12 -0
- package/dist/test_gap_alert.d.ts.map +1 -0
- package/dist/test_gap_alert.js +51 -0
- package/dist/test_gap_alert.js.map +1 -0
- package/dist/test_grok_offering_surfaces.d.ts +2 -0
- package/dist/test_grok_offering_surfaces.d.ts.map +1 -0
- package/dist/test_grok_offering_surfaces.js +155 -0
- package/dist/test_grok_offering_surfaces.js.map +1 -0
- package/dist/test_human_agent_live.d.ts +2 -0
- package/dist/test_human_agent_live.d.ts.map +1 -0
- package/dist/test_human_agent_live.js +48 -0
- package/dist/test_human_agent_live.js.map +1 -0
- package/dist/test_identity_gap.d.ts +2 -0
- package/dist/test_identity_gap.d.ts.map +1 -0
- package/dist/test_identity_gap.js +171 -0
- package/dist/test_identity_gap.js.map +1 -0
- package/dist/test_intelligence_and_verifier.d.ts +17 -0
- package/dist/test_intelligence_and_verifier.d.ts.map +1 -0
- package/dist/test_intelligence_and_verifier.js +162 -0
- package/dist/test_intelligence_and_verifier.js.map +1 -0
- package/dist/test_latency_mcp_brief5.d.ts +12 -0
- package/dist/test_latency_mcp_brief5.d.ts.map +1 -0
- package/dist/test_latency_mcp_brief5.js +200 -0
- package/dist/test_latency_mcp_brief5.js.map +1 -0
- package/dist/test_limit_and_credit_card_capture.d.ts +21 -0
- package/dist/test_limit_and_credit_card_capture.d.ts.map +1 -0
- package/dist/test_limit_and_credit_card_capture.js +216 -0
- package/dist/test_limit_and_credit_card_capture.js.map +1 -0
- package/dist/test_list_graphs_topics.js +1 -1
- package/dist/test_list_graphs_topics.js.map +1 -1
- package/dist/test_live_deployed_mcp_search.d.ts +2 -0
- package/dist/test_live_deployed_mcp_search.d.ts.map +1 -0
- package/dist/test_live_deployed_mcp_search.js +85 -0
- package/dist/test_live_deployed_mcp_search.js.map +1 -0
- package/dist/test_live_deployed_split.d.ts +2 -0
- package/dist/test_live_deployed_split.d.ts.map +1 -0
- package/dist/test_live_deployed_split.js +72 -0
- package/dist/test_live_deployed_split.js.map +1 -0
- package/dist/test_live_endpoint_runner.js +2 -2
- package/dist/test_live_endpoint_runner.js.map +1 -1
- package/dist/test_live_mcp.js +1 -1
- package/dist/test_live_mcp.js.map +1 -1
- package/dist/test_live_neil_widget.d.ts +2 -0
- package/dist/test_live_neil_widget.d.ts.map +1 -0
- package/dist/test_live_neil_widget.js +84 -0
- package/dist/test_live_neil_widget.js.map +1 -0
- package/dist/test_longpoll_status.d.ts +2 -0
- package/dist/test_longpoll_status.d.ts.map +1 -0
- package/dist/test_longpoll_status.js +117 -0
- package/dist/test_longpoll_status.js.map +1 -0
- package/dist/test_mcp_find_expert_and_vocabulary_routing.d.ts +2 -0
- package/dist/test_mcp_find_expert_and_vocabulary_routing.d.ts.map +1 -0
- package/dist/test_mcp_find_expert_and_vocabulary_routing.js +130 -0
- package/dist/test_mcp_find_expert_and_vocabulary_routing.js.map +1 -0
- package/dist/test_next_moves.d.ts +2 -0
- package/dist/test_next_moves.d.ts.map +1 -0
- package/dist/test_next_moves.js +714 -0
- package/dist/test_next_moves.js.map +1 -0
- package/dist/test_next_moves_transcripts.d.ts +2 -0
- package/dist/test_next_moves_transcripts.d.ts.map +1 -0
- package/dist/test_next_moves_transcripts.js +495 -0
- package/dist/test_next_moves_transcripts.js.map +1 -0
- package/dist/test_on_request_caller_identity.d.ts +13 -0
- package/dist/test_on_request_caller_identity.d.ts.map +1 -0
- package/dist/test_on_request_caller_identity.js +131 -0
- package/dist/test_on_request_caller_identity.js.map +1 -0
- package/dist/test_on_request_intercept.d.ts +10 -0
- package/dist/test_on_request_intercept.d.ts.map +1 -0
- package/dist/test_on_request_intercept.js +95 -0
- package/dist/test_on_request_intercept.js.map +1 -0
- package/dist/test_onboarding_consent_and_errors.d.ts +2 -0
- package/dist/test_onboarding_consent_and_errors.d.ts.map +1 -0
- package/dist/test_onboarding_consent_and_errors.js +110 -0
- package/dist/test_onboarding_consent_and_errors.js.map +1 -0
- package/dist/test_onboarding_live_scenarios.d.ts +2 -0
- package/dist/test_onboarding_live_scenarios.d.ts.map +1 -0
- package/dist/test_onboarding_live_scenarios.js +189 -0
- package/dist/test_onboarding_live_scenarios.js.map +1 -0
- package/dist/test_referral_live.d.ts +2 -0
- package/dist/test_referral_live.d.ts.map +1 -0
- package/dist/test_referral_live.js +33 -0
- package/dist/test_referral_live.js.map +1 -0
- package/dist/test_referral_piers.d.ts +2 -0
- package/dist/test_referral_piers.d.ts.map +1 -0
- package/dist/test_referral_piers.js +33 -0
- package/dist/test_referral_piers.js.map +1 -0
- package/dist/test_report_editorial_briefing.d.ts +21 -0
- package/dist/test_report_editorial_briefing.d.ts.map +1 -0
- package/dist/test_report_editorial_briefing.js +356 -0
- package/dist/test_report_editorial_briefing.js.map +1 -0
- package/dist/test_search_graph_quality.d.ts +2 -0
- package/dist/test_search_graph_quality.d.ts.map +1 -0
- package/dist/test_search_graph_quality.js +493 -0
- package/dist/test_search_graph_quality.js.map +1 -0
- package/dist/test_service_to_service_auth.d.ts +2 -0
- package/dist/test_service_to_service_auth.d.ts.map +1 -0
- package/dist/test_service_to_service_auth.js +199 -0
- package/dist/test_service_to_service_auth.js.map +1 -0
- package/dist/test_session_next_moves_telemetry.d.ts +2 -0
- package/dist/test_session_next_moves_telemetry.d.ts.map +1 -0
- package/dist/test_session_next_moves_telemetry.js +100 -0
- package/dist/test_session_next_moves_telemetry.js.map +1 -0
- package/dist/test_sources_live.d.ts +2 -0
- package/dist/test_sources_live.d.ts.map +1 -0
- package/dist/test_sources_live.js +30 -0
- package/dist/test_sources_live.js.map +1 -0
- package/dist/test_specialist_and_analyst_tiers.d.ts +2 -0
- package/dist/test_specialist_and_analyst_tiers.d.ts.map +1 -0
- package/dist/test_specialist_and_analyst_tiers.js +155 -0
- package/dist/test_specialist_and_analyst_tiers.js.map +1 -0
- package/dist/test_v2_2_visual_hardening.d.ts +5 -0
- package/dist/test_v2_2_visual_hardening.d.ts.map +1 -0
- package/dist/test_v2_2_visual_hardening.js +333 -0
- package/dist/test_v2_2_visual_hardening.js.map +1 -0
- package/dist/test_verify_deprecate_render_spec.d.ts +2 -0
- package/dist/test_verify_deprecate_render_spec.d.ts.map +1 -0
- package/dist/test_verify_deprecate_render_spec.js +217 -0
- package/dist/test_verify_deprecate_render_spec.js.map +1 -0
- package/dist/test_verify_neil_demo.d.ts +2 -0
- package/dist/test_verify_neil_demo.d.ts.map +1 -0
- package/dist/test_verify_neil_demo.js +149 -0
- package/dist/test_verify_neil_demo.js.map +1 -0
- package/dist/test_waitable_research_and_grounded_links.d.ts +2 -0
- package/dist/test_waitable_research_and_grounded_links.d.ts.map +1 -0
- package/dist/test_waitable_research_and_grounded_links.js +129 -0
- package/dist/test_waitable_research_and_grounded_links.js.map +1 -0
- package/dist/test_widget_guards.d.ts +2 -0
- package/dist/test_widget_guards.d.ts.map +1 -0
- package/dist/test_widget_guards.js +229 -0
- package/dist/test_widget_guards.js.map +1 -0
- package/dist/test_zero_retention_alerts.d.ts +14 -0
- package/dist/test_zero_retention_alerts.d.ts.map +1 -0
- package/dist/test_zero_retention_alerts.js +76 -0
- package/dist/test_zero_retention_alerts.js.map +1 -0
- package/dist/toolHandlers.d.ts +38 -1
- package/dist/toolHandlers.d.ts.map +1 -1
- package/dist/toolHandlers.js +4290 -711
- package/dist/toolHandlers.js.map +1 -1
- package/dist/tools.d.ts +22 -1
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +29 -4
- package/dist/tools.js.map +1 -1
- package/dist/verify_tools_endpoint.js +19 -5
- package/dist/verify_tools_endpoint.js.map +1 -1
- package/dist/widgetShell.d.ts +2 -2
- package/dist/widgetShell.d.ts.map +1 -1
- package/dist/widgetShell.js +1 -1
- package/dist/widgetShell.js.map +1 -1
- package/package.json +1 -1
- package/server.json +3 -31
- package/dist/debug_tools.d.ts +0 -2
- package/dist/debug_tools.d.ts.map +0 -1
- package/dist/debug_tools.js +0 -19
- package/dist/debug_tools.js.map +0 -1
- package/dist/inspect_list_graphs.d.ts +0 -2
- package/dist/inspect_list_graphs.d.ts.map +0 -1
- package/dist/inspect_list_graphs.js +0 -25
- package/dist/inspect_list_graphs.js.map +0 -1
- package/dist/scratch_test.d.ts.map +0 -1
- package/dist/scripts/simulate_gemini.d.ts +0 -2
- package/dist/scripts/simulate_gemini.d.ts.map +0 -1
- package/dist/scripts/simulate_gemini.js +0 -100
- package/dist/scripts/simulate_gemini.js.map +0 -1
- package/dist/scripts/verify_registry.d.ts +0 -2
- package/dist/scripts/verify_registry.d.ts.map +0 -1
- package/dist/scripts/verify_registry.js +0 -77
- package/dist/scripts/verify_registry.js.map +0 -1
- package/dist/src/index.d.ts +0 -2
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/index.js +0 -332
- package/dist/src/index.js.map +0 -1
- package/dist/src/tools.d.ts +0 -12
- package/dist/src/tools.d.ts.map +0 -1
- package/dist/src/tools.js +0 -104
- package/dist/src/tools.js.map +0 -1
- package/dist/src/types.d.ts +0 -44
- package/dist/src/types.d.ts.map +0 -1
- package/dist/src/types.js +0 -2
- package/dist/src/types.js.map +0 -1
- package/dist/src/verify.d.ts +0 -2
- package/dist/src/verify.d.ts.map +0 -1
- package/dist/src/verify.js +0 -47
- package/dist/src/verify.js.map +0 -1
- package/dist/src/verify_hmac.d.ts +0 -2
- package/dist/src/verify_hmac.d.ts.map +0 -1
- package/dist/src/verify_hmac.js +0 -106
- package/dist/src/verify_hmac.js.map +0 -1
- package/dist/src/verify_tools_endpoint.d.ts +0 -2
- package/dist/src/verify_tools_endpoint.d.ts.map +0 -1
- package/dist/src/verify_tools_endpoint.js +0 -40
- package/dist/src/verify_tools_endpoint.js.map +0 -1
- package/dist/test-waverunner-response.d.ts +0 -2
- package/dist/test-waverunner-response.d.ts.map +0 -1
- package/dist/test-waverunner-response.js +0 -20
- package/dist/test-waverunner-response.js.map +0 -1
- package/dist/test_coverage_r2.d.ts +0 -2
- package/dist/test_coverage_r2.d.ts.map +0 -1
- package/dist/test_coverage_scratch.d.ts +0 -2
- package/dist/test_coverage_scratch.d.ts.map +0 -1
package/dist/catalogCache.js
CHANGED
|
@@ -7,6 +7,100 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import axios from 'axios';
|
|
9
9
|
import { cacheClear } from './queryCache.js';
|
|
10
|
+
export function stringifyField(f) {
|
|
11
|
+
if (!f)
|
|
12
|
+
return '';
|
|
13
|
+
if (typeof f === 'string')
|
|
14
|
+
return f.trim();
|
|
15
|
+
if (Array.isArray(f)) {
|
|
16
|
+
return f.map(item => {
|
|
17
|
+
if (typeof item === 'string')
|
|
18
|
+
return item;
|
|
19
|
+
if (item && typeof item === 'object') {
|
|
20
|
+
return [item.title, item.name, item.description, item.topic].filter(Boolean).join(' ');
|
|
21
|
+
}
|
|
22
|
+
return '';
|
|
23
|
+
}).filter(Boolean).join('. ').trim();
|
|
24
|
+
}
|
|
25
|
+
if (typeof f === 'object') {
|
|
26
|
+
return [f.title, f.name, f.description, f.topic].filter(Boolean).join(' ').trim();
|
|
27
|
+
}
|
|
28
|
+
return String(f);
|
|
29
|
+
}
|
|
30
|
+
export function normalizeAnalyst(a) {
|
|
31
|
+
const analyst_id = String(a.analyst_id || a.id || a.slug || a.name || '').trim();
|
|
32
|
+
const name = cleanDisplayName(String(a.name || a.analyst_id || a.id || a.slug || '').trim());
|
|
33
|
+
const description = stringifyField(a.description || a.bio || a.lens || a.standpoint || '');
|
|
34
|
+
const expert_in = stringifyField(a.expert_in || a.expertIn || a.topic || '');
|
|
35
|
+
const outside_their_lane = stringifyField(a.outside_their_lane || a.outsideTheirLane || a.blind_spots || a.blindSpots || '');
|
|
36
|
+
const topics = Array.isArray(a.topics) ? a.topics : (typeof a.topics === 'string' ? a.topics.split(',').map((s) => s.trim()) : undefined);
|
|
37
|
+
const rawSubType = (a.graphSubType || a.graph_sub_type || a.subType || a.type || a.kind || a.agent_type || '').toString().trim();
|
|
38
|
+
const cleanId = analyst_id.toLowerCase();
|
|
39
|
+
// ── 4-Tier Agent Classification ──
|
|
40
|
+
// 1. C-Suite Agents: corporate executive personas grounded in SEC/earnings
|
|
41
|
+
const is_c_suite = cleanId === 'brand-cmo' || cleanId === 'brand-ceo' || cleanId === 'brand-cfo' ||
|
|
42
|
+
/brand-(cmo|ceo|cfo)/i.test(cleanId) || /c-suite|executive/i.test(rawSubType);
|
|
43
|
+
// 2. Classic Agents: public-domain historical thinkers providing philosophical lenses
|
|
44
|
+
const is_classic = !is_c_suite && (rawSubType === 'Classic Digital Twin' ||
|
|
45
|
+
/classic digital twin/i.test(rawSubType) ||
|
|
46
|
+
a.is_classic_agent === true ||
|
|
47
|
+
a.category === 'classic_agent');
|
|
48
|
+
// 3. Human Agents: real living industry figures with consented digital twins
|
|
49
|
+
const is_human = !is_c_suite && !is_classic && Boolean(rawSubType === 'Digital Twin' ||
|
|
50
|
+
rawSubType === 'human_agent' ||
|
|
51
|
+
rawSubType === 'human_twin' ||
|
|
52
|
+
rawSubType === 'expert_twin' ||
|
|
53
|
+
a.is_human_agent ||
|
|
54
|
+
a.is_digital_twin ||
|
|
55
|
+
a.isVerifiedRealPerson ||
|
|
56
|
+
a.is_verified_real_person ||
|
|
57
|
+
a.type === 'human_agent' ||
|
|
58
|
+
a.type === 'human_twin' ||
|
|
59
|
+
a.kind === 'human_agent' ||
|
|
60
|
+
a.kind === 'human_twin' ||
|
|
61
|
+
a.agent_type === 'human_agent' ||
|
|
62
|
+
a.agent_type === 'human_twin');
|
|
63
|
+
let category = 'synthetic_agent';
|
|
64
|
+
let category_label = 'Synthetic Agent';
|
|
65
|
+
let twin_type = undefined;
|
|
66
|
+
if (is_human) {
|
|
67
|
+
category = 'human_agent';
|
|
68
|
+
category_label = 'Human Agent';
|
|
69
|
+
twin_type = 'Expert Digital Twin';
|
|
70
|
+
}
|
|
71
|
+
else if (is_classic) {
|
|
72
|
+
category = 'classic_agent';
|
|
73
|
+
category_label = 'Classic Agent';
|
|
74
|
+
twin_type = 'Classic Agent';
|
|
75
|
+
}
|
|
76
|
+
else if (is_c_suite) {
|
|
77
|
+
category = 'c_suite_agent';
|
|
78
|
+
category_label = 'C-Suite Agent';
|
|
79
|
+
}
|
|
80
|
+
const slug = a.expertSlug || a.expert_slug || a.slug || a.analyst_id || (is_human ? a.id : undefined);
|
|
81
|
+
const consult_tool = is_human ? 'consult_human_agent' : 'consult_analyst';
|
|
82
|
+
return {
|
|
83
|
+
...a,
|
|
84
|
+
analyst_id,
|
|
85
|
+
name,
|
|
86
|
+
description,
|
|
87
|
+
category,
|
|
88
|
+
category_label,
|
|
89
|
+
...(twin_type ? { twin_type } : {}),
|
|
90
|
+
type: category,
|
|
91
|
+
is_human_agent: is_human,
|
|
92
|
+
is_classic_agent: is_classic,
|
|
93
|
+
is_c_suite_agent: is_c_suite,
|
|
94
|
+
is_verified_real_person: is_human,
|
|
95
|
+
consult_tool,
|
|
96
|
+
slug: slug ? String(slug).trim() : undefined,
|
|
97
|
+
...(expert_in ? { expert_in } : {}),
|
|
98
|
+
...(outside_their_lane ? { outside_their_lane } : {}),
|
|
99
|
+
...(topics ? { topics } : {}),
|
|
100
|
+
...(a.what_they_offer || a.whatTheyOffer || a.askLine ? { what_they_offer: stringifyField(a.what_they_offer || a.whatTheyOffer || a.askLine) } : {}),
|
|
101
|
+
...(a.example_questions || a.exampleQuestions || a.exampleQueries ? { example_questions: a.example_questions || a.exampleQuestions || a.exampleQueries } : {})
|
|
102
|
+
};
|
|
103
|
+
}
|
|
10
104
|
// ---------------------------------------------------------------------------
|
|
11
105
|
// Cache
|
|
12
106
|
// ---------------------------------------------------------------------------
|
|
@@ -34,18 +128,103 @@ async function fetchCatalog() {
|
|
|
34
128
|
* Fetch the analysts from the API. Requires internal auth.
|
|
35
129
|
*/
|
|
36
130
|
async function fetchAnalysts() {
|
|
37
|
-
const url = `${API_BASE_URL}/v1/analysts`;
|
|
38
|
-
const internalKey = process.env.FODDA_INTERNAL_API_KEY;
|
|
131
|
+
const url = `${API_BASE_URL}/v1/analysts?include=unclaimed`;
|
|
132
|
+
const internalKey = process.env.FODDA_INTERNAL_API_KEY || process.env.FODDA_API_KEY;
|
|
39
133
|
const headers = internalKey ? { 'Authorization': `Bearer ${internalKey}` } : {};
|
|
40
134
|
try {
|
|
41
135
|
const response = await axios.get(url, { headers, timeout: 10000 });
|
|
42
|
-
|
|
136
|
+
const raw = Array.isArray(response.data) ? response.data : (response.data.analysts || []);
|
|
137
|
+
return raw.map(normalizeAnalyst);
|
|
43
138
|
}
|
|
44
139
|
catch (err) {
|
|
45
140
|
console.error(`[catalogCache] Failed to fetch analysts: ${err.message}`);
|
|
46
141
|
return [];
|
|
47
142
|
}
|
|
48
143
|
}
|
|
144
|
+
/**
|
|
145
|
+
* Bridges active individual analysts into the searchable graph catalog.
|
|
146
|
+
* Solves the "two-store indexing gap" where an analyst exists in the Analysts table
|
|
147
|
+
* with a backing Neo4j graph, but has no dedicated record in Airtable's Graph List.
|
|
148
|
+
*/
|
|
149
|
+
function bridgeAnalystGraphs(catalog, analysts) {
|
|
150
|
+
if (!catalog?.graphs)
|
|
151
|
+
return catalog;
|
|
152
|
+
const existingIds = new Set(catalog.graphs.map(g => (g.graph_id || '').toLowerCase().trim()));
|
|
153
|
+
const bridged = [...catalog.graphs];
|
|
154
|
+
for (const a of analysts) {
|
|
155
|
+
const id = (a.analyst_id || a.id || '').toLowerCase().trim();
|
|
156
|
+
const status = (a.status || a.Status || '').toLowerCase().trim();
|
|
157
|
+
if (!id || (status && status !== 'active'))
|
|
158
|
+
continue;
|
|
159
|
+
// Skip synthetic C-suite leads that already span all graphs
|
|
160
|
+
if (a.is_c_suite_agent || /brand-(cmo|ceo|cfo|analyst)/i.test(id))
|
|
161
|
+
continue;
|
|
162
|
+
// Determine target graph IDs for this analyst:
|
|
163
|
+
// Check backingGraphs first (e.g. 'postpals-expert-graph', 'sic', '2026-macro-trend-graph')
|
|
164
|
+
const candidateGraphIds = new Set();
|
|
165
|
+
const backingList = Array.isArray(a.backingGraphs) ? a.backingGraphs : [];
|
|
166
|
+
for (const rawBg of backingList) {
|
|
167
|
+
const bg = (rawBg || '').toLowerCase().trim();
|
|
168
|
+
if (bg && bg !== '*' && !existingIds.has(bg)) {
|
|
169
|
+
candidateGraphIds.add(bg);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// If no un-registered backingGraph found, use the analyst ID itself (e.g. 'peter-abraham-bicycles-cycling')
|
|
173
|
+
if (candidateGraphIds.size === 0 && !existingIds.has(id) && !backingList.includes('*')) {
|
|
174
|
+
candidateGraphIds.add(id);
|
|
175
|
+
}
|
|
176
|
+
if (candidateGraphIds.size === 0)
|
|
177
|
+
continue;
|
|
178
|
+
const topics = Array.isArray(a.topics) ? a.topics : [];
|
|
179
|
+
const expertIn = typeof a.expert_in === 'string' ? a.expert_in : '';
|
|
180
|
+
const desc = a.description || a.what_they_offer || expertIn || `${a.name} expert knowledge graph`;
|
|
181
|
+
for (const targetGraphId of candidateGraphIds) {
|
|
182
|
+
bridged.push({
|
|
183
|
+
graph_id: targetGraphId,
|
|
184
|
+
name: a.name,
|
|
185
|
+
description: desc,
|
|
186
|
+
curator: a.name,
|
|
187
|
+
curator_url: '',
|
|
188
|
+
quality_checker_name: '',
|
|
189
|
+
quality_checker_title: '',
|
|
190
|
+
quality_checker_company: '',
|
|
191
|
+
update_frequency: 'Weekly',
|
|
192
|
+
domain: expertIn || 'Specialist Intelligence',
|
|
193
|
+
version: '1.0.0',
|
|
194
|
+
node_types: ['Trend', 'Article'],
|
|
195
|
+
relationship_types: ['HAS_EVIDENCE'],
|
|
196
|
+
graph_type: 'analyst',
|
|
197
|
+
status: 'live',
|
|
198
|
+
trend_count: 5,
|
|
199
|
+
evidence_count: 5,
|
|
200
|
+
last_synced: new Date().toISOString(),
|
|
201
|
+
topics: topics.length > 0 ? topics : [targetGraphId],
|
|
202
|
+
company: a.company || '',
|
|
203
|
+
headline: a.askLine || expertIn || desc,
|
|
204
|
+
subhead: expertIn || '',
|
|
205
|
+
geography: 'Global',
|
|
206
|
+
icon_url: '',
|
|
207
|
+
source_url: '',
|
|
208
|
+
available_as: 'API, MCP',
|
|
209
|
+
is_playground: false,
|
|
210
|
+
last_updated: new Date().toISOString(),
|
|
211
|
+
published_date: null,
|
|
212
|
+
example_queries: Array.isArray(a.example_questions) ? a.example_questions : [],
|
|
213
|
+
portrait_url: a.portrait_url || '',
|
|
214
|
+
core_tension: null,
|
|
215
|
+
matched_human_twin_slug: id,
|
|
216
|
+
expert_stance_quote: null,
|
|
217
|
+
cross_graph_signals: []
|
|
218
|
+
});
|
|
219
|
+
existingIds.add(targetGraphId);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return {
|
|
223
|
+
...catalog,
|
|
224
|
+
graph_count: bridged.length,
|
|
225
|
+
graphs: bridged
|
|
226
|
+
};
|
|
227
|
+
}
|
|
49
228
|
/**
|
|
50
229
|
* Initialize the catalog cache. Call once at server startup.
|
|
51
230
|
* Fetches immediately, then sets up hourly refresh.
|
|
@@ -53,8 +232,9 @@ async function fetchAnalysts() {
|
|
|
53
232
|
export async function initCatalogCache() {
|
|
54
233
|
try {
|
|
55
234
|
cacheClear();
|
|
56
|
-
|
|
235
|
+
const rawCatalog = await fetchCatalog();
|
|
57
236
|
cachedAnalysts = await fetchAnalysts();
|
|
237
|
+
cachedCatalog = bridgeAnalystGraphs(rawCatalog, cachedAnalysts);
|
|
58
238
|
lastFetchedAt = Date.now();
|
|
59
239
|
rebuildSearchIndex();
|
|
60
240
|
console.error(`[catalogCache] Loaded ${cachedCatalog.graph_count} graphs from catalog (search index: ${graphSearchTexts.size} entries)`);
|
|
@@ -68,8 +248,9 @@ export async function initCatalogCache() {
|
|
|
68
248
|
clearInterval(refreshInterval);
|
|
69
249
|
refreshInterval = setInterval(async () => {
|
|
70
250
|
try {
|
|
71
|
-
|
|
251
|
+
const rawCatalog = await fetchCatalog();
|
|
72
252
|
cachedAnalysts = await fetchAnalysts();
|
|
253
|
+
cachedCatalog = bridgeAnalystGraphs(rawCatalog, cachedAnalysts);
|
|
73
254
|
lastFetchedAt = Date.now();
|
|
74
255
|
rebuildSearchIndex();
|
|
75
256
|
console.error(`[catalogCache] Refreshed catalog — ${cachedCatalog.graph_count} graphs (search index rebuilt) and ${cachedAnalysts.length} analysts`);
|
|
@@ -79,6 +260,13 @@ export async function initCatalogCache() {
|
|
|
79
260
|
}
|
|
80
261
|
}, CATALOG_REFRESH_MS);
|
|
81
262
|
}
|
|
263
|
+
export function setCachedCatalogForTesting(catalog, analysts) {
|
|
264
|
+
if (analysts)
|
|
265
|
+
cachedAnalysts = analysts.map(normalizeAnalyst);
|
|
266
|
+
cachedCatalog = bridgeAnalystGraphs(catalog, cachedAnalysts);
|
|
267
|
+
lastFetchedAt = Date.now();
|
|
268
|
+
rebuildSearchIndex();
|
|
269
|
+
}
|
|
82
270
|
/**
|
|
83
271
|
* Get the cached catalog. Returns null if not yet fetched.
|
|
84
272
|
*/
|
|
@@ -185,6 +373,18 @@ export function buildDisplayName(g) {
|
|
|
185
373
|
}
|
|
186
374
|
return g.name;
|
|
187
375
|
}
|
|
376
|
+
/**
|
|
377
|
+
* Clean display names across MCP surfaces.
|
|
378
|
+
* Removes retired suffixes such as "(Classic Digital Twin)", "(Digital Twin)", etc.
|
|
379
|
+
*/
|
|
380
|
+
export function cleanDisplayName(name) {
|
|
381
|
+
if (!name || typeof name !== 'string')
|
|
382
|
+
return '';
|
|
383
|
+
return name
|
|
384
|
+
.replace(/\s*[\(\[]\s*(?:Classic\s+|Expert\s+|Human\s+)?(?:Digital\s+)?Twin\s*[\)\]]/gi, '')
|
|
385
|
+
.replace(/\s*[\(\[]\s*(?:Classic|Human|Synthetic|C-Suite)\s+Agent\s*[\)\]]/gi, '')
|
|
386
|
+
.trim();
|
|
387
|
+
}
|
|
188
388
|
/**
|
|
189
389
|
* Build the GRAPH TYPES block from catalog data.
|
|
190
390
|
*/
|
|
@@ -585,8 +785,30 @@ const QUERY_EXPANSION_MAP = {
|
|
|
585
785
|
// Pet & Animal Care
|
|
586
786
|
pet: ['pet', 'cpg', 'food', 'wellness', 'retail'],
|
|
587
787
|
pets: ['pet', 'cpg', 'food', 'wellness', 'retail'],
|
|
588
|
-
dog: ['pet', 'cpg', 'food', 'wellness'],
|
|
589
|
-
|
|
788
|
+
dog: ['pet', 'cpg', 'food', 'wellness', 'retail'],
|
|
789
|
+
dogs: ['pet', 'cpg', 'food', 'wellness', 'retail'],
|
|
790
|
+
cat: ['pet', 'cpg', 'food', 'wellness', 'retail'],
|
|
791
|
+
cats: ['pet', 'cpg', 'food', 'wellness', 'retail'],
|
|
792
|
+
petfood: ['pet', 'cpg', 'food', 'wellness', 'retail'],
|
|
793
|
+
// Marketing, Experiential & Activations
|
|
794
|
+
activation: ['retail', 'cpg', 'marketing', 'brand', 'experience', 'hospitality', 'culture'],
|
|
795
|
+
activations: ['retail', 'cpg', 'marketing', 'brand', 'experience', 'hospitality', 'culture'],
|
|
796
|
+
experiential: ['retail', 'cpg', 'marketing', 'brand', 'experience', 'hospitality', 'culture', 'design'],
|
|
797
|
+
popup: ['retail', 'cpg', 'fashion', 'experience', 'design'],
|
|
798
|
+
popups: ['retail', 'cpg', 'fashion', 'experience', 'design'],
|
|
799
|
+
sampling: ['cpg', 'retail', 'food', 'beverage', 'beauty', 'experience'],
|
|
800
|
+
sponsorship: ['marketing', 'brand', 'culture', 'retail', 'entertainment'],
|
|
801
|
+
installation: ['retail', 'design', 'art', 'experience', 'culture'],
|
|
802
|
+
flagship: ['retail', 'fashion', 'design', 'experience', 'brand'],
|
|
803
|
+
immersive: ['retail', 'experience', 'design', 'entertainment', 'technology'],
|
|
804
|
+
stunt: ['marketing', 'brand', 'culture', 'retail'],
|
|
805
|
+
showcase: ['retail', 'design', 'experience', 'marketing'],
|
|
806
|
+
marketing: ['retail', 'cpg', 'brand', 'strategy', 'advertising', 'culture'],
|
|
807
|
+
campaign: ['marketing', 'brand', 'advertising', 'retail', 'cpg'],
|
|
808
|
+
advertising: ['marketing', 'brand', 'media', 'retail', 'cpg'],
|
|
809
|
+
promo: ['marketing', 'retail', 'cpg', 'promotion'],
|
|
810
|
+
promotion: ['marketing', 'retail', 'cpg', 'promotion'],
|
|
811
|
+
hospitality: ['hospitality', 'hotel', 'dining', 'food', 'experience', 'leisure', 'culture'],
|
|
590
812
|
// Tech, AI & SaaS
|
|
591
813
|
ai: ['technology', 'ai', 'software', 'enterprise', 'innovation'],
|
|
592
814
|
saas: ['technology', 'software', 'enterprise', 'b2b'],
|
|
@@ -616,12 +838,15 @@ function scoreClauseRelevance(clause, g) {
|
|
|
616
838
|
const searchText = graphSearchTexts.get(g.graph_id) || buildSearchableText(g);
|
|
617
839
|
const topicsText = [...(g.topics || []), ...(g.routing_keywords || []), g.name || '', g.graph_id || ''].join(' ').toLowerCase();
|
|
618
840
|
const domainText = (g.domain || '').toLowerCase();
|
|
841
|
+
const words = new Set(searchText.split(/[^a-z0-9]+/));
|
|
842
|
+
const topicWords = new Set(topicsText.split(/[^a-z0-9]+/));
|
|
843
|
+
const domainWords = new Set(domainText.split(/[^a-z0-9]+/));
|
|
619
844
|
let matchedTerms = 0;
|
|
620
845
|
let highValueMatches = 0;
|
|
621
846
|
for (const term of terms) {
|
|
622
|
-
if (
|
|
847
|
+
if (words.has(term)) {
|
|
623
848
|
matchedTerms++;
|
|
624
|
-
if (
|
|
849
|
+
if (topicWords.has(term) || domainWords.has(term)) {
|
|
625
850
|
highValueMatches++;
|
|
626
851
|
}
|
|
627
852
|
}
|
|
@@ -642,9 +867,9 @@ function scoreClauseRelevance(clause, g) {
|
|
|
642
867
|
let expMatches = 0;
|
|
643
868
|
let expHighValueMatches = 0;
|
|
644
869
|
for (const exp of expandedSet) {
|
|
645
|
-
if (
|
|
870
|
+
if (words.has(exp)) {
|
|
646
871
|
expMatches++;
|
|
647
|
-
if (
|
|
872
|
+
if (topicWords.has(exp) || domainWords.has(exp)) {
|
|
648
873
|
expHighValueMatches++;
|
|
649
874
|
}
|
|
650
875
|
}
|
|
@@ -679,6 +904,18 @@ function scoreClauseRelevance(clause, g) {
|
|
|
679
904
|
finalScore = Math.min(finalScore, 0.40); // Cap generic agency floor
|
|
680
905
|
}
|
|
681
906
|
}
|
|
907
|
+
// Experiential & Activation Tactic Boost
|
|
908
|
+
const experientialTerms = new Set([
|
|
909
|
+
'activation', 'activations', 'experiential', 'popup', 'popups', 'sampling',
|
|
910
|
+
'installation', 'flagship', 'immersive', 'stunt', 'showcase'
|
|
911
|
+
]);
|
|
912
|
+
const queryHasExperiential = terms.some(t => experientialTerms.has(t));
|
|
913
|
+
if (queryHasExperiential) {
|
|
914
|
+
const isExperienceGraph = /retail|cpg|brand|marketing|experience|culture|hospitality/i.test(g.graph_id + ' ' + (g.domain || '') + ' ' + (g.topics || []).join(' ') + ' ' + (g.routing_keywords || []).join(' '));
|
|
915
|
+
if (isExperienceGraph || g.graph_id === 'retail') {
|
|
916
|
+
finalScore += 0.20; // Experiential Format Boost
|
|
917
|
+
}
|
|
918
|
+
}
|
|
682
919
|
// Generalized Narrow-Domain Mismatch Penalty
|
|
683
920
|
const narrowDomains = ['beauty', 'gaming', 'automotive', 'fashion'];
|
|
684
921
|
for (const dom of narrowDomains) {
|
|
@@ -716,10 +953,11 @@ export function classifyGraphTier(g) {
|
|
|
716
953
|
const isRecurring = g.update_frequency &&
|
|
717
954
|
!['One-Off', 'Coming Soon'].includes(g.update_frequency);
|
|
718
955
|
// Domain graphs with recurring updates + experts with recurring updates = Living
|
|
719
|
-
|
|
956
|
+
// ('analyst' = expert graphs typed 'analyst' in the registry, e.g. Digital Twins)
|
|
957
|
+
if ((g.graph_type === 'domain' || g.graph_type === 'expert' || g.graph_type === 'analyst') && isRecurring) {
|
|
720
958
|
return 'living';
|
|
721
959
|
}
|
|
722
|
-
if (g.graph_type === 'expert')
|
|
960
|
+
if (g.graph_type === 'expert' || g.graph_type === 'analyst')
|
|
723
961
|
return 'static_expert';
|
|
724
962
|
if (g.graph_type === 'industry report')
|
|
725
963
|
return 'report';
|
|
@@ -740,8 +978,18 @@ export function classifyGraphTier(g) {
|
|
|
740
978
|
*/
|
|
741
979
|
export function getRelevantGraphs(query, minGraphs = 4, maxGraphs = 15, threshold = 0.10) {
|
|
742
980
|
const DEPRECATED_GRAPH_IDS = new Set(['waldo', 'psfk']);
|
|
743
|
-
|
|
744
|
-
|
|
981
|
+
// 'analyst' graphs (e.g. Digital Twin expert graphs typed 'analyst' in the
|
|
982
|
+
// registry) are searchable expert content — excluding them made registry-live
|
|
983
|
+
// graphs like jeff-longevity invisible to routing (QA 2026-08-20).
|
|
984
|
+
const ROUTABLE_TYPES = new Set(['domain', 'expert', 'industry report', 'analyst']);
|
|
985
|
+
const allLive = getLiveGraphs();
|
|
986
|
+
const allGraphs = allLive.filter(g => ROUTABLE_TYPES.has(g.graph_type) && !DEPRECATED_GRAPH_IDS.has(g.graph_id));
|
|
987
|
+
// Registry-status visibility: surface live graphs excluded from routing by
|
|
988
|
+
// type, so an unroutable analyst/expert graph is detectable in logs.
|
|
989
|
+
const typeExcluded = allLive.filter(g => !ROUTABLE_TYPES.has(g.graph_type) && g.graph_type !== 'supplemental' && g.graph_type !== 'skill');
|
|
990
|
+
if (typeExcluded.length > 0) {
|
|
991
|
+
console.error(`[graphRouter] ${typeExcluded.length} live graph(s) excluded by graph_type: ${typeExcluded.map(g => `${g.graph_id}(${g.graph_type})`).join(', ')}`);
|
|
992
|
+
}
|
|
745
993
|
if (allGraphs.length === 0)
|
|
746
994
|
return [];
|
|
747
995
|
// Runtime check: skip graphs that have no synced content in Neo4j.
|
|
@@ -750,13 +998,35 @@ export function getRelevantGraphs(query, minGraphs = 4, maxGraphs = 15, threshol
|
|
|
750
998
|
// Once the CE pipeline syncs them, they'll automatically start routing.
|
|
751
999
|
const hasContent = (g) => (g.trend_count > 0) || (g.last_synced !== null && g.last_synced !== undefined);
|
|
752
1000
|
const syncedGraphs = allGraphs.filter(hasContent);
|
|
753
|
-
const
|
|
1001
|
+
const shellGraphs = allGraphs.filter(g => !hasContent(g));
|
|
1002
|
+
const skippedShells = shellGraphs.length;
|
|
754
1003
|
if (skippedShells > 0) {
|
|
755
|
-
console.error(`[graphRouter] Skipped ${skippedShells} unsynced graph shell(s) (trend_count=0, last_synced=null)`);
|
|
1004
|
+
console.error(`[graphRouter] Skipped ${skippedShells} unsynced graph shell(s) (trend_count=0, last_synced=null): ${shellGraphs.map(g => g.graph_id).join(', ')}`);
|
|
756
1005
|
}
|
|
757
|
-
// ── Phase 0: Direct name matching ──
|
|
1006
|
+
// ── Phase 0: Direct name matching & phrase routing overrides ──
|
|
758
1007
|
const queryLower = query.toLowerCase();
|
|
759
1008
|
const directMatchIds = new Set();
|
|
1009
|
+
const isCreatorQuery = /creator economy|content creator|creator monetization|creators report/i.test(queryLower);
|
|
1010
|
+
const isNightlifeQuery = /nightlife|going out|shared experiences|shared social experiences/i.test(queryLower);
|
|
1011
|
+
const eligibleGraphs = syncedGraphs.filter(g => {
|
|
1012
|
+
if (isCreatorQuery && g.graph_id === 'mary-shelley')
|
|
1013
|
+
return false;
|
|
1014
|
+
return true;
|
|
1015
|
+
});
|
|
1016
|
+
if (isCreatorQuery) {
|
|
1017
|
+
for (const g of eligibleGraphs) {
|
|
1018
|
+
if (g.graph_id === 'visa-creators_report-2025' || g.graph_id.includes('youtube') || g.graph_id.includes('tiktok')) {
|
|
1019
|
+
directMatchIds.add(g.graph_id);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
if (isNightlifeQuery) {
|
|
1024
|
+
for (const g of eligibleGraphs) {
|
|
1025
|
+
if (g.graph_id.includes('bompasparr')) {
|
|
1026
|
+
directMatchIds.add(g.graph_id);
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
760
1030
|
const matchesWordBoundary = (text, term) => {
|
|
761
1031
|
if (!term || term.length < 3)
|
|
762
1032
|
return false;
|
|
@@ -771,7 +1041,7 @@ export function getRelevantGraphs(query, minGraphs = 4, maxGraphs = 15, threshol
|
|
|
771
1041
|
const rx = new RegExp(`\\b${escaped}\\b`, 'i');
|
|
772
1042
|
return rx.test(text);
|
|
773
1043
|
};
|
|
774
|
-
for (const g of
|
|
1044
|
+
for (const g of eligibleGraphs) {
|
|
775
1045
|
// Check graph_id (e.g., "alyson-stevens-macro", "sic")
|
|
776
1046
|
if (g.graph_id.length > 3 && matchesWordBoundary(queryLower, g.graph_id)) {
|
|
777
1047
|
directMatchIds.add(g.graph_id);
|
|
@@ -813,7 +1083,7 @@ export function getRelevantGraphs(query, minGraphs = 4, maxGraphs = 15, threshol
|
|
|
813
1083
|
console.error(`[graphRouter] Phase 0 direct name matches: ${[...directMatchIds].join(', ')}`);
|
|
814
1084
|
}
|
|
815
1085
|
// Score ALL graph types together — domain, expert, industry report compete on merit
|
|
816
|
-
const scored =
|
|
1086
|
+
const scored = eligibleGraphs.map(g => {
|
|
817
1087
|
const isDirect = directMatchIds.has(g.graph_id);
|
|
818
1088
|
return {
|
|
819
1089
|
graph: g,
|
|
@@ -907,11 +1177,12 @@ const EARNINGS_SECTORS = ['retail', 'technology', 'travel', 'hospitality', 'luxu
|
|
|
907
1177
|
// (economic indicators/FRED, demographic surveys/Census, trade data, food
|
|
908
1178
|
// economics + agricultural production, commodity pricing).
|
|
909
1179
|
const SUPPLEMENTAL_CATEGORY_KEYWORDS = {
|
|
910
|
-
macro: ['macro', 'economy', 'economic', 'inflation', 'gdp', 'interest rate', 'consumer confidence', 'consumer spending', 'recession', 'fred', 'cost of living', 'purchasing power', 'unemployment'],
|
|
911
|
-
demographics: ['demographic', 'demographics', 'population', 'census', 'household income', 'age distribution', 'migration'],
|
|
1180
|
+
macro: ['macro', 'economy', 'economic', 'inflation', 'gdp', 'interest rate', 'consumer confidence', 'consumer sentiment', 'michigan sentiment', 'umcsent', 'consumer spending', 'recession', 'fred', 'cost of living', 'purchasing power', 'unemployment'],
|
|
1181
|
+
demographics: ['demographic', 'demographics', 'population', 'census', 'household income', 'age distribution', 'migration', 'republican', 'democrat', 'bipartisan', 'party affiliation', 'pew', 'npors'],
|
|
912
1182
|
trade: ['imports', 'exports', 'trade data', 'trade flows', 'tariff', 'tariffs', 'supply chain'],
|
|
913
1183
|
food_economics: ['food prices', 'food costs', 'crop', 'agriculture', 'agricultural', 'nutrition', 'nutritional'],
|
|
914
1184
|
commodities: ['commodity', 'commodities', 'raw materials', 'wholesale prices'],
|
|
1185
|
+
science: ['science', 'scientific', 'clinical', 'pubmed', 'openalex', 'clinical trials', 'dermatology', 'peer-reviewed', 'academic', 'study', 'studies', 'journal', 'biomedical'],
|
|
915
1186
|
};
|
|
916
1187
|
/**
|
|
917
1188
|
* Detect an earnings candidate for a query. Deterministic and conservative:
|