fodda-mcp 1.7.0 → 1.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/LICENSE +31 -0
  2. package/README.md +176 -18
  3. package/dist/a2aHandler.d.ts +20 -0
  4. package/dist/a2aHandler.d.ts.map +1 -0
  5. package/dist/a2aHandler.js +247 -0
  6. package/dist/a2aHandler.js.map +1 -0
  7. package/dist/agents/fodda-researcher/index.d.ts +35 -0
  8. package/dist/agents/fodda-researcher/index.d.ts.map +1 -0
  9. package/dist/agents/fodda-researcher/index.js +62 -0
  10. package/dist/agents/fodda-researcher/index.js.map +1 -0
  11. package/dist/agents/fodda-researcher/skills.d.ts +16 -0
  12. package/dist/agents/fodda-researcher/skills.d.ts.map +1 -0
  13. package/dist/agents/fodda-researcher/skills.js +163 -0
  14. package/dist/agents/fodda-researcher/skills.js.map +1 -0
  15. package/dist/brandTemplate.d.ts +14 -0
  16. package/dist/brandTemplate.d.ts.map +1 -0
  17. package/dist/brandTemplate.js +742 -0
  18. package/dist/brandTemplate.js.map +1 -0
  19. package/dist/catalogCache.d.ts +177 -0
  20. package/dist/catalogCache.d.ts.map +1 -0
  21. package/dist/catalogCache.js +708 -0
  22. package/dist/catalogCache.js.map +1 -0
  23. package/dist/editorialFill.d.ts +44 -0
  24. package/dist/editorialFill.d.ts.map +1 -0
  25. package/dist/editorialFill.js +377 -0
  26. package/dist/editorialFill.js.map +1 -0
  27. package/dist/enrichment.d.ts +74 -0
  28. package/dist/enrichment.d.ts.map +1 -0
  29. package/dist/enrichment.js +211 -0
  30. package/dist/enrichment.js.map +1 -0
  31. package/dist/errorHandling.d.ts +52 -0
  32. package/dist/errorHandling.d.ts.map +1 -0
  33. package/dist/errorHandling.js +327 -0
  34. package/dist/errorHandling.js.map +1 -0
  35. package/dist/index.d.ts +0 -1
  36. package/dist/index.js +595 -581
  37. package/dist/index.js.map +1 -1
  38. package/dist/pricingCache.d.ts +99 -0
  39. package/dist/pricingCache.d.ts.map +1 -0
  40. package/dist/pricingCache.js +414 -0
  41. package/dist/pricingCache.js.map +1 -0
  42. package/dist/queryCache.d.ts +25 -0
  43. package/dist/queryCache.d.ts.map +1 -0
  44. package/dist/queryCache.js +194 -0
  45. package/dist/queryCache.js.map +1 -0
  46. package/dist/searchTemplate.d.ts +16 -0
  47. package/dist/searchTemplate.d.ts.map +1 -0
  48. package/dist/searchTemplate.js +318 -0
  49. package/dist/searchTemplate.js.map +1 -0
  50. package/dist/sessionTracker.d.ts +39 -0
  51. package/dist/sessionTracker.d.ts.map +1 -0
  52. package/dist/sessionTracker.js +187 -0
  53. package/dist/sessionTracker.js.map +1 -0
  54. package/dist/skillClient.d.ts +142 -0
  55. package/dist/skillClient.d.ts.map +1 -0
  56. package/dist/skillClient.js +256 -0
  57. package/dist/skillClient.js.map +1 -0
  58. package/dist/svgVisuals.d.ts +77 -0
  59. package/dist/svgVisuals.d.ts.map +1 -0
  60. package/dist/svgVisuals.js +323 -0
  61. package/dist/svgVisuals.js.map +1 -0
  62. package/dist/systemPrompt.d.ts +26 -0
  63. package/dist/systemPrompt.d.ts.map +1 -0
  64. package/dist/systemPrompt.js +546 -0
  65. package/dist/systemPrompt.js.map +1 -0
  66. package/dist/test-waverunner-response.d.ts +2 -0
  67. package/dist/test-waverunner-response.d.ts.map +1 -0
  68. package/dist/test-waverunner-response.js +20 -0
  69. package/dist/test-waverunner-response.js.map +1 -0
  70. package/dist/test_analyst.d.ts +2 -0
  71. package/dist/test_analyst.d.ts.map +1 -0
  72. package/dist/test_analyst.js +39 -0
  73. package/dist/test_analyst.js.map +1 -0
  74. package/dist/test_analyst_local.d.ts +2 -0
  75. package/dist/test_analyst_local.d.ts.map +1 -0
  76. package/dist/test_analyst_local.js +51 -0
  77. package/dist/test_analyst_local.js.map +1 -0
  78. package/dist/test_live_mcp.js +1 -1
  79. package/dist/test_skills.d.ts +9 -0
  80. package/dist/test_skills.d.ts.map +1 -0
  81. package/dist/test_skills.js +267 -0
  82. package/dist/test_skills.js.map +1 -0
  83. package/dist/toolHandlers.d.ts +11 -0
  84. package/dist/toolHandlers.d.ts.map +1 -0
  85. package/dist/toolHandlers.js +2829 -0
  86. package/dist/toolHandlers.js.map +1 -0
  87. package/dist/tools.d.ts +78 -3
  88. package/dist/tools.d.ts.map +1 -1
  89. package/dist/tools.js +85 -292
  90. package/dist/tools.js.map +1 -1
  91. package/dist/trialTracker.d.ts +58 -0
  92. package/dist/trialTracker.d.ts.map +1 -0
  93. package/dist/trialTracker.js +179 -0
  94. package/dist/trialTracker.js.map +1 -0
  95. package/dist/types.d.ts +1 -0
  96. package/dist/types.d.ts.map +1 -1
  97. package/dist/verify_hmac.js +1 -1
  98. package/dist/verify_hmac.js.map +1 -1
  99. package/dist/verify_tools_endpoint.js +5 -5
  100. package/dist/verify_tools_endpoint.js.map +1 -1
  101. package/dist/widgetShell.d.ts +15 -0
  102. package/dist/widgetShell.d.ts.map +1 -0
  103. package/dist/widgetShell.js +151 -0
  104. package/dist/widgetShell.js.map +1 -0
  105. package/package.json +8 -4
  106. package/server.json +11 -2
package/dist/tools.js CHANGED
@@ -1,301 +1,94 @@
1
- export const MCP_SERVER_VERSION = "1.7.0";
1
+ /**
2
+ * Tool Metadata — version constants for the Fodda MCP server.
3
+ *
4
+ * The actual tool registrations live in toolHandlers.ts.
5
+ * This file provides version strings used by index.ts and toolHandlers.ts.
6
+ */
7
+ export const MCP_SERVER_VERSION = "1.30.0";
2
8
  export const TOOL_VERSIONS = {
3
- list_graphs: "1.0.0",
4
- search_graph: "1.2.0",
9
+ list_graphs: "1.4.0",
10
+ search_graph: "1.4.0",
5
11
  get_neighbors: "1.2.0",
6
12
  get_evidence: "1.1.0",
7
13
  get_node: "1.0.0",
8
14
  get_label_values: "1.2.0",
9
15
  psfk_overview: "1.0.0",
10
16
  discover_adjacent_trends: "1.0.0",
17
+ brand_tracker: "1.0.0",
18
+ get_census_retail_snapshot: "1.0.0",
19
+ get_census_demographics_snapshot: "1.1.0",
20
+ get_fred_economic_snapshot: "1.0.0",
21
+ get_wikipedia_pageviews: "1.0.0",
22
+ get_worldbank_global_snapshot: "1.1.0",
23
+ get_fda_ingredient_safety: "1.0.0",
24
+ get_fda_recalls: "1.0.0",
25
+ get_clinical_trials: "1.0.0",
26
+ get_bls_economic_snapshot: "1.0.0",
27
+ get_bea_spending_snapshot: "1.0.0",
28
+ get_cdc_health_data: "1.0.0",
29
+ get_pubmed_research_trends: "1.0.0",
30
+ get_openalex_research_trends: "1.0.0",
31
+ search_statistics: "1.0.0",
32
+ search_insights: "1.0.0",
33
+ get_wto_trade_snapshot: "1.0.0",
34
+ get_pew_survey_data: "1.0.0",
35
+ get_openfoodfacts_snapshot: "1.0.0",
36
+ get_ridb_recreation_snapshot: "1.0.0",
37
+ get_osm_commerce_snapshot: "1.0.0",
38
+ get_google_trends_snapshot: "1.0.0",
39
+ get_amazon_products_snapshot: "1.0.0",
40
+ get_oecd_economic_snapshot: "1.0.0",
41
+ get_cbs_nl_snapshot: "1.0.0",
42
+ get_ptdata_pt_snapshot: "1.0.0",
43
+ // v1.28.0 — European expansion
44
+ get_eurostat_eu_snapshot: "1.0.0",
45
+ get_ecb_euro_snapshot: "1.0.0",
46
+ get_boe_uk_snapshot: "1.0.0",
47
+ get_nordic_pxweb_snapshot: "1.0.0",
48
+ get_bundesbank_de_snapshot: "1.0.0",
49
+ get_ine_es_snapshot: "1.0.0",
50
+ get_insee_fr_snapshot: "1.0.0",
51
+ get_snb_ch_snapshot: "1.0.0",
52
+ get_bis_global_snapshot: "1.0.0",
53
+ // v1.28.0 — Americas expansion
54
+ get_bcb_br_snapshot: "1.0.0",
55
+ get_bcrp_pe_snapshot: "1.0.0",
56
+ get_arg_ar_snapshot: "1.0.0",
57
+ get_banxico_mx_snapshot: "1.0.0",
58
+ get_bch_hn_snapshot: "1.0.0",
59
+ get_boc_ca_snapshot: "1.0.0",
60
+ get_statcan_ca_snapshot: "1.0.0",
61
+ get_bcch_cl_snapshot: "1.0.0",
62
+ get_socrata_us_snapshot: "1.0.0",
63
+ // v1.28.0 — Asia-Pacific expansion
64
+ get_abs_au_snapshot: "1.0.0",
65
+ get_rba_au_snapshot: "1.0.0",
66
+ get_pdh_pacific_snapshot: "1.0.0",
67
+ get_bnm_my_snapshot: "1.0.0",
68
+ get_hkma_hk_snapshot: "1.0.0",
69
+ get_estat_jp_snapshot: "1.0.0",
70
+ get_bok_kr_snapshot: "1.0.0",
71
+ get_bot_th_snapshot: "1.0.0",
72
+ get_bps_id_snapshot: "1.0.0",
73
+ get_psa_ph_snapshot: "1.0.0",
74
+ get_sg_snapshot: "1.0.0",
75
+ get_cn_china_snapshot: "1.0.0",
76
+ get_india_in_snapshot: "1.0.0",
77
+ // v1.28.0 — Middle East & Africa expansion
78
+ get_qatar_qa_snapshot: "1.0.0",
79
+ get_bahrain_bh_snapshot: "1.0.0",
80
+ get_boi_il_snapshot: "1.0.0",
81
+ get_sarb_za_snapshot: "1.0.0",
82
+ // Utility tools
83
+ sign_up_free_account: "1.0.0",
84
+ send_feedback: "1.0.0",
85
+ get_my_account: "1.0.0",
86
+ update_user_profile: "1.0.0",
87
+ deep_research_topic: "1.0.0",
88
+ read_url: "1.0.0",
89
+ list_analysts: "1.0.0",
90
+ consult_analyst: "1.0.0",
91
+ toggle_graph_preference: "1.0.0",
92
+ manage_scheduled_reports: "2.0.0",
11
93
  };
12
- const GRAPH_ID_DESCRIPTION = "Select which curated graph to query. Use list_graphs to discover available options. Common values: 'retail', 'beauty', 'sports', 'psfk', 'sic', 'waldo', 'pew'.";
13
- const ALL_TOOLS = [
14
- {
15
- name: "list_graphs",
16
- description: "Discover available knowledge graphs and their schemas, including node types, relationship types, and versions. Use this tool first to find valid graphId values for other tools.",
17
- inputSchema: {
18
- type: "object",
19
- properties: {
20
- userId: { type: "string", description: "Unique identifier for the user (Required)" },
21
- },
22
- required: ["userId"],
23
- },
24
- outputSchema: {
25
- type: "object",
26
- properties: {
27
- graphs: {
28
- type: "array",
29
- description: "Array of available graphs with their schemas",
30
- },
31
- },
32
- },
33
- isDeterministic: true,
34
- },
35
- {
36
- name: "search_graph",
37
- description: "Search across expert-curated PSFK knowledge graphs (Retail, Beauty, Sports and partner datasets) to retrieve structured trend clusters, signals, and supporting articles relevant to a query. Note: a server-side relevance gate may reduce results for brand/entity-specific queries — results with low semantic scores that don't mention query terms are automatically filtered out.",
38
- inputSchema: {
39
- type: "object",
40
- properties: {
41
- graphId: { type: "string", description: GRAPH_ID_DESCRIPTION },
42
- query: { type: "string", description: "The search query. Location terms (city/country names like 'London', 'Tokyo') are auto-detected and used to hard-filter results geographically. Geo terms auto-expand (e.g., 'London' also matches 'UK', 'England'). No additional parameters needed for location filtering." },
43
- userId: { type: "string", description: "Unique identifier for the user (Required)" },
44
- limit: { type: "number", description: "Maximum number of results (default 25, max 50)" },
45
- use_semantic: { type: "boolean", description: "Whether to use semantic search (default true)" },
46
- filters: {
47
- type: "object",
48
- description: "Optional filters to narrow search results",
49
- properties: {
50
- node_types: {
51
- type: "array",
52
- items: { type: "string" },
53
- description: "Filter by node labels (e.g., ['Trend', 'Article'])"
54
- },
55
- },
56
- },
57
- include_evidence: { type: "boolean", description: "If true, batch-fetch supporting evidence articles inline with results. Saves a separate get_evidence call per trend. Default: true. Each evidence item includes: sourceUrl, place (geographic location), brandNames, snippet, and publishedAt." },
58
- },
59
- required: ["graphId", "query", "userId"],
60
- },
61
- outputSchema: {
62
- type: "object",
63
- properties: {
64
- results: {
65
- type: "array",
66
- description: "Array of matching nodes (trends, articles), each optionally with an 'evidence' array if include_evidence=true. When evidence is included, each evidence item contains: sourceUrl (article link), place (geographic location of the article), brandNames (brands mentioned), snippet (relevant excerpt), and publishedAt (publication date).",
67
- },
68
- total: { type: "number", description: "Total number of results found" },
69
- search_method: { type: "string", description: "Search method used: 'vector', 'keyword', 'hybrid', or 'all_fallback'" },
70
- usage: {
71
- type: "object",
72
- description: "Billing/usage metadata",
73
- properties: {
74
- total_billable_units: { type: "number" },
75
- },
76
- },
77
- },
78
- },
79
- isDeterministic: false,
80
- },
81
- {
82
- name: "get_neighbors",
83
- description: "Explore how a trend, brand, or technology connects to related signals, concepts, and adjacent innovation patterns within the selected graph. Traversal is depth-limited for focused discovery.",
84
- inputSchema: {
85
- type: "object",
86
- properties: {
87
- graphId: { type: "string", description: GRAPH_ID_DESCRIPTION },
88
- seed_node_ids: { type: "array", items: { type: "string" }, description: "Array of node IDs to start traversal from" },
89
- userId: { type: "string", description: "Unique identifier for the user (Required)" },
90
- relationship_types: { type: "array", items: { type: "string" }, description: "Filter by relationship types: 'EVIDENCED_BY', 'RELATED_TO', 'SEMANTICALLY_SIMILAR', 'ASSOCIATED_BRAND', 'MENTIONS_BRAND', 'IN_LOCATION'" },
91
- direction: { type: "string", enum: ["in", "out"], description: "Traversal direction: 'out' (default) follows outgoing edges, 'in' follows incoming edges" },
92
- depth: { type: "number", description: "Traversal depth (default 1, max 2)" },
93
- limit: { type: "number", description: "Maximum results (default 50)" },
94
- },
95
- required: ["graphId", "seed_node_ids", "userId"],
96
- },
97
- outputSchema: {
98
- type: "object",
99
- properties: {
100
- nodes: {
101
- type: "array",
102
- description: "Array of neighboring nodes",
103
- },
104
- edges: {
105
- type: "array",
106
- description: "Array of relationships between nodes",
107
- },
108
- usage: {
109
- type: "object",
110
- properties: {
111
- total_billable_units: { type: "number" },
112
- },
113
- },
114
- },
115
- },
116
- isDeterministic: true,
117
- },
118
- {
119
- name: "get_evidence",
120
- description: "Retrieve supporting signals, source articles, and structured evidence for a specific trend or concept. Designed for provenance, validation, and strategic briefing.",
121
- inputSchema: {
122
- type: "object",
123
- properties: {
124
- graphId: { type: "string", description: GRAPH_ID_DESCRIPTION },
125
- for_node_id: { type: "string", description: "The ID of the node (Trend or Article)" },
126
- userId: { type: "string", description: "Unique identifier for the user (Required)" },
127
- top_k: { type: "number", description: "Number of evidence items to return (default 5)" },
128
- },
129
- required: ["graphId", "for_node_id", "userId"],
130
- },
131
- outputSchema: {
132
- type: "object",
133
- properties: {
134
- evidence: {
135
- type: "array",
136
- description: "Array of evidence items with source URLs, titles, snippets, relevance scores, place (geographic location of the article), and brand names",
137
- },
138
- node_id: { type: "string", description: "The node this evidence supports" },
139
- usage: {
140
- type: "object",
141
- properties: {
142
- total_billable_units: { type: "number" },
143
- },
144
- },
145
- },
146
- },
147
- isDeterministic: true,
148
- },
149
- {
150
- name: "get_node",
151
- description: "Retrieve the full metadata and properties of a specific node within the knowledge graph, including labels and structured attributes.",
152
- inputSchema: {
153
- type: "object",
154
- properties: {
155
- graphId: { type: "string", description: GRAPH_ID_DESCRIPTION },
156
- nodeId: { type: "string", description: "The ID of the node" },
157
- userId: { type: "string", description: "Unique identifier for the user (Required)" },
158
- },
159
- required: ["graphId", "nodeId", "userId"],
160
- },
161
- outputSchema: {
162
- type: "object",
163
- properties: {
164
- id: { type: "string", description: "Node ID" },
165
- display: { type: "string", description: "Display name of the node" },
166
- labels: { type: "array", description: "Array of node labels/types" },
167
- properties: { type: "object", description: "Key-value properties of the node" },
168
- },
169
- },
170
- isDeterministic: true,
171
- },
172
- {
173
- name: "get_label_values",
174
- description: "Discover available values for a specific category (e.g., Technology, Audience, RetailerType, Brand) to support structured filtering and exploration.",
175
- inputSchema: {
176
- type: "object",
177
- properties: {
178
- graphId: { type: "string", description: GRAPH_ID_DESCRIPTION },
179
- label: { type: "string", description: "The label to fetch values for (e.g., 'Brand', 'Location', 'Technology', 'Audience', 'RetailerType', 'Trend')" },
180
- userId: { type: "string", description: "Unique identifier for the user (Required)" },
181
- property: { type: "string", description: "Optional property to return values for. Defaults vary by label (Brand→name, Technology→slug, Audience→slug, Trend→trendName)" },
182
- },
183
- required: ["graphId", "label", "userId"],
184
- },
185
- outputSchema: {
186
- type: "object",
187
- properties: {
188
- label: { type: "string", description: "The label queried" },
189
- property: { type: "string", description: "The property values were fetched from" },
190
- values: { type: "array", description: "Array of valid values for the label" },
191
- count: { type: "number", description: "Number of values found" },
192
- },
193
- },
194
- isDeterministic: true,
195
- },
196
- {
197
- name: "psfk_overview",
198
- description: "Generate a macro-level overview of a selected PSFK domain (e.g., Retail, Beauty, Sports), summarizing key meta-patterns and structured trend clusters for strategic briefing.",
199
- inputSchema: {
200
- type: "object",
201
- properties: {
202
- industry: { type: "string", description: "Filter by industry (e.g. 'Retail', 'Health')" },
203
- sector: { type: "string", description: "Filter by sector" },
204
- region: { type: "string", description: "Filter by region" },
205
- timeframe: { type: "string", description: "Timeframe for the overview" },
206
- userId: { type: "string", description: "Unique identifier for the user (Required)" },
207
- },
208
- required: ["userId"],
209
- anyOf: [
210
- { required: ["industry"] },
211
- { required: ["sector"] }
212
- ],
213
- },
214
- outputSchema: {
215
- type: "object",
216
- properties: {
217
- meta_patterns: {
218
- type: "array",
219
- description: "Up to 3 macro-level pattern objects with trend summaries",
220
- },
221
- industry: { type: "string", description: "Industry filter applied" },
222
- sector: { type: "string", description: "Sector filter applied" },
223
- usage: {
224
- type: "object",
225
- properties: {
226
- total_billable_units: { type: "number" },
227
- },
228
- },
229
- },
230
- },
231
- isDeterministic: false,
232
- },
233
- {
234
- name: "discover_adjacent_trends",
235
- description: "Find trends that are semantically similar to a given trend — useful for discovering 'what else should I be watching?' or expanding a research brief with related signals. Returns trends connected by AI-computed similarity, not editorial curation.",
236
- inputSchema: {
237
- type: "object",
238
- properties: {
239
- graphId: {
240
- type: "string",
241
- description: GRAPH_ID_DESCRIPTION
242
- },
243
- trend_id: {
244
- type: "string",
245
- description: "The trendId of the seed trend to find adjacent possibilities for"
246
- },
247
- userId: {
248
- type: "string",
249
- description: "Unique identifier for the user (Required)"
250
- },
251
- min_score: {
252
- type: "number",
253
- description: "Minimum similarity score threshold (0-1). Higher = more similar. Default: 0.80"
254
- },
255
- limit: {
256
- type: "number",
257
- description: "Maximum number of adjacent trends to return. Default: 10"
258
- },
259
- include_editorial: {
260
- type: "boolean",
261
- description: "If true, also include trends that are already editorially linked via RELATED_TO. Default: false (shows only AI-discovered connections)"
262
- }
263
- },
264
- required: ["graphId", "trend_id", "userId"]
265
- },
266
- outputSchema: {
267
- type: "object",
268
- properties: {
269
- adjacent: {
270
- type: "array",
271
- description: "Array of semantically similar trends with similarity scores",
272
- },
273
- node_id: { type: "string", description: "The seed trend ID queried" },
274
- count: { type: "number", description: "Number of adjacent trends returned" },
275
- min_score: { type: "number", description: "Minimum similarity threshold applied" },
276
- usage: {
277
- type: "object",
278
- properties: {
279
- total_billable_units: { type: "number" },
280
- },
281
- },
282
- },
283
- },
284
- isDeterministic: true,
285
- },
286
- ];
287
- const DEFAULT_ENTERPRISE_TOOLS = [
288
- "list_graphs",
289
- "search_graph",
290
- "get_node",
291
- "get_evidence",
292
- "get_neighbors",
293
- "get_label_values",
294
- "discover_adjacent_trends"
295
- ];
296
- const allowedToolsStr = process.env.ALLOWED_TOOLS;
297
- const allowedTools = allowedToolsStr
298
- ? allowedToolsStr.split(',').map(s => s.trim())
299
- : DEFAULT_ENTERPRISE_TOOLS;
300
- export const TOOLS = ALL_TOOLS.filter(tool => allowedTools.includes(tool.name));
301
94
  //# sourceMappingURL=tools.js.map
package/dist/tools.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,kBAAkB,GAAG,QAAQ,CAAC;AAE3C,MAAM,CAAC,MAAM,aAAa,GAAG;IACzB,WAAW,EAAE,OAAO;IACpB,YAAY,EAAE,OAAO;IACrB,aAAa,EAAE,OAAO;IACtB,YAAY,EAAE,OAAO;IACrB,QAAQ,EAAE,OAAO;IACjB,gBAAgB,EAAE,OAAO;IACzB,aAAa,EAAE,OAAO;IACtB,wBAAwB,EAAE,OAAO;IACjC,aAAa,EAAE,OAAO;IACtB,0BAA0B,EAAE,OAAO;IACnC,gCAAgC,EAAE,OAAO;IACzC,0BAA0B,EAAE,OAAO;IACnC,uBAAuB,EAAE,OAAO;IAChC,6BAA6B,EAAE,OAAO;IACtC,yBAAyB,EAAE,OAAO;IAClC,eAAe,EAAE,OAAO;IACxB,mBAAmB,EAAE,OAAO;IAC5B,yBAAyB,EAAE,OAAO;IAClC,yBAAyB,EAAE,OAAO;IAClC,mBAAmB,EAAE,OAAO;IAC5B,0BAA0B,EAAE,OAAO;IACnC,4BAA4B,EAAE,OAAO;IACrC,iBAAiB,EAAE,OAAO;IAC1B,eAAe,EAAE,OAAO;IACxB,sBAAsB,EAAE,OAAO;IAC/B,mBAAmB,EAAE,OAAO;IAC5B,0BAA0B,EAAE,OAAO;IACnC,4BAA4B,EAAE,OAAO;IACrC,yBAAyB,EAAE,OAAO;IAClC,0BAA0B,EAAE,OAAO;IACnC,4BAA4B,EAAE,OAAO;IACrC,0BAA0B,EAAE,OAAO;IACnC,mBAAmB,EAAE,OAAO;IAC5B,sBAAsB,EAAE,OAAO;IAC/B,+BAA+B;IAC/B,wBAAwB,EAAE,OAAO;IACjC,qBAAqB,EAAE,OAAO;IAC9B,mBAAmB,EAAE,OAAO;IAC5B,yBAAyB,EAAE,OAAO;IAClC,0BAA0B,EAAE,OAAO;IACnC,mBAAmB,EAAE,OAAO;IAC5B,qBAAqB,EAAE,OAAO;IAC9B,mBAAmB,EAAE,OAAO;IAC5B,uBAAuB,EAAE,OAAO;IAChC,+BAA+B;IAC/B,mBAAmB,EAAE,OAAO;IAC5B,oBAAoB,EAAE,OAAO;IAC7B,mBAAmB,EAAE,OAAO;IAC5B,uBAAuB,EAAE,OAAO;IAChC,mBAAmB,EAAE,OAAO;IAC5B,mBAAmB,EAAE,OAAO;IAC5B,uBAAuB,EAAE,OAAO;IAChC,oBAAoB,EAAE,OAAO;IAC7B,uBAAuB,EAAE,OAAO;IAChC,mCAAmC;IACnC,mBAAmB,EAAE,OAAO;IAC5B,mBAAmB,EAAE,OAAO;IAC5B,wBAAwB,EAAE,OAAO;IACjC,mBAAmB,EAAE,OAAO;IAC5B,oBAAoB,EAAE,OAAO;IAC7B,qBAAqB,EAAE,OAAO;IAC9B,mBAAmB,EAAE,OAAO;IAC5B,mBAAmB,EAAE,OAAO;IAC5B,mBAAmB,EAAE,OAAO;IAC5B,mBAAmB,EAAE,OAAO;IAC5B,eAAe,EAAE,OAAO;IACxB,qBAAqB,EAAE,OAAO;IAC9B,qBAAqB,EAAE,OAAO;IAC9B,2CAA2C;IAC3C,qBAAqB,EAAE,OAAO;IAC9B,uBAAuB,EAAE,OAAO;IAChC,mBAAmB,EAAE,OAAO;IAC5B,oBAAoB,EAAE,OAAO;IAC7B,gBAAgB;IAChB,oBAAoB,EAAE,OAAO;IAC7B,aAAa,EAAE,OAAO;IACtB,cAAc,EAAE,OAAO;IACvB,mBAAmB,EAAE,OAAO;IAC5B,mBAAmB,EAAE,OAAO;IAC5B,QAAQ,EAAE,OAAO;IACjB,aAAa,EAAE,OAAO;IACtB,eAAe,EAAE,OAAO;IACxB,uBAAuB,EAAE,OAAO;IAChC,wBAAwB,EAAE,OAAO;CACpC,CAAC"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Trial Tracker — Firestore-backed per-user trial token counter.
3
+ *
4
+ * Tracks how many API calls each trial user (identified by userId/email)
5
+ * has made across all sessions. Cloud Run instances are ephemeral, so
6
+ * in-memory counters reset on cold starts — Firestore provides persistence.
7
+ *
8
+ * Does NOT modify the existing trial → Base conversion flow.
9
+ * Only adds a per-user counting layer that triggers it earlier.
10
+ *
11
+ * Split credit model (2026-04-24):
12
+ * - 50 search tokens (standard graph queries)
13
+ * - 1 free Deep Dive (fast or comprehensive)
14
+ * - 2 free Expert Agent turns
15
+ * Each pool is tracked independently in the Firestore document.
16
+ */
17
+ export type TrialInteractionType = 'search' | 'deep_dive' | 'expert_agent';
18
+ export interface TrialUsage {
19
+ count: number;
20
+ deepDiveUsed: number;
21
+ expertAgentUsed: number;
22
+ firstUse: Date;
23
+ lastUse: Date;
24
+ }
25
+ export interface TrialCheckResult {
26
+ allowed: boolean;
27
+ count: number;
28
+ remaining: number;
29
+ limit: number;
30
+ shouldWarn: boolean;
31
+ /** Which credit pool was checked */
32
+ pool: TrialInteractionType;
33
+ }
34
+ /**
35
+ * Get the current trial usage for a user.
36
+ * If the user's firstUse is older than 30 days, resets all counters.
37
+ * Returns { count, deepDiveUsed, expertAgentUsed, firstUse, lastUse }.
38
+ */
39
+ export declare function getTrialUsage(userId: string): Promise<TrialUsage>;
40
+ /**
41
+ * Increment the trial usage counter for a user.
42
+ * Called after a successful API call.
43
+ * Uses a transaction to ensure firstUse is only set on doc creation.
44
+ *
45
+ * @param userId - The user identifier (email or fingerprint)
46
+ * @param cost - Number of tokens to deduct (default: 1 for search)
47
+ * @param interactionType - Which credit pool to deduct from
48
+ */
49
+ export declare function incrementTrialUsage(userId: string, cost?: number, interactionType?: TrialInteractionType): Promise<number>;
50
+ /**
51
+ * Check if a trial user is allowed to make an API call.
52
+ * Checks the appropriate credit pool based on interactionType.
53
+ *
54
+ * @param userId - The user identifier
55
+ * @param interactionType - Which pool to check: 'search' (50 tokens), 'deep_dive' (1 free), 'expert_agent' (2 free)
56
+ */
57
+ export declare function checkTrialLimit(userId: string, interactionType?: TrialInteractionType): Promise<TrialCheckResult>;
58
+ //# sourceMappingURL=trialTracker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trialTracker.d.ts","sourceRoot":"","sources":["../src/trialTracker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAoBH,MAAM,MAAM,oBAAoB,GAAG,QAAQ,GAAG,WAAW,GAAG,cAAc,CAAC;AAoB3E,MAAM,WAAW,UAAU;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,IAAI,CAAC;IACf,OAAO,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,OAAO,CAAC;IACpB,oCAAoC;IACpC,IAAI,EAAE,oBAAoB,CAAC;CAC9B;AAMD;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CA4BvE;AAED;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CACrC,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,MAAU,EAChB,eAAe,GAAE,oBAA+B,GACjD,OAAO,CAAC,MAAM,CAAC,CA2CjB;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CACjC,MAAM,EAAE,MAAM,EACd,eAAe,GAAE,oBAA+B,GACjD,OAAO,CAAC,gBAAgB,CAAC,CAqC3B"}
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Trial Tracker — Firestore-backed per-user trial token counter.
3
+ *
4
+ * Tracks how many API calls each trial user (identified by userId/email)
5
+ * has made across all sessions. Cloud Run instances are ephemeral, so
6
+ * in-memory counters reset on cold starts — Firestore provides persistence.
7
+ *
8
+ * Does NOT modify the existing trial → Base conversion flow.
9
+ * Only adds a per-user counting layer that triggers it earlier.
10
+ *
11
+ * Split credit model (2026-04-24):
12
+ * - 50 search tokens (standard graph queries)
13
+ * - 1 free Deep Dive (fast or comprehensive)
14
+ * - 2 free Expert Agent turns
15
+ * Each pool is tracked independently in the Firestore document.
16
+ */
17
+ import { Firestore } from '@google-cloud/firestore';
18
+ // ---------------------------------------------------------------------------
19
+ // Configuration
20
+ // ---------------------------------------------------------------------------
21
+ const PER_USER_TRIAL_LIMIT = 50;
22
+ const DEEP_DIVE_TRIAL_LIMIT = 1; // 1 free Deep Dive per trial user
23
+ const EXPERT_AGENT_TRIAL_LIMIT = 2; // 2 free Expert Agent turns per trial user
24
+ const WARNING_THRESHOLD = 10; // warn when remaining < 10
25
+ const RESET_PERIOD_MS = 30 * 24 * 60 * 60 * 1000; // 30 days
26
+ const COLLECTION = 'trial-usage';
27
+ // ---------------------------------------------------------------------------
28
+ // Firestore client — uses Application Default Credentials on Cloud Run
29
+ // ---------------------------------------------------------------------------
30
+ let db = null;
31
+ function getDb() {
32
+ if (!db) {
33
+ const projectId = process.env.GOOGLE_CLOUD_PROJECT || process.env.GCP_PROJECT;
34
+ db = new Firestore(projectId ? { projectId } : {});
35
+ }
36
+ return db;
37
+ }
38
+ // ---------------------------------------------------------------------------
39
+ // Core functions
40
+ // ---------------------------------------------------------------------------
41
+ /**
42
+ * Get the current trial usage for a user.
43
+ * If the user's firstUse is older than 30 days, resets all counters.
44
+ * Returns { count, deepDiveUsed, expertAgentUsed, firstUse, lastUse }.
45
+ */
46
+ export async function getTrialUsage(userId) {
47
+ try {
48
+ const docRef = getDb().collection(COLLECTION).doc(sanitizeDocId(userId));
49
+ const snap = await docRef.get();
50
+ if (!snap.exists) {
51
+ return { count: 0, deepDiveUsed: 0, expertAgentUsed: 0, firstUse: new Date(), lastUse: new Date() };
52
+ }
53
+ const data = snap.data();
54
+ const firstUse = data['firstUse']?.toDate?.() ?? new Date(data['firstUse']);
55
+ const lastUse = data['lastUse']?.toDate?.() ?? new Date(data['lastUse']);
56
+ const count = data['count'] ?? 0;
57
+ const deepDiveUsed = data['deepDiveUsed'] ?? 0;
58
+ const expertAgentUsed = data['expertAgentUsed'] ?? 0;
59
+ // Monthly reset: if firstUse is older than 30 days, reset all counters
60
+ if (Date.now() - firstUse.getTime() > RESET_PERIOD_MS) {
61
+ await docRef.set({ count: 0, deepDiveUsed: 0, expertAgentUsed: 0, firstUse: new Date(), lastUse: new Date() });
62
+ return { count: 0, deepDiveUsed: 0, expertAgentUsed: 0, firstUse: new Date(), lastUse: new Date() };
63
+ }
64
+ return { count, deepDiveUsed, expertAgentUsed, firstUse, lastUse };
65
+ }
66
+ catch (err) {
67
+ // If Firestore is unavailable, fail open — let the shared pool handle it
68
+ console.error(`[trialTracker] Firestore read failed for ${userId}:`, err.message);
69
+ return { count: 0, deepDiveUsed: 0, expertAgentUsed: 0, firstUse: new Date(), lastUse: new Date() };
70
+ }
71
+ }
72
+ /**
73
+ * Increment the trial usage counter for a user.
74
+ * Called after a successful API call.
75
+ * Uses a transaction to ensure firstUse is only set on doc creation.
76
+ *
77
+ * @param userId - The user identifier (email or fingerprint)
78
+ * @param cost - Number of tokens to deduct (default: 1 for search)
79
+ * @param interactionType - Which credit pool to deduct from
80
+ */
81
+ export async function incrementTrialUsage(userId, cost = 1, interactionType = 'search') {
82
+ try {
83
+ const docRef = getDb().collection(COLLECTION).doc(sanitizeDocId(userId));
84
+ const now = new Date();
85
+ const newCount = await getDb().runTransaction(async (tx) => {
86
+ const snap = await tx.get(docRef);
87
+ if (!snap.exists) {
88
+ // First ever API call for this user
89
+ const initial = { count: 0, deepDiveUsed: 0, expertAgentUsed: 0, firstUse: now, lastUse: now };
90
+ if (interactionType === 'deep_dive') {
91
+ initial.deepDiveUsed = 1;
92
+ }
93
+ else if (interactionType === 'expert_agent') {
94
+ initial.expertAgentUsed = 1;
95
+ }
96
+ else {
97
+ initial.count = cost;
98
+ }
99
+ tx.set(docRef, initial);
100
+ return interactionType === 'search' ? cost : 0;
101
+ }
102
+ const data = snap.data();
103
+ const updates = { lastUse: now };
104
+ if (interactionType === 'deep_dive') {
105
+ updates.deepDiveUsed = (data['deepDiveUsed'] ?? 0) + 1;
106
+ }
107
+ else if (interactionType === 'expert_agent') {
108
+ updates.expertAgentUsed = (data['expertAgentUsed'] ?? 0) + 1;
109
+ }
110
+ else {
111
+ updates.count = (data['count'] ?? 0) + cost;
112
+ }
113
+ tx.update(docRef, updates);
114
+ return updates.count ?? data['count'] ?? 0;
115
+ });
116
+ return newCount;
117
+ }
118
+ catch (err) {
119
+ // If Firestore is unavailable, fail open
120
+ console.error(`[trialTracker] Firestore write failed for ${userId}:`, err.message);
121
+ return 0;
122
+ }
123
+ }
124
+ /**
125
+ * Check if a trial user is allowed to make an API call.
126
+ * Checks the appropriate credit pool based on interactionType.
127
+ *
128
+ * @param userId - The user identifier
129
+ * @param interactionType - Which pool to check: 'search' (50 tokens), 'deep_dive' (1 free), 'expert_agent' (2 free)
130
+ */
131
+ export async function checkTrialLimit(userId, interactionType = 'search') {
132
+ const usage = await getTrialUsage(userId);
133
+ if (interactionType === 'deep_dive') {
134
+ const remaining = Math.max(0, DEEP_DIVE_TRIAL_LIMIT - usage.deepDiveUsed);
135
+ return {
136
+ allowed: usage.deepDiveUsed < DEEP_DIVE_TRIAL_LIMIT,
137
+ count: usage.deepDiveUsed,
138
+ remaining,
139
+ limit: DEEP_DIVE_TRIAL_LIMIT,
140
+ shouldWarn: false, // Only 1 credit — no point warning
141
+ pool: 'deep_dive',
142
+ };
143
+ }
144
+ if (interactionType === 'expert_agent') {
145
+ const remaining = Math.max(0, EXPERT_AGENT_TRIAL_LIMIT - usage.expertAgentUsed);
146
+ return {
147
+ allowed: usage.expertAgentUsed < EXPERT_AGENT_TRIAL_LIMIT,
148
+ count: usage.expertAgentUsed,
149
+ remaining,
150
+ limit: EXPERT_AGENT_TRIAL_LIMIT,
151
+ shouldWarn: remaining === 1,
152
+ pool: 'expert_agent',
153
+ };
154
+ }
155
+ // Default: search token pool
156
+ const remaining = Math.max(0, PER_USER_TRIAL_LIMIT - usage.count);
157
+ return {
158
+ allowed: usage.count < PER_USER_TRIAL_LIMIT,
159
+ count: usage.count,
160
+ remaining,
161
+ limit: PER_USER_TRIAL_LIMIT,
162
+ shouldWarn: remaining > 0 && remaining < WARNING_THRESHOLD,
163
+ pool: 'search',
164
+ };
165
+ }
166
+ // ---------------------------------------------------------------------------
167
+ // Helpers
168
+ // ---------------------------------------------------------------------------
169
+ /**
170
+ * Sanitize a userId for use as a Firestore document ID.
171
+ * Firestore doc IDs cannot contain '/' and should be reasonable length.
172
+ */
173
+ function sanitizeDocId(userId) {
174
+ return userId
175
+ .replace(/[\/\.+\s]/g, '_') // Replace /, ., +, whitespace with _
176
+ .replace(/__+/g, '_') // Collapse multiple underscores
177
+ .substring(0, 200);
178
+ }
179
+ //# sourceMappingURL=trialTracker.js.map