@lincoln504/pi-research 0.1.13 → 1.0.1
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/LICENSE +1 -1
- package/README.md +55 -138
- package/dist/cli.mjs +23375 -0
- package/dist/prompts/research-knowledge-relevance-triage.md +35 -0
- package/dist/prompts/research-knowledge-search-extractor.md +41 -0
- package/dist/prompts/research-tool-usage.md +92 -0
- package/dist/prompts/researcher.md +89 -0
- package/dist/prompts/system-coordinator.md +75 -0
- package/dist/prompts/system-lead-evaluator.md +118 -0
- package/dist/thread-worker.mjs +2611 -0
- package/dist/webgpu-probe.mjs +80 -0
- package/docs/AGENT-SKILL.md +130 -0
- package/docs/ARCHITECTURE.md +306 -80
- package/docs/CONFIGURATION.md +249 -0
- package/docs/KNOWLEDGE-STORE.md +211 -0
- package/docs/PI-EXTENSION.md +91 -0
- package/docs/SDK.md +122 -0
- package/docs/THIRD-PARTY-NOTICES.md +66 -0
- package/package.json +102 -36
- package/scripts/build.cjs +140 -0
- package/scripts/cleanup.cjs +149 -0
- package/scripts/prepare.cjs +28 -0
- package/scripts/setup.cjs +134 -0
- package/skills/pi-research/README.md +90 -0
- package/skills/pi-research/SKILL.md +102 -0
- package/skills/pi-research/references/configuration.md +77 -0
- package/skills/pi-research/scripts/run.mjs +237 -0
- package/src/cleanup/research-cleanup.ts +140 -0
- package/src/cli.ts +1195 -0
- package/src/config.ts +1032 -155
- package/src/constants.ts +115 -84
- package/src/core/health-check-service.ts +265 -0
- package/src/core/interfaces/health-check-interfaces.ts +50 -0
- package/src/core/interfaces/knowledge-interfaces.ts +190 -0
- package/src/core/interfaces/observer-interfaces.ts +67 -0
- package/src/core/interfaces/orchestration-interfaces.ts +86 -0
- package/src/core/interfaces/planning-interfaces.ts +100 -0
- package/src/core/interfaces/process-interfaces.ts +37 -0
- package/src/core/interfaces/research-plan-types.ts +52 -0
- package/src/core/interfaces/scheduler-interfaces.ts +91 -0
- package/src/core/interfaces/service-names.ts +39 -0
- package/src/core/interfaces/state-manager-interfaces.ts +29 -0
- package/src/core/interfaces/steering-interfaces.ts +31 -0
- package/src/core/llm/agentic-repair.ts +169 -0
- package/src/core/llm/inject-date.ts +52 -0
- package/src/core/llm/llm-timeout.ts +64 -0
- package/src/core/llm/llm-utils.ts +86 -0
- package/src/core/llm/model-registry-factory.ts +287 -0
- package/src/core/llm/pi-ai-completion.ts +29 -0
- package/src/core/llm/prompts.ts +50 -0
- package/src/core/llm/research-model-resolver.ts +91 -0
- package/src/core/planning-constants.ts +13 -0
- package/src/core/planning-service.ts +649 -0
- package/src/core/planning-utils.ts +299 -0
- package/src/core/scheduler-factory.ts +48 -0
- package/src/core/scheduler-service.ts +123 -0
- package/src/core/service-initialization.ts +194 -0
- package/src/core/service-interfaces.ts +48 -0
- package/src/core/service-registry.ts +659 -0
- package/src/healthcheck/index.ts +271 -265
- package/src/healthcheck/registry.ts +130 -0
- package/src/index.ts +639 -50
- package/src/infrastructure/browser/browser-cleanup.ts +309 -0
- package/src/infrastructure/browser/browser-client.ts +199 -0
- package/src/infrastructure/browser/browser-error-utils.ts +160 -0
- package/src/infrastructure/browser/browser-lifecycle.ts +120 -0
- package/src/infrastructure/browser/browser-server.ts +174 -0
- package/src/infrastructure/browser/browser-task-scheduler.ts +461 -0
- package/src/infrastructure/browser/cleanup-utils.ts +155 -0
- package/src/infrastructure/browser/client-agent.ts +31 -0
- package/src/infrastructure/browser/config.ts +288 -0
- package/src/infrastructure/browser/ensure-browser.ts +205 -0
- package/src/infrastructure/browser/index.ts +37 -0
- package/src/infrastructure/browser/priority-task-queue.ts +226 -0
- package/src/infrastructure/browser/scheduler-factory.ts +354 -0
- package/src/infrastructure/browser/task-execution-service.ts +209 -0
- package/src/infrastructure/browser/thread-worker-browser.ts +282 -0
- package/src/infrastructure/browser/thread-worker-lifecycle.ts +323 -0
- package/src/infrastructure/browser/thread-worker-messaging.ts +662 -0
- package/src/infrastructure/browser/thread-worker-types.ts +25 -0
- package/src/infrastructure/browser/thread-worker.mjs +2611 -0
- package/src/infrastructure/browser/thread-worker.ts +168 -0
- package/src/infrastructure/browser/worker-pool-manager.ts +449 -0
- package/src/infrastructure/embedding/embedding-client.ts +205 -0
- package/src/infrastructure/embedding/embedding-factory.ts +324 -0
- package/src/infrastructure/embedding/embedding-server.ts +535 -0
- package/src/infrastructure/file-lock-service.ts +592 -0
- package/src/infrastructure/gpu-resource-service.ts +191 -0
- package/src/infrastructure/knowledge-store-service.ts +475 -0
- package/src/infrastructure/metrics-service.ts +129 -0
- package/src/infrastructure/process-lifecycle-service.ts +253 -0
- package/src/infrastructure/scheduler-factory-service.ts +58 -0
- package/src/infrastructure/service-initialization.ts +292 -0
- package/src/infrastructure/state/state-backup-manager.ts +290 -0
- package/src/infrastructure/state/state-browser-api.ts +62 -0
- package/src/infrastructure/state/state-browser-manager.ts +70 -0
- package/src/infrastructure/state/state-manager-service.ts +264 -0
- package/src/infrastructure/state/state-manager.ts +547 -0
- package/src/infrastructure/state/state-metrics.ts +92 -0
- package/src/infrastructure/state/state-migration.ts +103 -0
- package/src/infrastructure/state/state-path-configuration.ts +119 -0
- package/src/infrastructure/state/state-session-api.ts +94 -0
- package/src/infrastructure/state/state-session-manager.ts +127 -0
- package/src/infrastructure/state/state-validator.ts +93 -0
- package/src/infrastructure/types/state-types.ts +85 -0
- package/src/knowledge/chunker.ts +157 -0
- package/src/knowledge/embedder-init.ts +413 -0
- package/src/knowledge/embedder-utils.ts +296 -0
- package/src/knowledge/embedder.ts +592 -0
- package/src/knowledge/index.ts +126 -0
- package/src/knowledge/lancedb-loader.ts +35 -0
- package/src/knowledge/migration.ts +17 -0
- package/src/knowledge/model-config.ts +132 -0
- package/src/knowledge/onnx-env.ts +48 -0
- package/src/knowledge/store-operations.ts +291 -0
- package/src/knowledge/store-schema.ts +79 -0
- package/src/knowledge/store.ts +1251 -0
- package/src/knowledge/transformers-loader.ts +31 -0
- package/src/knowledge/webgpu-probe.mjs +80 -0
- package/src/knowledge/webgpu-viability.ts +255 -0
- package/src/knowledge/writer-queue.ts +252 -0
- package/src/logger.ts +316 -122
- package/src/observers/research-observer-impl.ts +459 -0
- package/src/orchestration/deep-research-orchestrator.ts +524 -773
- package/src/orchestration/deep-research-types.ts +47 -35
- package/src/orchestration/headless-observer.ts +114 -0
- package/src/orchestration/orchestration-types.ts +33 -0
- package/src/orchestration/quick-research-orchestrator.ts +463 -0
- package/src/orchestration/research-orchestration-service.ts +549 -0
- package/src/orchestration/research-session-service.ts +178 -0
- package/src/orchestration/research-synthesis-service.ts +348 -0
- package/src/orchestration/researcher-executor.ts +394 -0
- package/src/orchestration/researcher.ts +115 -42
- package/src/orchestration/service-initialization.ts +109 -0
- package/src/orchestration/session-context.ts +7 -3
- package/src/orchestration/session-state.ts +672 -0
- package/src/prompts/research-knowledge-relevance-triage.md +35 -0
- package/src/prompts/research-knowledge-search-extractor.md +41 -0
- package/src/prompts/research-tool-usage.md +63 -16
- package/src/prompts/researcher.md +59 -136
- package/src/prompts/system-coordinator.md +64 -13
- package/src/prompts/system-lead-evaluator.md +82 -189
- package/src/research-config.ts +872 -0
- package/src/sdk.ts +847 -0
- package/src/security/cisa-kev.ts +72 -9
- package/src/security/cvss.ts +92 -0
- package/src/security/ecosystem.ts +45 -0
- package/src/security/github-advisories.ts +168 -428
- package/src/security/github-advisory-types.ts +282 -0
- package/src/security/index.ts +63 -177
- package/src/security/nvd-client.ts +550 -0
- package/src/security/nvd-types.ts +179 -0
- package/src/security/osv-client.ts +246 -0
- package/src/security/osv-types.ts +285 -0
- package/src/security/types.ts +67 -53
- package/src/skill-install/skill-installer.ts +519 -0
- package/src/stackexchange/index.ts +81 -27
- package/src/stackexchange/output/compact.ts +7 -6
- package/src/stackexchange/output/table.ts +11 -7
- package/src/stackexchange/queries.ts +0 -10
- package/src/stackexchange/rest-client.ts +141 -66
- package/src/stackexchange/types.ts +0 -17
- package/src/tool.ts +15 -423
- package/src/tools/grep.ts +32 -310
- package/src/tools/health-tool-definition.ts +101 -0
- package/src/tools/index.ts +38 -9
- package/src/tools/research-knowledge-search.ts +974 -0
- package/src/tools/research-knowledge-types.ts +77 -0
- package/src/tools/research-tool-definition.ts +494 -0
- package/src/tools/scrape.ts +286 -388
- package/src/tools/search.ts +98 -153
- package/src/tools/security.ts +107 -80
- package/src/tools/stackexchange.ts +58 -35
- package/src/tools/youtube-transcript.ts +163 -0
- package/src/tui/knowledge-search-panel.ts +86 -0
- package/src/tui/pulse.ts +86 -0
- package/src/tui/research-health.ts +124 -0
- package/src/tui/research-panel-color-utils.ts +340 -0
- package/src/tui/research-panel-format.ts +54 -0
- package/src/tui/research-panel-state.ts +404 -0
- package/src/tui/research-panel-wave.ts +280 -0
- package/src/tui/research-panel.ts +339 -435
- package/src/tui/research-tui-manager.ts +233 -0
- package/src/tui/tui-controller.ts +137 -0
- package/src/types/extension-context.ts +18 -49
- package/src/types/index.ts +61 -0
- package/src/types/llm.ts +68 -29
- package/src/types/research-panel-types.ts +61 -0
- package/src/utils/circuit-breaker.ts +146 -0
- package/src/utils/citation-utils.ts +97 -0
- package/src/utils/disk-space-checker.ts +55 -0
- package/src/utils/error-tracker.ts +265 -0
- package/src/utils/host-config.ts +45 -0
- package/src/utils/input-validation.ts +8 -10
- package/src/utils/json-utils.ts +150 -16
- package/src/utils/llm-usage.ts +54 -0
- package/src/utils/log-rotation.ts +125 -0
- package/src/utils/log-utils.ts +159 -0
- package/src/utils/make-resource-loader.ts +3 -1
- package/src/utils/metrics-summary.ts +459 -0
- package/src/utils/metrics.ts +302 -0
- package/src/utils/research-export.ts +28 -12
- package/src/utils/safe-unref.ts +31 -0
- package/src/utils/shared-links.ts +195 -90
- package/src/utils/shutdown-manager.ts +65 -0
- package/src/utils/stdio-capture.ts +407 -0
- package/src/utils/text-utils.ts +258 -11
- package/src/utils/tool-usage-tracker.ts +83 -16
- package/src/utils/url-utils.ts +255 -0
- package/src/utils/user-agent.ts +28 -0
- package/src/web-research/browser-search.ts +179 -0
- package/src/web-research/retry-utils.ts +173 -77
- package/src/web-research/scraper-types.ts +72 -0
- package/src/web-research/scraper-utils.ts +434 -0
- package/src/web-research/search.ts +78 -218
- package/src/web-research/types.ts +9 -24
- package/src/web-research/utils.ts +2 -143
- package/src/web-research/web-scraper.ts +477 -0
- package/src/youtube/index.ts +154 -0
- package/src/youtube/potoken.ts +259 -0
- package/src/youtube/transcript-client.ts +390 -0
- package/src/youtube/video-id.ts +118 -0
- package/config/searxng/default-settings.yml +0 -189
- package/config/searxng/limiter.toml +0 -34
- package/config/tooling/eslint.config.js +0 -73
- package/config/tooling/vitest.config.integration.ts +0 -17
- package/config/tooling/vitest.config.ts +0 -40
- package/config/tooling/vitest.config.unit.ts +0 -12
- package/docs/assets/README-banner.jpg +0 -0
- package/scripts/setup.js +0 -212
- package/src/infrastructure/searxng-lifecycle.ts +0 -644
- package/src/infrastructure/searxng-manager.ts +0 -1183
- package/src/infrastructure/state-manager.ts +0 -918
- package/src/orchestration/deep-research-reducer.ts +0 -129
- package/src/orchestration/id-utils.ts +0 -63
- package/src/security/nvd.ts +0 -532
- package/src/security/osv.ts +0 -487
- package/src/stackexchange/cache.ts +0 -111
- package/src/stackexchange/output/index.ts +0 -7
- package/src/stackexchange/output/json.ts +0 -21
- package/src/tui/searxng-status.ts +0 -80
- package/src/utils/inject-date.ts +0 -29
- package/src/utils/research-error.ts +0 -205
- package/src/utils/searxng-config.ts +0 -94
- package/src/utils/session-state.ts +0 -295
- package/src/web-research/dependencies.ts +0 -20
- package/src/web-research/scrapers.ts +0 -935
- package/tsconfig.json +0 -40
package/src/utils/inject-date.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Date Injection Utility
|
|
3
|
-
*
|
|
4
|
-
* Injects the current date into agent prompts.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Format current date as a readable string
|
|
9
|
-
*/
|
|
10
|
-
function getCurrentDateString(): string {
|
|
11
|
-
const now = new Date();
|
|
12
|
-
const options: Intl.DateTimeFormatOptions = {
|
|
13
|
-
weekday: 'long',
|
|
14
|
-
year: 'numeric',
|
|
15
|
-
month: 'long',
|
|
16
|
-
day: 'numeric',
|
|
17
|
-
};
|
|
18
|
-
return now.toLocaleDateString('en-US', options);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Inject current date into a prompt
|
|
23
|
-
* Prepends a date context line to the prompt
|
|
24
|
-
*/
|
|
25
|
-
export function injectCurrentDate(prompt: string, _agentType: 'coordinator' | 'researcher'): string {
|
|
26
|
-
const dateString = getCurrentDateString();
|
|
27
|
-
const dateContext = `**Current Date:** ${dateString}\n\n`;
|
|
28
|
-
return dateContext + prompt;
|
|
29
|
-
}
|
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Research Error Handling
|
|
3
|
-
*
|
|
4
|
-
* Custom error class and error codes for research operations.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { logger } from '../logger.ts';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Research error codes
|
|
11
|
-
*/
|
|
12
|
-
export enum ResearchErrorCode {
|
|
13
|
-
// General errors
|
|
14
|
-
UNKNOWN = 'UNKNOWN',
|
|
15
|
-
INVALID_INPUT = 'INVALID_INPUT',
|
|
16
|
-
NOT_IMPLEMENTED = 'NOT_IMPLEMENTED',
|
|
17
|
-
|
|
18
|
-
// Configuration errors
|
|
19
|
-
CONFIG_INVALID = 'CONFIG_INVALID',
|
|
20
|
-
CONFIG_MISSING = 'CONFIG_MISSING',
|
|
21
|
-
|
|
22
|
-
// Network/API errors
|
|
23
|
-
NETWORK_ERROR = 'NETWORK_ERROR',
|
|
24
|
-
API_ERROR = 'API_ERROR',
|
|
25
|
-
API_RATE_LIMITED = 'API_RATE_LIMITED',
|
|
26
|
-
API_TIMEOUT = 'API_TIMEOUT',
|
|
27
|
-
API_UNAVAILABLE = 'API_UNAVAILABLE',
|
|
28
|
-
|
|
29
|
-
// SearXNG errors
|
|
30
|
-
SEARXNG_START_FAILED = 'SEARXNG_START_FAILED',
|
|
31
|
-
SEARXNG_NOT_RUNNING = 'SEARXNG_NOT_RUNNING',
|
|
32
|
-
SEARXNG_QUERY_FAILED = 'SEARXNG_QUERY_FAILED',
|
|
33
|
-
|
|
34
|
-
// Orchestrator errors
|
|
35
|
-
ORCHESTRATION_FAILED = 'ORCHESTRATION_FAILED',
|
|
36
|
-
PLANNING_FAILED = 'PLANNING_FAILED',
|
|
37
|
-
RESEARCH_FAILED = 'RESEARCH_FAILED',
|
|
38
|
-
SYNTHESIS_FAILED = 'SYNTHESIS_FAILED',
|
|
39
|
-
|
|
40
|
-
// Researcher errors
|
|
41
|
-
RESEARCHER_FAILED = 'RESEARCHER_FAILED',
|
|
42
|
-
RESEARCHER_TIMEOUT = 'RESEARCHER_TIMEOUT',
|
|
43
|
-
|
|
44
|
-
// Scraping errors
|
|
45
|
-
SCRAPE_FAILED = 'SCRAPE_FAILED',
|
|
46
|
-
SCRAPE_TIMEOUT = 'SCRAPE_TIMEOUT',
|
|
47
|
-
SCRAPE_RATE_LIMITED = 'SCRAPE_RATE_LIMITED',
|
|
48
|
-
|
|
49
|
-
// Export errors
|
|
50
|
-
EXPORT_FAILED = 'EXPORT_FAILED',
|
|
51
|
-
|
|
52
|
-
// Validation errors
|
|
53
|
-
VALIDATION_FAILED = 'VALIDATION_FAILED',
|
|
54
|
-
QUERY_TOO_SHORT = 'QUERY_TOO_SHORT',
|
|
55
|
-
QUERY_TOO_LONG = 'QUERY_TOO_LONG',
|
|
56
|
-
QUERY_INVALID = 'QUERY_INVALID',
|
|
57
|
-
|
|
58
|
-
// State errors
|
|
59
|
-
STATE_INVALID = 'STATE_INVALID',
|
|
60
|
-
STATE_TRANSITION_FAILED = 'STATE_TRANSITION_FAILED',
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Research error class
|
|
65
|
-
*
|
|
66
|
-
* Provides structured error information for research operations.
|
|
67
|
-
*/
|
|
68
|
-
export class ResearchError extends Error {
|
|
69
|
-
public readonly code: ResearchErrorCode;
|
|
70
|
-
public readonly details?: unknown;
|
|
71
|
-
public readonly correlationId: string;
|
|
72
|
-
public readonly timestamp: number;
|
|
73
|
-
|
|
74
|
-
constructor(
|
|
75
|
-
code: ResearchErrorCode,
|
|
76
|
-
message: string,
|
|
77
|
-
details?: unknown,
|
|
78
|
-
) {
|
|
79
|
-
super(message);
|
|
80
|
-
this.name = 'ResearchError';
|
|
81
|
-
this.code = code;
|
|
82
|
-
this.details = details;
|
|
83
|
-
this.correlationId = generateCorrelationId();
|
|
84
|
-
this.timestamp = Date.now();
|
|
85
|
-
|
|
86
|
-
// Ensure proper prototype chain
|
|
87
|
-
Object.setPrototypeOf(this, ResearchError.prototype);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Convert error to a serializable object
|
|
92
|
-
*/
|
|
93
|
-
toJSON(): {
|
|
94
|
-
name: string;
|
|
95
|
-
code: ResearchErrorCode;
|
|
96
|
-
message: string;
|
|
97
|
-
details?: unknown;
|
|
98
|
-
correlationId: string;
|
|
99
|
-
timestamp: number;
|
|
100
|
-
} {
|
|
101
|
-
return {
|
|
102
|
-
name: this.name,
|
|
103
|
-
code: this.code,
|
|
104
|
-
message: this.message,
|
|
105
|
-
details: this.details,
|
|
106
|
-
correlationId: this.correlationId,
|
|
107
|
-
timestamp: this.timestamp,
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Log the error with correlation ID
|
|
113
|
-
*/
|
|
114
|
-
log(context?: string): void {
|
|
115
|
-
const prefix = context ? `[${context}] ` : '';
|
|
116
|
-
logger.error(`${prefix}ResearchError: ${this.code}`, {
|
|
117
|
-
message: this.message,
|
|
118
|
-
details: this.details,
|
|
119
|
-
correlationId: this.correlationId,
|
|
120
|
-
timestamp: this.timestamp,
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Generate a correlation ID for error tracking
|
|
127
|
-
*/
|
|
128
|
-
function generateCorrelationId(): string {
|
|
129
|
-
return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Create a ResearchError from an unknown error
|
|
134
|
-
*
|
|
135
|
-
* @param error - The error to convert
|
|
136
|
-
* @param defaultCode - Default error code if type cannot be determined
|
|
137
|
-
* @returns A ResearchError instance
|
|
138
|
-
*/
|
|
139
|
-
export function createResearchError(
|
|
140
|
-
error: unknown,
|
|
141
|
-
defaultCode: ResearchErrorCode = ResearchErrorCode.UNKNOWN,
|
|
142
|
-
): ResearchError {
|
|
143
|
-
if (error instanceof ResearchError) {
|
|
144
|
-
return error;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (error instanceof Error) {
|
|
148
|
-
// Try to determine error code from error message
|
|
149
|
-
const message = error.message.toLowerCase();
|
|
150
|
-
|
|
151
|
-
if (message.includes('timeout')) {
|
|
152
|
-
return new ResearchError(ResearchErrorCode.API_TIMEOUT, error.message, error);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (message.includes('rate limit') || message.includes('too many requests')) {
|
|
156
|
-
return new ResearchError(ResearchErrorCode.API_RATE_LIMITED, error.message, error);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
if (message.includes('network') || message.includes('econnrefused')) {
|
|
160
|
-
return new ResearchError(ResearchErrorCode.NETWORK_ERROR, error.message, error);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (message.includes('404') || message.includes('not found')) {
|
|
164
|
-
return new ResearchError(ResearchErrorCode.API_UNAVAILABLE, error.message, error);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Generic error
|
|
168
|
-
return new ResearchError(defaultCode, error.message, error);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// Unknown error type
|
|
172
|
-
return new ResearchError(
|
|
173
|
-
defaultCode,
|
|
174
|
-
typeof error === 'string' ? error : 'Unknown error',
|
|
175
|
-
error,
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Check if an error is transient (can be retried)
|
|
181
|
-
*
|
|
182
|
-
* @param error - The error to check
|
|
183
|
-
* @returns True if the error is transient
|
|
184
|
-
*/
|
|
185
|
-
export function isTransientError(error: ResearchError): boolean {
|
|
186
|
-
const transientCodes = [
|
|
187
|
-
ResearchErrorCode.NETWORK_ERROR,
|
|
188
|
-
ResearchErrorCode.API_RATE_LIMITED,
|
|
189
|
-
ResearchErrorCode.API_TIMEOUT,
|
|
190
|
-
ResearchErrorCode.API_UNAVAILABLE,
|
|
191
|
-
ResearchErrorCode.SEARXNG_START_FAILED,
|
|
192
|
-
];
|
|
193
|
-
|
|
194
|
-
return transientCodes.includes(error.code);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Check if an error is fatal (cannot be retried)
|
|
199
|
-
*
|
|
200
|
-
* @param error - The error to check
|
|
201
|
-
* @returns True if the error is fatal
|
|
202
|
-
*/
|
|
203
|
-
export function isFatalError(error: ResearchError): boolean {
|
|
204
|
-
return !isTransientError(error);
|
|
205
|
-
}
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SearXNG Configuration Loader
|
|
3
|
-
*
|
|
4
|
-
* Reads the SearXNG config YAML and extracts active search engines.
|
|
5
|
-
* Ensures healthcheck and config stay synchronized.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { readFileSync } from 'node:fs';
|
|
9
|
-
import { dirname, join } from 'node:path';
|
|
10
|
-
import { fileURLToPath } from 'node:url';
|
|
11
|
-
import { load } from 'js-yaml';
|
|
12
|
-
import { logger } from '../logger.ts';
|
|
13
|
-
|
|
14
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
-
const __dirname = dirname(__filename);
|
|
16
|
-
|
|
17
|
-
function parseYamlEngines(content: string): Array<{ name: string; disabled?: boolean }> {
|
|
18
|
-
const parsed = load(content) as { engines?: Array<{ name?: string; disabled?: boolean }> } | undefined;
|
|
19
|
-
const engines = parsed?.engines;
|
|
20
|
-
|
|
21
|
-
if (!Array.isArray(engines)) {
|
|
22
|
-
logger.warn('[searxng-config] No engines section found in config');
|
|
23
|
-
return [];
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return engines
|
|
27
|
-
.filter((engine): engine is { name: string; disabled?: boolean } => typeof engine?.name === 'string')
|
|
28
|
-
.map((engine) => ({ name: engine.name.trim(), disabled: engine.disabled === true }));
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Load SearXNG configuration and extract active general search engines
|
|
33
|
-
* Filters out:
|
|
34
|
-
* - Disabled engines
|
|
35
|
-
* - Category-specific engines (images, news, videos)
|
|
36
|
-
* - Category-restricted engines (stackoverflow, arxiv, semantic scholar, etc.)
|
|
37
|
-
* - Encyclopedic engines (wikipedia)
|
|
38
|
-
*/
|
|
39
|
-
export function getActiveSearxngEngines(): string[] {
|
|
40
|
-
try {
|
|
41
|
-
const configPath = join(__dirname, '../../config/searxng/default-settings.yml');
|
|
42
|
-
const content = readFileSync(configPath, 'utf-8');
|
|
43
|
-
const allEngines = parseYamlEngines(content);
|
|
44
|
-
|
|
45
|
-
// List of general purpose search engines (whitelist approach is safer)
|
|
46
|
-
const knownGeneralEngines = ['google', 'bing', 'brave', 'duckduckgo', 'startpage', 'mojeek', 'qwant'];
|
|
47
|
-
|
|
48
|
-
// Filter for general search engines only
|
|
49
|
-
const activeGeneralEngines = allEngines
|
|
50
|
-
.filter(
|
|
51
|
-
e =>
|
|
52
|
-
!e.disabled && // Not disabled
|
|
53
|
-
knownGeneralEngines.includes(e.name.toLowerCase()) // Only known general engines
|
|
54
|
-
)
|
|
55
|
-
.map(e => e.name.toLowerCase());
|
|
56
|
-
|
|
57
|
-
if (activeGeneralEngines.length === 0) {
|
|
58
|
-
logger.warn('[searxng-config] No active general search engines found in config');
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
logger.log(`[searxng-config] Active general search engines: [${activeGeneralEngines.join(', ')}]`);
|
|
62
|
-
return activeGeneralEngines;
|
|
63
|
-
} catch (error) {
|
|
64
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
65
|
-
logger.error('[searxng-config] Failed to load config:', msg);
|
|
66
|
-
// Fallback to known good defaults if config can't be loaded
|
|
67
|
-
return ['google', 'bing', 'brave'];
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Validate that healthcheck engine list matches config
|
|
73
|
-
* Used in tests to catch drift
|
|
74
|
-
*/
|
|
75
|
-
export function validateEngineListConsistency(expectedEngines: string[]): {
|
|
76
|
-
isValid: boolean;
|
|
77
|
-
missing: string[];
|
|
78
|
-
extra: string[];
|
|
79
|
-
} {
|
|
80
|
-
const actualEngines = getActiveSearxngEngines();
|
|
81
|
-
const actualSet = new Set(actualEngines);
|
|
82
|
-
const expectedSet = new Set(expectedEngines);
|
|
83
|
-
|
|
84
|
-
const missing = actualEngines.filter(e => !expectedSet.has(e));
|
|
85
|
-
const extra = expectedEngines.filter(e => !actualSet.has(e));
|
|
86
|
-
|
|
87
|
-
const isValid = missing.length === 0 && extra.length === 0;
|
|
88
|
-
|
|
89
|
-
return {
|
|
90
|
-
isValid,
|
|
91
|
-
missing,
|
|
92
|
-
extra,
|
|
93
|
-
};
|
|
94
|
-
}
|
|
@@ -1,295 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Research Session State Management
|
|
3
|
-
*
|
|
4
|
-
* Tracks failures and state for multiple simultaneous research sessions,
|
|
5
|
-
* scoped by parent Pi session to prevent cross-context interference.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { generateSessionId as generateUniqueSessionId } from './shared-links.ts';
|
|
9
|
-
import { logger } from '../logger.ts';
|
|
10
|
-
import { getConfig } from '../config.ts';
|
|
11
|
-
import type { ResearchPanelState } from '../tui/research-panel.ts';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* State container for a single Pi session
|
|
15
|
-
*/
|
|
16
|
-
interface PiSessionState {
|
|
17
|
-
/** Map of research run ID → array of failed researcher IDs */
|
|
18
|
-
failures: Map<string, string[]>;
|
|
19
|
-
/** Ordered list of research run IDs for TUI stacking (Index 0 = oldest/bottom-most) */
|
|
20
|
-
order: string[];
|
|
21
|
-
/** Registry of panel states for each research run */
|
|
22
|
-
panels: Map<string, ResearchPanelState>;
|
|
23
|
-
/** Subscribers for order changes in this Pi session */
|
|
24
|
-
subscribers: Array<() => void>;
|
|
25
|
-
/** Global debounce timer for this specific Pi session */
|
|
26
|
-
refreshTimeout: NodeJS.Timeout | null;
|
|
27
|
-
/** Single update function for the Master Widget of this Pi session */
|
|
28
|
-
masterUpdate: (() => void) | null;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Global map of Pi session ID → PiSessionState
|
|
33
|
-
*/
|
|
34
|
-
const piSessions = new Map<string, PiSessionState>();
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Get or create state for a specific Pi session
|
|
38
|
-
*/
|
|
39
|
-
function getPiState(piSessionId: string): PiSessionState {
|
|
40
|
-
let state = piSessions.get(piSessionId);
|
|
41
|
-
if (!state) {
|
|
42
|
-
state = {
|
|
43
|
-
failures: new Map(),
|
|
44
|
-
order: [],
|
|
45
|
-
panels: new Map(),
|
|
46
|
-
subscribers: [],
|
|
47
|
-
refreshTimeout: null,
|
|
48
|
-
masterUpdate: null,
|
|
49
|
-
};
|
|
50
|
-
piSessions.set(piSessionId, state);
|
|
51
|
-
}
|
|
52
|
-
return state;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Maximum allowed unique failed researchers before stopping research
|
|
57
|
-
*/
|
|
58
|
-
const MAX_FAILED_RESEARCHERS = 2;
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Subscribe to session order changes for a specific Pi session
|
|
62
|
-
*/
|
|
63
|
-
export function onSessionOrderChange(piSessionId: string, callback: () => void): () => void {
|
|
64
|
-
const state = getPiState(piSessionId);
|
|
65
|
-
state.subscribers.push(callback);
|
|
66
|
-
return () => {
|
|
67
|
-
const index = state.subscribers.indexOf(callback);
|
|
68
|
-
if (index !== -1) {
|
|
69
|
-
state.subscribers.splice(index, 1);
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Notify subscribers of session order change
|
|
76
|
-
*/
|
|
77
|
-
function notifyOrderChange(piSessionId: string): void {
|
|
78
|
-
const state = getPiState(piSessionId);
|
|
79
|
-
for (const subscriber of state.subscribers) {
|
|
80
|
-
try {
|
|
81
|
-
subscriber();
|
|
82
|
-
} catch (error) {
|
|
83
|
-
logger.error(`[session-state] Error in subscriber for ${piSessionId}:`, error);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Register a panel state for a research run
|
|
90
|
-
*/
|
|
91
|
-
export function registerSessionPanel(piSessionId: string, researchId: string, panel: ResearchPanelState): void {
|
|
92
|
-
const state = getPiState(piSessionId);
|
|
93
|
-
state.panels.set(researchId, panel);
|
|
94
|
-
|
|
95
|
-
if (!state.order.includes(researchId)) {
|
|
96
|
-
state.order.push(researchId);
|
|
97
|
-
notifyOrderChange(piSessionId);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Unregister a panel state
|
|
103
|
-
*/
|
|
104
|
-
export function unregisterSessionPanel(piSessionId: string, researchId: string): void {
|
|
105
|
-
const state = getPiState(piSessionId);
|
|
106
|
-
state.panels.delete(researchId);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Register the update function for a Pi session's Master Widget
|
|
111
|
-
*/
|
|
112
|
-
export function registerMasterUpdate(piSessionId: string, update: () => void): void {
|
|
113
|
-
const state = getPiState(piSessionId);
|
|
114
|
-
state.masterUpdate = update;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Refresh the Master Widget for a Pi session
|
|
119
|
-
*/
|
|
120
|
-
export function refreshAllSessions(piSessionId: string): void {
|
|
121
|
-
const state = getPiState(piSessionId);
|
|
122
|
-
|
|
123
|
-
// Clear existing timeout for this specific Pi session
|
|
124
|
-
if (state.refreshTimeout) {
|
|
125
|
-
clearTimeout(state.refreshTimeout);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const debounceMs = getConfig().TUI_REFRESH_DEBOUNCE_MS;
|
|
129
|
-
state.refreshTimeout = setTimeout(() => {
|
|
130
|
-
try {
|
|
131
|
-
// Validate order integrity
|
|
132
|
-
const validIds = state.order.filter(id =>
|
|
133
|
-
state.panels.has(id) && state.failures.has(id)
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
if (validIds.length !== state.order.length) {
|
|
137
|
-
for (const id of state.order) {
|
|
138
|
-
if (!validIds.includes(id)) {
|
|
139
|
-
state.failures.delete(id);
|
|
140
|
-
state.panels.delete(id);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
state.order.length = 0;
|
|
144
|
-
state.order.push(...validIds);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Trigger the single Master Update for this Pi session
|
|
148
|
-
if (state.masterUpdate) {
|
|
149
|
-
try {
|
|
150
|
-
state.masterUpdate();
|
|
151
|
-
} catch (error) {
|
|
152
|
-
logger.error(`[session-state] Error updating Master Widget for ${piSessionId}:`, error);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
} finally {
|
|
156
|
-
state.refreshTimeout = null;
|
|
157
|
-
}
|
|
158
|
-
}, debounceMs);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Start a new research run within a Pi session
|
|
163
|
-
*/
|
|
164
|
-
export function startResearchSession(piSessionId: string): string {
|
|
165
|
-
const researchId = generateUniqueSessionId('research');
|
|
166
|
-
const state = getPiState(piSessionId);
|
|
167
|
-
state.failures.set(researchId, []);
|
|
168
|
-
return researchId;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Clear pending refreshes for a Pi session
|
|
173
|
-
*/
|
|
174
|
-
export function clearPendingRefresh(piSessionId: string): void {
|
|
175
|
-
const state = piSessions.get(piSessionId);
|
|
176
|
-
if (state?.refreshTimeout) {
|
|
177
|
-
clearTimeout(state.refreshTimeout);
|
|
178
|
-
state.refreshTimeout = null;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* End a research run
|
|
184
|
-
*/
|
|
185
|
-
export function endResearchSession(piSessionId: string, researchId: string): void {
|
|
186
|
-
const state = piSessions.get(piSessionId);
|
|
187
|
-
if (!state) return;
|
|
188
|
-
|
|
189
|
-
state.failures.delete(researchId);
|
|
190
|
-
state.panels.delete(researchId);
|
|
191
|
-
|
|
192
|
-
const index = state.order.indexOf(researchId);
|
|
193
|
-
if (index !== -1) {
|
|
194
|
-
state.order.splice(index, 1);
|
|
195
|
-
notifyOrderChange(piSessionId);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// If this was the last research run in the Pi session, clean up the state
|
|
199
|
-
if (state.order.length === 0 && state.panels.size === 0 && state.subscribers.length === 0) {
|
|
200
|
-
clearPendingRefresh(piSessionId);
|
|
201
|
-
piSessions.delete(piSessionId);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Check if a research run is the bottom-most in its Pi session
|
|
207
|
-
*/
|
|
208
|
-
export function isBottomMostSession(piSessionId: string, researchId: string): boolean {
|
|
209
|
-
const state = piSessions.get(piSessionId);
|
|
210
|
-
if (!state || state.order.length === 0) return false;
|
|
211
|
-
return state.order[0] === researchId;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Record a researcher failure
|
|
216
|
-
*/
|
|
217
|
-
export function recordResearcherFailure(piSessionId: string, researchId: string, researcherId: string): void {
|
|
218
|
-
const state = getPiState(piSessionId);
|
|
219
|
-
const failures = state.failures.get(researchId) || [];
|
|
220
|
-
failures.push(researcherId);
|
|
221
|
-
state.failures.set(researchId, failures);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Get list of unique failed researchers in a research run
|
|
226
|
-
*/
|
|
227
|
-
export function getFailedResearchers(piSessionId: string, researchId: string): string[] {
|
|
228
|
-
const state = getPiState(piSessionId);
|
|
229
|
-
const failures = state.failures.get(researchId) || [];
|
|
230
|
-
return [...new Set(failures)];
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Check if research should stop due to too many unique failures
|
|
235
|
-
*/
|
|
236
|
-
export function shouldStopResearch(piSessionId: string, researchId: string): boolean {
|
|
237
|
-
return getFailedResearchers(piSessionId, researchId).length >= MAX_FAILED_RESEARCHERS;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Get formatted error message for research stoppage
|
|
242
|
-
*/
|
|
243
|
-
export function getResearchStopMessage(piSessionId: string, researchId: string): string {
|
|
244
|
-
const failed = getFailedResearchers(piSessionId, researchId);
|
|
245
|
-
const count = failed.length;
|
|
246
|
-
|
|
247
|
-
return [
|
|
248
|
-
`Research stopped: ${count} researcher(s) failed: ${failed.join(', ')}.`,
|
|
249
|
-
'',
|
|
250
|
-
'This indicates infrastructure failure — multiple researchers could not complete research.',
|
|
251
|
-
'Possible causes: network unavailable, SearXNG container not running, search engines returning errors.',
|
|
252
|
-
'',
|
|
253
|
-
'▎ If the health check passed (search and scrape verified), this failure is at the AI session layer —',
|
|
254
|
-
' check model availability, API key, and context settings.',
|
|
255
|
-
'',
|
|
256
|
-
'Troubleshooting:',
|
|
257
|
-
'• Verify network connection is active',
|
|
258
|
-
'• Check SearXNG container: `docker ps | grep searxng`',
|
|
259
|
-
'• View SearXNG logs: `docker logs pi-searxng` (check for engine HTTP errors like 503, 400)',
|
|
260
|
-
'• Verify PROXY_URL if configured (optional): should be socks5://host:port or http://host:port',
|
|
261
|
-
'• Check PI_RESEARCH_RESEARCHER_TIMEOUT_MS if set (default: 4 minutes)',
|
|
262
|
-
'',
|
|
263
|
-
'Partial results may be available below.',
|
|
264
|
-
].join('\n');
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Get all active research panels in a Pi session, in display order (newest first)
|
|
269
|
-
*/
|
|
270
|
-
export function getPiActivePanels(piSessionId: string): ResearchPanelState[] {
|
|
271
|
-
const state = piSessions.get(piSessionId);
|
|
272
|
-
if (!state) return [];
|
|
273
|
-
// Return in reverse order (newest first) for top-to-bottom stacking in a single widget
|
|
274
|
-
return [...state.order].reverse().map(id => state.panels.get(id)!).filter(Boolean);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Get ordered list of active research runs in a Pi session
|
|
279
|
-
*/
|
|
280
|
-
export function getPiActiveSessionOrder(piSessionId: string): string[] {
|
|
281
|
-
const state = piSessions.get(piSessionId);
|
|
282
|
-
return state ? [...state.order] : [];
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Reset all state (for testing only)
|
|
287
|
-
*/
|
|
288
|
-
export function resetAllPiSessions(): void {
|
|
289
|
-
for (const state of piSessions.values()) {
|
|
290
|
-
if (state.refreshTimeout) {
|
|
291
|
-
clearTimeout(state.refreshTimeout);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
piSessions.clear();
|
|
295
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Web Research Extension - Dependencies
|
|
3
|
-
*
|
|
4
|
-
* Dependencies are managed by npm via package.json.
|
|
5
|
-
* No custom install/uninstall commands needed.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { getDependencyStatus } from './scrapers.ts';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Check all dependencies and return detailed status
|
|
12
|
-
*/
|
|
13
|
-
export function checkAllDependencies(): {
|
|
14
|
-
playwrightAvailable: boolean;
|
|
15
|
-
} {
|
|
16
|
-
const scrapersStatus = getDependencyStatus();
|
|
17
|
-
return {
|
|
18
|
-
playwrightAvailable: scrapersStatus.playwrightAvailable,
|
|
19
|
-
};
|
|
20
|
-
}
|