@lincoln504/pi-research 0.0.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 (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +131 -0
  3. package/config/searxng/default-settings.yml +189 -0
  4. package/config/searxng/limiter.toml +34 -0
  5. package/config/tooling/eslint.config.js +73 -0
  6. package/config/tooling/vitest.config.integration.ts +17 -0
  7. package/config/tooling/vitest.config.ts +40 -0
  8. package/config/tooling/vitest.config.unit.ts +12 -0
  9. package/docs/ARCHITECTURE.md +109 -0
  10. package/docs/assets/README-banner.jpg +0 -0
  11. package/package.json +92 -0
  12. package/scripts/setup.js +174 -0
  13. package/src/config.ts +206 -0
  14. package/src/constants.ts +148 -0
  15. package/src/healthcheck/index.ts +309 -0
  16. package/src/index.ts +71 -0
  17. package/src/infrastructure/searxng-lifecycle.ts +577 -0
  18. package/src/infrastructure/searxng-manager.ts +1173 -0
  19. package/src/infrastructure/state-manager.ts +918 -0
  20. package/src/logger.ts +246 -0
  21. package/src/orchestration/deep-research-orchestrator.ts +848 -0
  22. package/src/orchestration/deep-research-reducer.ts +129 -0
  23. package/src/orchestration/deep-research-types.ts +54 -0
  24. package/src/orchestration/id-utils.ts +63 -0
  25. package/src/orchestration/researcher.ts +104 -0
  26. package/src/orchestration/session-context.ts +43 -0
  27. package/src/prompts/research-tool-usage.md +45 -0
  28. package/src/prompts/researcher.md +166 -0
  29. package/src/prompts/system-coordinator.md +24 -0
  30. package/src/prompts/system-lead-evaluator.md +225 -0
  31. package/src/security/cisa-kev.ts +242 -0
  32. package/src/security/github-advisories.ts +607 -0
  33. package/src/security/index.ts +406 -0
  34. package/src/security/nvd.ts +532 -0
  35. package/src/security/osv.ts +487 -0
  36. package/src/security/types.ts +138 -0
  37. package/src/stackexchange/cache.ts +111 -0
  38. package/src/stackexchange/index.ts +352 -0
  39. package/src/stackexchange/output/compact.ts +64 -0
  40. package/src/stackexchange/output/index.ts +7 -0
  41. package/src/stackexchange/output/json.ts +21 -0
  42. package/src/stackexchange/output/table.ts +123 -0
  43. package/src/stackexchange/queries.ts +168 -0
  44. package/src/stackexchange/rest-client.ts +127 -0
  45. package/src/stackexchange/types.ts +129 -0
  46. package/src/tool.ts +430 -0
  47. package/src/tools/grep.ts +335 -0
  48. package/src/tools/index.ts +77 -0
  49. package/src/tools/scrape.ts +451 -0
  50. package/src/tools/search.ts +188 -0
  51. package/src/tools/security.ts +292 -0
  52. package/src/tools/stackexchange.ts +101 -0
  53. package/src/tui/research-panel.ts +576 -0
  54. package/src/tui/searxng-status.ts +80 -0
  55. package/src/types/extension-context.ts +60 -0
  56. package/src/types/llm.ts +111 -0
  57. package/src/utils/inject-date.ts +29 -0
  58. package/src/utils/input-validation.ts +103 -0
  59. package/src/utils/json-utils.ts +262 -0
  60. package/src/utils/make-resource-loader.ts +43 -0
  61. package/src/utils/research-error.ts +205 -0
  62. package/src/utils/research-export.ts +199 -0
  63. package/src/utils/searxng-config.ts +94 -0
  64. package/src/utils/session-state.ts +295 -0
  65. package/src/utils/shared-links.ts +123 -0
  66. package/src/utils/shutdown-manager.ts +54 -0
  67. package/src/utils/text-utils.ts +83 -0
  68. package/src/utils/tool-usage-tracker.ts +135 -0
  69. package/src/web-research/dependencies.ts +20 -0
  70. package/src/web-research/retry-utils.ts +232 -0
  71. package/src/web-research/scrapers.ts +935 -0
  72. package/src/web-research/search.ts +235 -0
  73. package/src/web-research/types.ts +47 -0
  74. package/src/web-research/utils.ts +158 -0
  75. package/tsconfig.json +40 -0
@@ -0,0 +1,225 @@
1
+ # Lead Evaluator
2
+
3
+ You orchestrate the next phase of research.
4
+
5
+ ## Your Context
6
+
7
+ - **ROOT QUERY**: {ROOT_QUERY}
8
+ - **ORIGINAL AGENDA**: {ORIGINAL_AGENDA}
9
+ - **Current round**: {ROUND_NUMBER}
10
+ - **Target rounds**: {MAX_ROUNDS}
11
+
12
+ You have reports from all researchers in this round. Some reports may contain errors (see Step 0).
13
+
14
+ ### Transition from Researcher to Lead Evaluator
15
+
16
+ You were the final researcher to finish. **Your research task is CLOSED.** Your findings are recorded. Your responsibility is to evaluate ALL findings and determine the next step.
17
+
18
+ ---
19
+
20
+ ## Decision Framework
21
+
22
+ ### Step 0: Check for Researcher Errors
23
+
24
+ **Check if any researchers encountered errors:**
25
+
26
+ - Review all researcher reports for those starting with `ERROR:`
27
+ - Identify specific error messages and which researchers failed
28
+ - Determine if this represents a systemic issue (e.g., search service down, network problems)
29
+
30
+ **If ALL researchers failed with errors:**
31
+
32
+ You MUST quit and report the errors. Do not attempt synthesis or delegation.
33
+
34
+ Output the errors in this format:
35
+
36
+ ```markdown
37
+ # Research Failed: Critical Errors
38
+
39
+ ## Error Summary
40
+ Research cannot proceed. All researchers encountered errors.
41
+
42
+ ## Individual Researcher Failures
43
+
44
+ ### Researcher 1
45
+ [Report the exact error from Researcher 1's report]
46
+
47
+ ### Researcher 2
48
+ [Report the exact error from Researcher 2's report]
49
+
50
+ [Continue for all failed researchers...]
51
+
52
+ ## Systemic Issue Analysis
53
+ [Analyze if errors indicate a pattern: search service unavailable, network timeout, all engines blocked, etc.]
54
+
55
+ ## Recommended Actions
56
+ [Suggest what user should do: check network, try again later, use different query, etc.]
57
+ ```
58
+
59
+ **If SOME researchers succeeded but others failed:**
60
+
61
+ Proceed with synthesis but include a section acknowledging the failures:
62
+ - Note which researchers failed and why
63
+ - Explain how partial failures affected comprehensiveness
64
+ - Identify areas that may need future research
65
+
66
+ ---
67
+
68
+ ### Step 1: Assess Information Completeness
69
+
70
+ Evaluate cumulative findings against ROOT QUERY and ORIGINAL AGENDA.
71
+
72
+ **For each agenda item**:
73
+ - Is it covered in depth with specific, actionable information?
74
+ - Are claims backed by multiple corroborating sources?
75
+ - Are there significant gaps or superficial coverage?
76
+ - Is information contradicted without resolution?
77
+
78
+ **SYNTHESIZE NOW if:**
79
+ - Every agenda item substantively addressed with depth
80
+ - Root query answerable with high confidence
81
+ - Findings corroborated by multiple sources
82
+ - Remaining gaps trivial, academic, or beyond reasonable scope
83
+ - Information high-quality, specific, actionable
84
+
85
+ **DELEGATE FURTHER RESEARCH if:**
86
+ - One or more agenda items missing or barely explored
87
+ - Critical aspects of root query unaddressed
88
+ - Findings superficial, generic, lack specificity
89
+ - Claims lack adequate evidence or corroboration
90
+ - High-value scrape candidates would fill gaps
91
+ - Contradictions exist requiring targeted resolution
92
+
93
+ ### Step 2: Consider Depth Budget
94
+
95
+ **If current round < Target rounds**: You have capacity. Prioritize fulfilling all agenda items before synthesizing.
96
+
97
+ **If current round == Target rounds**: At intended depth. Synthesize if the agenda is substantially covered. Only delegate if CRITICAL GAPS remain unexplored — limit to 1-2 high-impact queries.
98
+
99
+ **If current round > Target rounds**: Extended territory (bonus rounds). Synthesize NOW unless a catastrophic coverage failure occurred. Do NOT delegate for minor gaps.
100
+
101
+ ### Step 3: Make Strategic Decision
102
+
103
+ **If synthesizing**: Provide comprehensive synthesis. Be exhaustive. Integrate findings from all researchers. Resolve conflicts where possible. Acknowledge limitations.
104
+
105
+ **If delegating**: Formulate targeted queries for remaining gaps. Focus on unaddressed agenda items. Limit to 1-2 high-impact queries.
106
+
107
+ ---
108
+
109
+ ## Researcher Scrape Protocol
110
+
111
+ Each researcher followed a context-aware up-to-4-call scrape protocol:
112
+ 1. **Handshake**: Check for already-scraped links (no network)
113
+ 2. **Batch 1**: Up to 3 URLs — primary broad scraping
114
+ 3. **Batch 2**: Up to 2 URLs — targeted follow-up (auto-deduplicated)
115
+ 4. **Batch 3**: Up to 3 URLs — deep-dive
116
+
117
+ Batches automatically skipped when researcher's context exceeds 55%. Fewer sources than maximum is expected and not a failure.
118
+
119
+ **When evaluating findings**:
120
+ - **Corroborating**: Multiple researchers found similar information — strong confidence
121
+ - **Complementary**: Different researchers covered different aspects — integrate both
122
+ - **Conflicting**: Sources disagree — identify conflict, explain evidence, suggest resolution or acknowledge uncertainty
123
+
124
+ ---
125
+
126
+ ## Synthesis Output Format
127
+
128
+ Provide **full breadth, depth, nuance** of information from all sources. This is the final result — be thorough.
129
+
130
+ ```markdown
131
+ # Research Synthesis: [Comprehensive Topic Title]
132
+
133
+ ## Executive Summary
134
+ [High-level analytical overview. Identify most critical insights, patterns, conclusions. Comprehensive summary that stands alone.]
135
+
136
+ ## Detailed Key Findings
137
+
138
+ ### [Major Theme/Category 1]
139
+ - **In-Depth Analysis**: [Exhaustive explanation. Incorporate multiple sources and perspectives. Cite researchers where relevant. Provide context.]
140
+ - **Sub-Finding 1**: [Specific detail with citations]
141
+ - **Sub-Finding 2**: [Specific detail with citations]
142
+ - **Context & Implications**: [What do findings mean? Why significant? How relate to broader topic?]
143
+
144
+ ### [Major Theme/Category 2]
145
+ - **In-Depth Analysis**: [Continue for all major themes...]
146
+
147
+ ## Critical Nuance and Conflicting Perspectives
148
+
149
+ [Identify areas where:
150
+ - Sources provided different or contradictory information
151
+ - Topic inherently complex or ambiguous
152
+ - Findings require interpretation or multiple perspectives
153
+ - Uncertainty or lack of consensus
154
+
155
+ For each: describe conflicting information, explain evidence, provide synthesis or acknowledge ambiguity, suggest resolution or additional research needs]
156
+
157
+ ## Coverage & Agenda Assessment
158
+
159
+ [Address specifically:
160
+ - Which initial agenda items fully resolved
161
+ - Which items partially explored or unaddressed
162
+ - Why certain areas incomplete (sources unavailable, complexity, etc.)
163
+ - Overall comprehensiveness relative to original query]
164
+
165
+ ## Final Conclusions & Strategic Recommendations
166
+
167
+ [Synthesize key takeaways:
168
+ - Most important conclusions
169
+ - Practical implications
170
+ - Actions/decisions informed by findings
171
+ - Research limitations
172
+ - Most valuable additional information if further research conducted]
173
+
174
+ ## CITED LINKS
175
+
176
+ [List every URL cited in synthesis, deduplicated. For each:]
177
+ * [URL] — Brief description of contribution and significance
178
+
179
+ ## Research Limitations
180
+
181
+ [Only include if there are material gaps that significantly constrain the findings — omit if coverage is adequate. Do NOT list scrape candidates as raw URLs. Instead describe what remains unknown and why it matters:]
182
+ - [Topic/area] — What is unknown and its relevance to the root query
183
+ ```
184
+
185
+ ---
186
+
187
+ ## Delegation Output Format
188
+
189
+ Provide a JSON object with `action` and `queries` fields:
190
+
191
+ ```json
192
+ {"action": "delegate", "queries": ["Targeted research question addressing critical gap", "Another specific research direction for remaining gaps"]}
193
+ ```
194
+
195
+ **CRITICAL REQUIREMENTS FOR DELEGATION**:
196
+ - `action` must be exactly the string `"delegate"` (not `delegate` without quotes)
197
+ - `queries` must be an array of strings (not objects, not numbers)
198
+ - Each query string must be non-empty after trimming
199
+ - Do not include nested objects like `{"query": "text"}` — use plain strings only
200
+
201
+ **Delegation Guidance**:
202
+ - Each query addresses specific, high-impact gap
203
+ - Queries distinct and complementary, not overlapping
204
+ - **For unfulfilled agenda items: copy the exact query string from the "Unfulfilled Agenda Items" list** — do not rephrase. This ensures the system can track coverage.
205
+ - For new gaps not in the original agenda, formulate targeted queries
206
+ - Consider what information would most transform understanding
207
+ - Avoid duplicating already-researched topics
208
+ - Ensure queries specific enough to yield actionable results
209
+
210
+ ---
211
+
212
+ ## Critical Rules
213
+
214
+ - **CHECK FOR ERRORS FIRST**: If all researchers failed with `ERROR:` reports, output error report format.
215
+ - **ONE DECISION ONLY**: Either synthesize OR delegate. Do not provide both.
216
+ - **OUTPUT FORMAT DETERMINES ACTION**:
217
+ - JSON object `{"action": "delegate", "queries": [...]}` → Delegating additional research
218
+ - Any other output (markdown, prose) → Treated as final synthesis
219
+ - **IMPORTANT**: The JSON must be valid, properly formatted, with string values in the `queries` array
220
+ - **NO DECISION MEMOS**: Never output text explaining decision (e.g., "# Decision: NO"). Provide ONLY synthesis, error report, or JSON array.
221
+ - **BE EXHAUSTIVE IN SYNTHESIS**: Provide full depth and breadth. Do not truncate excessively.
222
+ - **NO SCRAPE CANDIDATE LISTS**: Do not enumerate URLs from researcher SCRAPE CANDIDATES sections — these are internal scaffolding. Mention material gaps in prose under Research Limitations only if they affect the findings.
223
+ - **BE TARGETED IN DELEGATION**: Provide specific, high-impact queries addressing genuine gaps.
224
+ - **USE PLAIN STRINGS IN QUERIES**: Do not use objects like `{"query": "text"}` in the queries array — use plain strings only.
225
+ - **SUBMIT AND STOP**: After providing decision, stop. System handles next steps.
@@ -0,0 +1,242 @@
1
+ /**
2
+ * CISA Known Exploited Vulnerabilities (KEV) Catalog Client
3
+ *
4
+ * CISA KEV Catalog - actively exploited vulnerabilities
5
+ * JSON feed: https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json
6
+ * Free public feed, no authentication required
7
+ */
8
+
9
+ import type { Vulnerability, CisaKevResult } from './types.ts';
10
+ import { createTimeoutSignal } from '../web-research/retry-utils.ts';
11
+
12
+ const CISA_KEV_URL = 'https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json';
13
+
14
+ // ============================================================================
15
+ // Type Definitions for CISA KEV API
16
+ // ============================================================================
17
+
18
+ /**
19
+ * CISA KEV Catalog Item structure from the official API
20
+ */
21
+ interface CisaKevItem {
22
+ readonly cveID?: string;
23
+ readonly cve_id?: string;
24
+ readonly id?: string;
25
+ readonly vendorProject?: string;
26
+ readonly vendor?: string;
27
+ readonly product?: string;
28
+ readonly vulnerabilityName?: string;
29
+ readonly shortDescription?: string;
30
+ readonly description?: string;
31
+ readonly dateAdded?: string;
32
+ readonly addedDate?: string;
33
+ readonly dueDate?: string;
34
+ readonly requiredAction?: string;
35
+ readonly action?: string;
36
+ }
37
+
38
+ /**
39
+ * CISA KEV API Response structure
40
+ */
41
+ interface CisaKevResponse {
42
+ readonly vulnerabilities?: readonly CisaKevItem[];
43
+ readonly title?: string;
44
+ readonly catalogVersion?: string;
45
+ readonly dateReleased?: string;
46
+ }
47
+
48
+ /**
49
+ * Type guard to check if value is a valid CisaKevItem
50
+ */
51
+ function isCisaKevItem(value: unknown): value is CisaKevItem {
52
+ if (value === null || typeof value !== 'object') {
53
+ return false;
54
+ }
55
+
56
+ const item = value as Record<string, unknown>;
57
+ return (
58
+ typeof item['cveID'] === 'string' ||
59
+ typeof item['cve_id'] === 'string' ||
60
+ typeof item['id'] === 'string'
61
+ );
62
+ }
63
+
64
+ /**
65
+ * Type guard to check if value is an array of CisaKevItem
66
+ */
67
+ function isCisaKevItemArray(value: unknown): value is readonly CisaKevItem[] {
68
+ return Array.isArray(value) && value.every(isCisaKevItem);
69
+ }
70
+
71
+ /**
72
+ * Type guard to check if value is a CisaKevResponse
73
+ */
74
+ function isCisaKevResponse(value: unknown): value is CisaKevResponse {
75
+ if (value === null || typeof value !== 'object') {
76
+ return false;
77
+ }
78
+
79
+ const response = value as Record<string, unknown>;
80
+ return (
81
+ typeof response === 'object' &&
82
+ (response['vulnerabilities'] === undefined || Array.isArray(response['vulnerabilities']))
83
+ );
84
+ }
85
+
86
+ /**
87
+ * Extract CISA KEV items from various possible response formats
88
+ */
89
+ function extractCisaKevItems(data: unknown): readonly CisaKevItem[] {
90
+ if (isCisaKevItemArray(data)) {
91
+ return data;
92
+ }
93
+
94
+ if (isCisaKevResponse(data) && data.vulnerabilities) {
95
+ return data.vulnerabilities.filter(isCisaKevItem);
96
+ }
97
+
98
+ return [];
99
+ }
100
+
101
+ // ============================================================================
102
+ // Main API Functions
103
+ // ============================================================================
104
+
105
+ /**
106
+ * Fetch and search CISA KEV catalog
107
+ *
108
+ * @param terms - CVE IDs to search for (optional)
109
+ * @param options - Optional filters
110
+ * @returns Promise resolving to CISA KEV search results
111
+ */
112
+ export async function searchCisaKev(
113
+ terms: string[] = [],
114
+ options?: {
115
+ readonly vendor?: string;
116
+ readonly product?: string;
117
+ readonly maxResults?: number;
118
+ },
119
+ ): Promise<CisaKevResult> {
120
+ const maxResults = options?.maxResults ?? 100;
121
+ const vulnerabilities: Vulnerability[] = [];
122
+ let error: string | undefined = undefined;
123
+
124
+ try {
125
+ const response: Response = await fetch(CISA_KEV_URL, {
126
+ headers: {
127
+ 'User-Agent': 'pi-research/2.0',
128
+ 'Accept': 'application/json',
129
+ },
130
+ signal: createTimeoutSignal(30000), // 30s timeout
131
+ });
132
+
133
+ if (!response.ok) {
134
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
135
+ }
136
+
137
+ const data: unknown = await response.json();
138
+
139
+ // CISA KEV format is an array of vulnerability objects or nested in a response object
140
+ const cisaData = extractCisaKevItems(data);
141
+
142
+ // Map CISA format to our Vulnerability interface
143
+ for (const item of cisaData) {
144
+ const vuln: Vulnerability = mapCisaItemToVulnerability(item);
145
+
146
+ // Filter by search terms if provided
147
+ if (terms.length > 0 && !terms.some((term: string): boolean =>
148
+ vuln.id.toLowerCase().includes(term.toLowerCase()) ||
149
+ vuln.description.toLowerCase().includes(term.toLowerCase()),
150
+ )) {
151
+ continue;
152
+ }
153
+
154
+ // Filter by vendor/product if provided
155
+ if (options?.vendor !== undefined) {
156
+ const vendorLower = vuln.vendor?.toLowerCase() ?? '';
157
+ if (!vendorLower.includes(options.vendor.toLowerCase())) {
158
+ continue;
159
+ }
160
+ }
161
+
162
+ if (options?.product !== undefined) {
163
+ const productLower = vuln.product?.toLowerCase() ?? '';
164
+ if (!productLower.includes(options.product.toLowerCase())) {
165
+ continue;
166
+ }
167
+ }
168
+
169
+ vulnerabilities.push(vuln);
170
+ }
171
+
172
+ // Sort by due date (most urgent first)
173
+ vulnerabilities.sort((a: Vulnerability, b: Vulnerability): number => {
174
+ if (a.dueDate === undefined) {
175
+ return 1;
176
+ }
177
+ if (b.dueDate === undefined) {
178
+ return -1;
179
+ }
180
+ return new Date(a.dueDate).getTime() - new Date(b.dueDate).getTime();
181
+ });
182
+
183
+ } catch (err: unknown) {
184
+ error = err instanceof Error ? err.message : String(err);
185
+ }
186
+
187
+ return {
188
+ count: vulnerabilities.length,
189
+ vulnerabilities: vulnerabilities.slice(0, maxResults),
190
+ error,
191
+ };
192
+ }
193
+
194
+ /**
195
+ * Safely extract a string property from an unknown object
196
+ */
197
+ function safeString(value: unknown, defaultValue: string = ''): string {
198
+ if (typeof value === 'string') {
199
+ return value;
200
+ }
201
+ return defaultValue;
202
+ }
203
+
204
+ /**
205
+ * Map CISA KEV item to Vulnerability interface
206
+ *
207
+ * @param item - CISA KEV item from API
208
+ * @returns Mapped Vulnerability object
209
+ */
210
+ function mapCisaItemToVulnerability(item: CisaKevItem): Vulnerability {
211
+ // CISA KEV format (example based on scraped structure)
212
+ const cveId: string = [item.cveID, item.cve_id, item.id].map(v => safeString(v)).find(s => s.length > 0) ?? '';
213
+ const vendor: string = [item.vendorProject, item.vendor].map(v => safeString(v)).find(s => s.length > 0) ?? '';
214
+ const product: string = [item.product, item.vulnerabilityName].map(v => safeString(v)).find(s => s.length > 0) ?? '';
215
+ const description: string = [item.shortDescription, item.description].map(v => safeString(v)).find(s => s.length > 0) ?? '';
216
+ const dateAdded: string = [item.dateAdded, item.addedDate].map(v => safeString(v)).find(s => s.length > 0) ?? '';
217
+ const dueDate: string = safeString(item.dueDate);
218
+ const requiredAction: string = [item.requiredAction, item.action].map(v => safeString(v)).find(s => s.length > 0) ?? '';
219
+
220
+ const affectedProducts: string[] = [vendor, product].filter(
221
+ (value: string): boolean => value.length > 0,
222
+ );
223
+
224
+ return {
225
+ id: cveId,
226
+ source: 'cisa_kev',
227
+ severity: 'CRITICAL', // All KEV entries are critical priority
228
+ description,
229
+ published: dateAdded.length > 0 ? dateAdded : undefined,
230
+ modified: dateAdded.length > 0 ? dateAdded : undefined,
231
+ cvssScore: undefined, // CISA doesn't include CVSS score
232
+ cwes: [],
233
+ references: [],
234
+ affectedProducts,
235
+ fixes: [],
236
+ vendor: vendor.length > 0 ? vendor : undefined,
237
+ product: product.length > 0 ? product : undefined,
238
+ knownExploited: true,
239
+ dueDate: dueDate.length > 0 ? dueDate : undefined,
240
+ requiredAction: requiredAction.length > 0 ? requiredAction : undefined,
241
+ };
242
+ }