@lincoln504/pi-research 0.1.12 → 1.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.
- package/LICENSE +1 -1
- package/README.md +64 -115
- package/dist/cli.mjs +23211 -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 +102 -0
- package/docs/ARCHITECTURE.md +306 -80
- package/docs/CONFIGURATION.md +249 -0
- package/docs/KNOWLEDGE-STORE.md +213 -0
- package/docs/PI-EXTENSION.md +91 -0
- package/docs/SDK.md +113 -0
- package/docs/THIRD-PARTY-NOTICES.md +64 -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 +1044 -0
- package/src/config.ts +1036 -147
- 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 +264 -7
- 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 -174
- package/src/infrastructure/searxng-lifecycle.ts +0 -577
- 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/tool.ts
CHANGED
|
@@ -1,430 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* pi-research: Multi-Agent Research Orchestration Tool
|
|
3
3
|
*
|
|
4
|
-
* This is the primary entry point for the pi-research extension. It
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* This is the primary entry point for the pi-research extension. It provides:
|
|
5
|
+
* - Research tool: Web/internet research with multi-agent orchestration
|
|
6
|
+
* - Health tool: System health checks across all components
|
|
7
|
+
*
|
|
8
|
+
* Tool definitions are modularized into:
|
|
9
|
+
* - tools/research-tool-definition.ts - Main research tool logic
|
|
10
|
+
* - tools/health-tool-definition.ts - Health check tool logic
|
|
11
|
+
* - tui/research-tui-manager.ts - TUI coordination
|
|
12
|
+
* - observers/research-observer-impl.ts - Research lifecycle observer
|
|
13
|
+
* - cleanup/research-cleanup.ts - Session cleanup logic
|
|
14
|
+
* - tui/research-health.ts - Health check helpers
|
|
15
|
+
* - orchestration/research-session-service.ts - session tracking and metadata helpers
|
|
9
16
|
*/
|
|
10
17
|
|
|
11
|
-
import { readFileSync } from 'node:fs';
|
|
12
|
-
import { fileURLToPath } from 'node:url';
|
|
13
|
-
import { dirname, join } from 'node:path';
|
|
14
|
-
import type {
|
|
15
|
-
ToolDefinition,
|
|
16
|
-
AgentToolResult,
|
|
17
|
-
ExtensionContext,
|
|
18
|
-
} from '@mariozechner/pi-coding-agent';
|
|
19
|
-
import type {
|
|
20
|
-
ExtendedExtensionContext,
|
|
21
|
-
SessionManager,
|
|
22
|
-
ModelWithId,
|
|
23
|
-
ExtendedAgentSessionEvent,
|
|
24
|
-
} from './types/extension-context.ts';
|
|
25
|
-
import type { SettingsManager } from '@mariozechner/pi-coding-agent';
|
|
26
|
-
import type { Model } from '@mariozechner/pi-ai';
|
|
27
|
-
import { Type } from '@sinclair/typebox';
|
|
28
|
-
import { createResearcherSession } from "./orchestration/researcher.ts";
|
|
29
|
-
import { validateConfig } from './config.ts';
|
|
30
|
-
import {
|
|
31
|
-
createMasterResearchPanel,
|
|
32
|
-
addSlice,
|
|
33
|
-
activateSlice,
|
|
34
|
-
completeSlice,
|
|
35
|
-
removeSlice,
|
|
36
|
-
updateSliceTokens,
|
|
37
|
-
createInitialPanelState,
|
|
38
|
-
clearAllFlashTimeouts,
|
|
39
|
-
flashSlice,
|
|
40
|
-
} from './tui/research-panel.ts';
|
|
41
|
-
import { ensureAssistantResponse } from './utils/text-utils.ts';
|
|
42
|
-
import { isTextContentBlock, parseTokenUsage, calculateTotalTokens } from './types/llm.ts';
|
|
43
|
-
import {
|
|
44
|
-
initLifecycle,
|
|
45
|
-
ensureRunning,
|
|
46
|
-
getStatus,
|
|
47
|
-
isFunctional,
|
|
48
|
-
setFunctional,
|
|
49
|
-
} from './infrastructure/searxng-lifecycle.ts';
|
|
50
|
-
import { getManager } from './infrastructure/searxng-lifecycle.ts';
|
|
51
|
-
import { DeepResearchOrchestrator } from './orchestration/deep-research-orchestrator.ts';
|
|
52
|
-
import { createResearchRunId, logger, runWithLogContext } from './logger.ts';
|
|
53
|
-
import { exportResearchReport, appendExportMessage } from './utils/research-export.ts';
|
|
54
|
-
import { validateAndSanitizeQuery } from './utils/input-validation.ts';
|
|
55
|
-
import { injectCurrentDate } from './utils/inject-date.ts';
|
|
56
|
-
import {
|
|
57
|
-
startResearchSession,
|
|
58
|
-
endResearchSession,
|
|
59
|
-
registerSessionPanel,
|
|
60
|
-
registerMasterUpdate,
|
|
61
|
-
refreshAllSessions,
|
|
62
|
-
onSessionOrderChange,
|
|
63
|
-
getPiActivePanels,
|
|
64
|
-
} from './utils/session-state.ts';
|
|
65
|
-
import { cleanupSharedLinks } from './utils/shared-links.ts';
|
|
66
|
-
|
|
67
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
68
|
-
const __dirname = dirname(__filename);
|
|
69
|
-
|
|
70
18
|
/**
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* Ensures SearXNG is not just running, but actually returning results.
|
|
74
|
-
* Shows status in TUI while running.
|
|
19
|
+
* Export individual tool creation functions for direct use.
|
|
75
20
|
*/
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
onUpdate: () => void
|
|
79
|
-
): Promise<void> {
|
|
80
|
-
if (isFunctional()) return;
|
|
81
|
-
|
|
82
|
-
const sliceLabel = 'health check ...';
|
|
83
|
-
addSlice(panelState, sliceLabel, sliceLabel, false);
|
|
84
|
-
activateSlice(panelState, sliceLabel);
|
|
85
|
-
onUpdate();
|
|
86
|
-
|
|
87
|
-
try {
|
|
88
|
-
const { runHealthCheck } = await import('./healthcheck/index.ts');
|
|
89
|
-
const health = await runHealthCheck();
|
|
90
|
-
|
|
91
|
-
if (!health.success) {
|
|
92
|
-
throw new Error(`Functional health check failed: ${health.error || 'Unknown error'}. Your network or search engines may be blocked.`);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
setFunctional(true);
|
|
96
|
-
} finally {
|
|
97
|
-
removeSlice(panelState, sliceLabel);
|
|
98
|
-
onUpdate();
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function getPiSessionMetadata(ctx: ExtensionContext) {
|
|
103
|
-
const extendedCtx = ctx as ExtendedExtensionContext;
|
|
104
|
-
const sessionManager: SessionManager | undefined = extendedCtx.sessionManager;
|
|
105
|
-
const piSessionId = typeof sessionManager?.getSessionId === 'function' ? String(sessionManager.getSessionId()) : 'default';
|
|
106
|
-
return {
|
|
107
|
-
piSessionId,
|
|
108
|
-
sessionFile: typeof sessionManager?.getSessionFile === 'function' ? String(sessionManager.getSessionFile()) : undefined,
|
|
109
|
-
cwd: ctx.cwd,
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
export function createResearchTool(): ToolDefinition {
|
|
114
|
-
return {
|
|
115
|
-
name: 'research',
|
|
116
|
-
label: 'Research',
|
|
117
|
-
description:
|
|
118
|
-
'Perform deep web/internet research using a coordinated team of agents. Synthesizes findings from web search, scraping, security databases, and Stack Exchange.',
|
|
119
|
-
promptSnippet: 'Conduct multi-agent web/internet research',
|
|
120
|
-
promptGuidelines: [
|
|
121
|
-
'Specifically for web research, not local project exploration.',
|
|
122
|
-
'Research is organized into Rounds. Each round contains multiple parallel siblings.',
|
|
123
|
-
'SCRAPE PROTOCOL: Call the `scrape` tool up to FOUR times per agent: (1) handshake returns globally scraped links, (2) Batch 1 broad scrape ≤3 URLs, (3) Batch 2 targeted ≤2 URLs, (4) Batch 3 deep-dive ≤3 URLs. Batches skip automatically when context > 55%.',
|
|
124
|
-
'The last sibling in each round evaluates progress and decides whether to continue or synthesize.',
|
|
125
|
-
'Use `security_search` for vulnerabilities, CVE IDs, package security, or actively exploited vulnerabilities.',
|
|
126
|
-
'Use `stackexchange` for technical questions, code solutions, debugging help, and best practices.',
|
|
127
|
-
],
|
|
128
|
-
parameters: Type.Object({
|
|
129
|
-
query: Type.String({
|
|
130
|
-
description: 'Research query or topic to investigate',
|
|
131
|
-
}),
|
|
132
|
-
depth: Type.Optional(Type.Integer({
|
|
133
|
-
minimum: 0,
|
|
134
|
-
maximum: 3,
|
|
135
|
-
description: [
|
|
136
|
-
'Research complexity level (0-3).',
|
|
137
|
-
'0: Quick/Brief — 1 researcher, 1 round. Direct session; no coordinator/orchestrator.',
|
|
138
|
-
'1: Normal — 2 researchers, 2 rounds. AI-orchestrated.',
|
|
139
|
-
'2: Deep — 3 researchers, 3 rounds. AI-orchestrated.',
|
|
140
|
-
'3: Ultra — 5 researchers, 5 rounds (Exhaustive). AI-orchestrated.',
|
|
141
|
-
'Omit to use default complexity 0 (Quick mode).',
|
|
142
|
-
].join(' '),
|
|
143
|
-
})),
|
|
144
|
-
model: Type.Optional(Type.String({
|
|
145
|
-
description: 'Model ID to use for all research agents (defaults to current active model)',
|
|
146
|
-
})),
|
|
147
|
-
}),
|
|
148
|
-
async execute(
|
|
149
|
-
_toolCallId: string,
|
|
150
|
-
params: unknown,
|
|
151
|
-
signal: AbortSignal | undefined,
|
|
152
|
-
_onUpdate: unknown,
|
|
153
|
-
ctx: ExtensionContext,
|
|
154
|
-
): Promise<AgentToolResult<unknown>> {
|
|
155
|
-
const { query, model: modelId, depth } = params as {
|
|
156
|
-
query: string;
|
|
157
|
-
depth?: number;
|
|
158
|
-
model?: string;
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
if (!query || !ctx.model) {
|
|
162
|
-
return { content: [{ type: 'text', text: 'Error: query and model are required' }], details: {} };
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Validate and sanitize query
|
|
166
|
-
const sanitizedQuery = validateAndSanitizeQuery(query);
|
|
167
|
-
|
|
168
|
-
const baseModel = ctx.model;
|
|
169
|
-
const researchRunId = createResearchRunId();
|
|
170
|
-
const metadata = getPiSessionMetadata(ctx);
|
|
171
|
-
const piSessionId = metadata.piSessionId;
|
|
172
|
-
|
|
173
|
-
return runWithLogContext({
|
|
174
|
-
...metadata,
|
|
175
|
-
researchRunId,
|
|
176
|
-
toolName: 'research',
|
|
177
|
-
}, async () => {
|
|
178
|
-
let aborted = false;
|
|
179
|
-
let cleanup: (() => void) | null = null;
|
|
180
|
-
let unsubOrder: (() => void) | null = null;
|
|
181
|
-
|
|
182
|
-
try {
|
|
183
|
-
validateConfig();
|
|
184
|
-
await initLifecycle(ctx);
|
|
185
|
-
const searxngUrl = await ensureRunning();
|
|
186
|
-
const manager = getManager();
|
|
187
|
-
if (manager) {
|
|
188
|
-
const { setSearxngManager } = await import('./web-research/utils.ts');
|
|
189
|
-
setSearxngManager(manager);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
let selectedModel = baseModel;
|
|
193
|
-
if (modelId) {
|
|
194
|
-
selectedModel = ctx.modelRegistry.getAll().find(m => m.id === modelId) || baseModel;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const typedModel = selectedModel as ModelWithId;
|
|
198
|
-
const modelIdStr = typedModel?.id || 'unknown';
|
|
199
|
-
|
|
200
|
-
const researchId = startResearchSession(piSessionId);
|
|
201
|
-
const masterWidgetId = `pi-research-master-${piSessionId}`;
|
|
202
|
-
|
|
203
|
-
cleanup = () => {
|
|
204
|
-
if (unsubOrder) unsubOrder();
|
|
205
|
-
endResearchSession(piSessionId, researchId);
|
|
206
|
-
cleanupSharedLinks(researchId);
|
|
207
|
-
// Clear all flash timeouts for this specific run
|
|
208
|
-
clearAllFlashTimeouts(researchId);
|
|
209
|
-
|
|
210
|
-
// If no more research runs are active in this Pi session, remove the master widget
|
|
211
|
-
const activePanels = getPiActivePanels(piSessionId);
|
|
212
|
-
if (activePanels.length === 0) {
|
|
213
|
-
ctx.ui.setWidget(masterWidgetId, undefined);
|
|
214
|
-
} else {
|
|
215
|
-
// Otherwise, just refresh to remove this run's block
|
|
216
|
-
refreshAllSessions(piSessionId);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
logger.info('[research] cleanup completed', { piSessionId, researchId });
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
const panelState = createInitialPanelState(researchId, sanitizedQuery, getStatus(), modelIdStr);
|
|
223
|
-
|
|
224
|
-
// Register this research run's state and the master update function
|
|
225
|
-
registerSessionPanel(piSessionId, researchId, panelState);
|
|
226
|
-
const updateMasterWidget = () => {
|
|
227
|
-
const masterPanelCreator = createMasterResearchPanel(piSessionId);
|
|
228
|
-
ctx.ui.setWidget(masterWidgetId, masterPanelCreator, { placement: 'aboveEditor' });
|
|
229
|
-
};
|
|
230
|
-
registerMasterUpdate(piSessionId, updateMasterWidget);
|
|
231
|
-
|
|
232
|
-
// Subscribe to order changes to ensure refresh when ANY run starts/ends
|
|
233
|
-
unsubOrder = onSessionOrderChange(piSessionId, () => refreshAllSessions(piSessionId));
|
|
234
|
-
|
|
235
|
-
// Robust functional health check before research starts
|
|
236
|
-
await ensureFunctionalHealth(panelState, () => refreshAllSessions(piSessionId));
|
|
237
|
-
|
|
238
|
-
const onTokens = (n: number) => {
|
|
239
|
-
panelState.totalTokens += n;
|
|
240
|
-
refreshAllSessions(piSessionId);
|
|
241
|
-
};
|
|
242
|
-
|
|
243
|
-
// Default complexity to 0 (quick mode) if not provided
|
|
244
|
-
const researchComplexity = depth ?? 0;
|
|
245
|
-
const isQuick = researchComplexity === 0;
|
|
246
|
-
|
|
247
|
-
// For deep mode, complexity (1-3) is passed directly to orchestrator
|
|
248
|
-
// complexity 1 → Normal (2 researchers, 2 rounds)
|
|
249
|
-
// complexity 2 → Deep (3 researchers, 3 rounds)
|
|
250
|
-
// complexity 3 → Ultra (5 researchers, 5 rounds)
|
|
251
|
-
const orchestratorComplexity = researchComplexity as 1 | 2 | 3;
|
|
252
|
-
|
|
253
|
-
logger.info('[research] run initialized', {
|
|
254
|
-
mode: isQuick ? 'quick' : 'deep',
|
|
255
|
-
complexity: researchComplexity,
|
|
256
|
-
piSessionId,
|
|
257
|
-
researchId,
|
|
258
|
-
selectedModelId: modelIdStr,
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
signal?.addEventListener('abort', () => {
|
|
262
|
-
aborted = true;
|
|
263
|
-
logger.warn('[research] run aborted', { piSessionId, researchId });
|
|
264
|
-
cleanup?.();
|
|
265
|
-
}, { once: true });
|
|
266
|
-
|
|
267
|
-
if (isQuick) {
|
|
268
|
-
const truncatedQuery = sanitizedQuery.length > 20 ? sanitizedQuery.slice(0, 20) + '...' : sanitizedQuery;
|
|
269
|
-
const sliceLabel = `researching: ${truncatedQuery}`;
|
|
270
|
-
addSlice(panelState, sliceLabel, sliceLabel, false);
|
|
271
|
-
activateSlice(panelState, sliceLabel);
|
|
272
|
-
// Quick mode: deterministic tool limits
|
|
273
|
-
// - 4 gathering calls (search/security/stackexchange/grep combined)
|
|
274
|
-
// - 4 scrape calls (handshake + 3 batches)
|
|
275
|
-
// Total: 8 tool calls, then STOP
|
|
276
|
-
panelState.progress = { expected: 8, made: 0, extended: false };
|
|
277
|
-
logger.info('[research] quick mode started', { query: sanitizedQuery });
|
|
278
|
-
refreshAllSessions(piSessionId);
|
|
279
|
-
const researcherPromptRaw = readFileSync(join(__dirname, 'prompts', 'researcher.md'), 'utf-8');
|
|
280
|
-
let researcherPrompt = injectCurrentDate(researcherPromptRaw, 'researcher');
|
|
281
|
-
|
|
282
|
-
// QUICK MODE EFFICIENCY: Encourage early exit ONLY for definitive fact-finding.
|
|
283
|
-
researcherPrompt += '\n\n## Quick Mode Efficiency\n' +
|
|
284
|
-
'For specific fact-seeking queries: if you find a definitive, up-to-date answer from a highly reliable source ' +
|
|
285
|
-
'that fully resolves the request, stop gathering/scraping immediately and provide a concise, high-signal report.';
|
|
286
|
-
|
|
287
|
-
const extendedCtx = ctx as ExtendedExtensionContext;
|
|
288
|
-
|
|
289
|
-
// Per-session token accumulator for context-aware scrape gating
|
|
290
|
-
let quickSessionTokens = 0;
|
|
291
|
-
const quickContextWindowSize = (selectedModel as any)?.contextWindow ?? 200000;
|
|
292
|
-
|
|
293
|
-
const session = await createResearcherSession({
|
|
294
|
-
cwd: ctx.cwd,
|
|
295
|
-
ctxModel: selectedModel,
|
|
296
|
-
modelRegistry: ctx.modelRegistry,
|
|
297
|
-
settingsManager: extendedCtx.settingsManager || (ctx as unknown as { settingsManager: SettingsManager }).settingsManager!,
|
|
298
|
-
systemPrompt: researcherPrompt,
|
|
299
|
-
searxngUrl,
|
|
300
|
-
extensionCtx: ctx,
|
|
301
|
-
getTokensUsed: () => quickSessionTokens,
|
|
302
|
-
contextWindowSize: quickContextWindowSize,
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
const calculateUsageCost = (usage: Partial<import('./types/llm').TokenUsage>): number => {
|
|
306
|
-
if (!usage || !selectedModel?.cost) return 0;
|
|
307
|
-
const modelCost = selectedModel.cost;
|
|
308
|
-
const inputCost = (modelCost.input / 1_000_000) * (usage.input ?? 0);
|
|
309
|
-
const outputCost = (modelCost.output / 1_000_000) * (usage.output ?? 0);
|
|
310
|
-
const cacheReadCost = (modelCost.cacheRead / 1_000_000) * (usage.cacheRead ?? 0);
|
|
311
|
-
const cacheWriteCost = (modelCost.cacheWrite / 1_000_000) * (usage.cacheWrite ?? 0);
|
|
312
|
-
return inputCost + outputCost + cacheReadCost + cacheWriteCost;
|
|
313
|
-
};
|
|
314
|
-
|
|
315
|
-
const subscription = session.subscribe((event: ExtendedAgentSessionEvent) => {
|
|
316
|
-
if (event.type === 'message_end' && event.message?.role === 'assistant') {
|
|
317
|
-
const usage = parseTokenUsage(event.message?.usage);
|
|
318
|
-
const tokens = calculateTotalTokens(usage);
|
|
319
|
-
const cost = calculateUsageCost(usage);
|
|
320
|
-
if (tokens > 0) {
|
|
321
|
-
quickSessionTokens += tokens; // drives context-aware scrape gating
|
|
322
|
-
onTokens(tokens);
|
|
323
|
-
updateSliceTokens(panelState, sliceLabel, tokens, cost);
|
|
324
|
-
refreshAllSessions(piSessionId);
|
|
325
|
-
}
|
|
326
|
-
} else if (event.type === 'message_update') {
|
|
327
|
-
// Estimate context growth from streaming text during model generation.
|
|
328
|
-
// Cost is left unchanged (only updated with exact values on message_end).
|
|
329
|
-
const updateMsg = event.message;
|
|
330
|
-
// Content may be in the message object but not typed in ExtendedAgentSessionEvent
|
|
331
|
-
const content = updateMsg?.role === 'assistant' ? (updateMsg as any).content : undefined;
|
|
332
|
-
if (Array.isArray(content)) {
|
|
333
|
-
const textLen = content
|
|
334
|
-
.filter(isTextContentBlock)
|
|
335
|
-
.reduce((sum, b) => sum + b.text.length, 0);
|
|
336
|
-
if (textLen > 200) {
|
|
337
|
-
const estimated = quickSessionTokens + Math.ceil(textLen / 4);
|
|
338
|
-
updateSliceTokens(panelState, sliceLabel, estimated, 0);
|
|
339
|
-
refreshAllSessions(piSessionId);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
} else if (event.type === 'tool_execution_end') {
|
|
343
|
-
const isError = event.isError ?? false;
|
|
344
|
-
const isBlocked = event.details?.blocked === true;
|
|
345
|
-
const color = isError || isBlocked ? 'red' : 'green';
|
|
346
|
-
const duration = isError || isBlocked ? 1000 : 500;
|
|
347
|
-
flashSlice(panelState, sliceLabel, color, duration, () => refreshAllSessions(piSessionId));
|
|
348
|
-
// Advance progress bar ONLY for non-blocked tool calls
|
|
349
|
-
if (panelState.progress && !isBlocked) {
|
|
350
|
-
panelState.progress.made += 1;
|
|
351
|
-
// Snap to 100% when all tool calls are used
|
|
352
|
-
if (panelState.progress.made >= panelState.progress.expected) {
|
|
353
|
-
panelState.progress.made = panelState.progress.expected;
|
|
354
|
-
}
|
|
355
|
-
refreshAllSessions(piSessionId);
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
// Race the session prompt against the abort signal so the tool returns
|
|
361
|
-
// immediately on Ctrl+C rather than waiting for the session to finish.
|
|
362
|
-
// The session itself continues running in the background (pi SDK limitation),
|
|
363
|
-
// but the tool call resolves and the user gets a responsive cancel.
|
|
364
|
-
if (signal) {
|
|
365
|
-
await Promise.race([
|
|
366
|
-
session.prompt(sanitizedQuery),
|
|
367
|
-
new Promise<never>((_, reject) => {
|
|
368
|
-
if (signal.aborted) { reject(new Error('Research aborted')); return; }
|
|
369
|
-
signal.addEventListener('abort', () => reject(new Error('Research aborted')), { once: true });
|
|
370
|
-
}),
|
|
371
|
-
]);
|
|
372
|
-
} else {
|
|
373
|
-
await session.prompt(sanitizedQuery);
|
|
374
|
-
}
|
|
375
|
-
const result = ensureAssistantResponse(session, 'Quick');
|
|
376
|
-
|
|
377
|
-
// Snap to 100%
|
|
378
|
-
if (panelState.progress) {
|
|
379
|
-
panelState.progress.made = panelState.progress.expected;
|
|
380
|
-
refreshAllSessions(piSessionId);
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
// Export research report
|
|
384
|
-
const exportPath = await exportResearchReport(sanitizedQuery, result, 'quick', ctx.cwd);
|
|
385
|
-
const finalResult = exportPath ? appendExportMessage(result, exportPath) : result;
|
|
386
|
-
|
|
387
|
-
// Cleanup subscription
|
|
388
|
-
if (typeof subscription === 'function') {
|
|
389
|
-
subscription();
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
completeSlice(panelState, sliceLabel);
|
|
393
|
-
cleanup();
|
|
394
|
-
return { content: [{ type: 'text', text: finalResult }], details: { totalTokens: panelState.totalTokens } };
|
|
395
|
-
} else {
|
|
396
|
-
// DEEP MODE: orchestrator complexity has already been determined above.
|
|
397
|
-
const orchestrator = new DeepResearchOrchestrator({
|
|
398
|
-
ctx,
|
|
399
|
-
model: selectedModel as Model<any>,
|
|
400
|
-
query: sanitizedQuery,
|
|
401
|
-
complexity: orchestratorComplexity,
|
|
402
|
-
onTokens,
|
|
403
|
-
onUpdate: () => refreshAllSessions(piSessionId),
|
|
404
|
-
searxngUrl,
|
|
405
|
-
panelState,
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
const result = await orchestrator.run(signal);
|
|
409
|
-
|
|
410
|
-
// Export research report
|
|
411
|
-
const exportPath = await exportResearchReport(sanitizedQuery, result, 'deep', ctx.cwd);
|
|
412
|
-
const finalResult = exportPath ? appendExportMessage(result, exportPath) : result;
|
|
413
|
-
|
|
414
|
-
cleanup();
|
|
415
|
-
return { content: [{ type: 'text', text: finalResult }], details: { totalTokens: panelState.totalTokens } };
|
|
416
|
-
}
|
|
417
|
-
} catch (error) {
|
|
418
|
-
// If aborted, don't treat as error - just return gracefully
|
|
419
|
-
if (aborted) {
|
|
420
|
-
return { content: [{ type: 'text', text: 'Research cancelled.' }], details: {} };
|
|
421
|
-
}
|
|
422
|
-
cleanup?.();
|
|
423
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
424
|
-
logger.error('[research] run failed', error);
|
|
425
|
-
return { content: [{ type: 'text', text: `Research failed: ${errorMsg}` }], details: {} };
|
|
426
|
-
}
|
|
427
|
-
});
|
|
428
|
-
},
|
|
429
|
-
};
|
|
430
|
-
}
|
|
21
|
+
export { createResearchTool } from './tools/research-tool-definition.ts';
|
|
22
|
+
export { createHealthTool } from './tools/health-tool-definition.ts';
|