@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.
Files changed (248) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +55 -138
  3. package/dist/cli.mjs +23375 -0
  4. package/dist/prompts/research-knowledge-relevance-triage.md +35 -0
  5. package/dist/prompts/research-knowledge-search-extractor.md +41 -0
  6. package/dist/prompts/research-tool-usage.md +92 -0
  7. package/dist/prompts/researcher.md +89 -0
  8. package/dist/prompts/system-coordinator.md +75 -0
  9. package/dist/prompts/system-lead-evaluator.md +118 -0
  10. package/dist/thread-worker.mjs +2611 -0
  11. package/dist/webgpu-probe.mjs +80 -0
  12. package/docs/AGENT-SKILL.md +130 -0
  13. package/docs/ARCHITECTURE.md +306 -80
  14. package/docs/CONFIGURATION.md +249 -0
  15. package/docs/KNOWLEDGE-STORE.md +211 -0
  16. package/docs/PI-EXTENSION.md +91 -0
  17. package/docs/SDK.md +122 -0
  18. package/docs/THIRD-PARTY-NOTICES.md +66 -0
  19. package/package.json +102 -36
  20. package/scripts/build.cjs +140 -0
  21. package/scripts/cleanup.cjs +149 -0
  22. package/scripts/prepare.cjs +28 -0
  23. package/scripts/setup.cjs +134 -0
  24. package/skills/pi-research/README.md +90 -0
  25. package/skills/pi-research/SKILL.md +102 -0
  26. package/skills/pi-research/references/configuration.md +77 -0
  27. package/skills/pi-research/scripts/run.mjs +237 -0
  28. package/src/cleanup/research-cleanup.ts +140 -0
  29. package/src/cli.ts +1195 -0
  30. package/src/config.ts +1032 -155
  31. package/src/constants.ts +115 -84
  32. package/src/core/health-check-service.ts +265 -0
  33. package/src/core/interfaces/health-check-interfaces.ts +50 -0
  34. package/src/core/interfaces/knowledge-interfaces.ts +190 -0
  35. package/src/core/interfaces/observer-interfaces.ts +67 -0
  36. package/src/core/interfaces/orchestration-interfaces.ts +86 -0
  37. package/src/core/interfaces/planning-interfaces.ts +100 -0
  38. package/src/core/interfaces/process-interfaces.ts +37 -0
  39. package/src/core/interfaces/research-plan-types.ts +52 -0
  40. package/src/core/interfaces/scheduler-interfaces.ts +91 -0
  41. package/src/core/interfaces/service-names.ts +39 -0
  42. package/src/core/interfaces/state-manager-interfaces.ts +29 -0
  43. package/src/core/interfaces/steering-interfaces.ts +31 -0
  44. package/src/core/llm/agentic-repair.ts +169 -0
  45. package/src/core/llm/inject-date.ts +52 -0
  46. package/src/core/llm/llm-timeout.ts +64 -0
  47. package/src/core/llm/llm-utils.ts +86 -0
  48. package/src/core/llm/model-registry-factory.ts +287 -0
  49. package/src/core/llm/pi-ai-completion.ts +29 -0
  50. package/src/core/llm/prompts.ts +50 -0
  51. package/src/core/llm/research-model-resolver.ts +91 -0
  52. package/src/core/planning-constants.ts +13 -0
  53. package/src/core/planning-service.ts +649 -0
  54. package/src/core/planning-utils.ts +299 -0
  55. package/src/core/scheduler-factory.ts +48 -0
  56. package/src/core/scheduler-service.ts +123 -0
  57. package/src/core/service-initialization.ts +194 -0
  58. package/src/core/service-interfaces.ts +48 -0
  59. package/src/core/service-registry.ts +659 -0
  60. package/src/healthcheck/index.ts +271 -265
  61. package/src/healthcheck/registry.ts +130 -0
  62. package/src/index.ts +639 -50
  63. package/src/infrastructure/browser/browser-cleanup.ts +309 -0
  64. package/src/infrastructure/browser/browser-client.ts +199 -0
  65. package/src/infrastructure/browser/browser-error-utils.ts +160 -0
  66. package/src/infrastructure/browser/browser-lifecycle.ts +120 -0
  67. package/src/infrastructure/browser/browser-server.ts +174 -0
  68. package/src/infrastructure/browser/browser-task-scheduler.ts +461 -0
  69. package/src/infrastructure/browser/cleanup-utils.ts +155 -0
  70. package/src/infrastructure/browser/client-agent.ts +31 -0
  71. package/src/infrastructure/browser/config.ts +288 -0
  72. package/src/infrastructure/browser/ensure-browser.ts +205 -0
  73. package/src/infrastructure/browser/index.ts +37 -0
  74. package/src/infrastructure/browser/priority-task-queue.ts +226 -0
  75. package/src/infrastructure/browser/scheduler-factory.ts +354 -0
  76. package/src/infrastructure/browser/task-execution-service.ts +209 -0
  77. package/src/infrastructure/browser/thread-worker-browser.ts +282 -0
  78. package/src/infrastructure/browser/thread-worker-lifecycle.ts +323 -0
  79. package/src/infrastructure/browser/thread-worker-messaging.ts +662 -0
  80. package/src/infrastructure/browser/thread-worker-types.ts +25 -0
  81. package/src/infrastructure/browser/thread-worker.mjs +2611 -0
  82. package/src/infrastructure/browser/thread-worker.ts +168 -0
  83. package/src/infrastructure/browser/worker-pool-manager.ts +449 -0
  84. package/src/infrastructure/embedding/embedding-client.ts +205 -0
  85. package/src/infrastructure/embedding/embedding-factory.ts +324 -0
  86. package/src/infrastructure/embedding/embedding-server.ts +535 -0
  87. package/src/infrastructure/file-lock-service.ts +592 -0
  88. package/src/infrastructure/gpu-resource-service.ts +191 -0
  89. package/src/infrastructure/knowledge-store-service.ts +475 -0
  90. package/src/infrastructure/metrics-service.ts +129 -0
  91. package/src/infrastructure/process-lifecycle-service.ts +253 -0
  92. package/src/infrastructure/scheduler-factory-service.ts +58 -0
  93. package/src/infrastructure/service-initialization.ts +292 -0
  94. package/src/infrastructure/state/state-backup-manager.ts +290 -0
  95. package/src/infrastructure/state/state-browser-api.ts +62 -0
  96. package/src/infrastructure/state/state-browser-manager.ts +70 -0
  97. package/src/infrastructure/state/state-manager-service.ts +264 -0
  98. package/src/infrastructure/state/state-manager.ts +547 -0
  99. package/src/infrastructure/state/state-metrics.ts +92 -0
  100. package/src/infrastructure/state/state-migration.ts +103 -0
  101. package/src/infrastructure/state/state-path-configuration.ts +119 -0
  102. package/src/infrastructure/state/state-session-api.ts +94 -0
  103. package/src/infrastructure/state/state-session-manager.ts +127 -0
  104. package/src/infrastructure/state/state-validator.ts +93 -0
  105. package/src/infrastructure/types/state-types.ts +85 -0
  106. package/src/knowledge/chunker.ts +157 -0
  107. package/src/knowledge/embedder-init.ts +413 -0
  108. package/src/knowledge/embedder-utils.ts +296 -0
  109. package/src/knowledge/embedder.ts +592 -0
  110. package/src/knowledge/index.ts +126 -0
  111. package/src/knowledge/lancedb-loader.ts +35 -0
  112. package/src/knowledge/migration.ts +17 -0
  113. package/src/knowledge/model-config.ts +132 -0
  114. package/src/knowledge/onnx-env.ts +48 -0
  115. package/src/knowledge/store-operations.ts +291 -0
  116. package/src/knowledge/store-schema.ts +79 -0
  117. package/src/knowledge/store.ts +1251 -0
  118. package/src/knowledge/transformers-loader.ts +31 -0
  119. package/src/knowledge/webgpu-probe.mjs +80 -0
  120. package/src/knowledge/webgpu-viability.ts +255 -0
  121. package/src/knowledge/writer-queue.ts +252 -0
  122. package/src/logger.ts +316 -122
  123. package/src/observers/research-observer-impl.ts +459 -0
  124. package/src/orchestration/deep-research-orchestrator.ts +524 -773
  125. package/src/orchestration/deep-research-types.ts +47 -35
  126. package/src/orchestration/headless-observer.ts +114 -0
  127. package/src/orchestration/orchestration-types.ts +33 -0
  128. package/src/orchestration/quick-research-orchestrator.ts +463 -0
  129. package/src/orchestration/research-orchestration-service.ts +549 -0
  130. package/src/orchestration/research-session-service.ts +178 -0
  131. package/src/orchestration/research-synthesis-service.ts +348 -0
  132. package/src/orchestration/researcher-executor.ts +394 -0
  133. package/src/orchestration/researcher.ts +115 -42
  134. package/src/orchestration/service-initialization.ts +109 -0
  135. package/src/orchestration/session-context.ts +7 -3
  136. package/src/orchestration/session-state.ts +672 -0
  137. package/src/prompts/research-knowledge-relevance-triage.md +35 -0
  138. package/src/prompts/research-knowledge-search-extractor.md +41 -0
  139. package/src/prompts/research-tool-usage.md +63 -16
  140. package/src/prompts/researcher.md +59 -136
  141. package/src/prompts/system-coordinator.md +64 -13
  142. package/src/prompts/system-lead-evaluator.md +82 -189
  143. package/src/research-config.ts +872 -0
  144. package/src/sdk.ts +847 -0
  145. package/src/security/cisa-kev.ts +72 -9
  146. package/src/security/cvss.ts +92 -0
  147. package/src/security/ecosystem.ts +45 -0
  148. package/src/security/github-advisories.ts +168 -428
  149. package/src/security/github-advisory-types.ts +282 -0
  150. package/src/security/index.ts +63 -177
  151. package/src/security/nvd-client.ts +550 -0
  152. package/src/security/nvd-types.ts +179 -0
  153. package/src/security/osv-client.ts +246 -0
  154. package/src/security/osv-types.ts +285 -0
  155. package/src/security/types.ts +67 -53
  156. package/src/skill-install/skill-installer.ts +519 -0
  157. package/src/stackexchange/index.ts +81 -27
  158. package/src/stackexchange/output/compact.ts +7 -6
  159. package/src/stackexchange/output/table.ts +11 -7
  160. package/src/stackexchange/queries.ts +0 -10
  161. package/src/stackexchange/rest-client.ts +141 -66
  162. package/src/stackexchange/types.ts +0 -17
  163. package/src/tool.ts +15 -423
  164. package/src/tools/grep.ts +32 -310
  165. package/src/tools/health-tool-definition.ts +101 -0
  166. package/src/tools/index.ts +38 -9
  167. package/src/tools/research-knowledge-search.ts +974 -0
  168. package/src/tools/research-knowledge-types.ts +77 -0
  169. package/src/tools/research-tool-definition.ts +494 -0
  170. package/src/tools/scrape.ts +286 -388
  171. package/src/tools/search.ts +98 -153
  172. package/src/tools/security.ts +107 -80
  173. package/src/tools/stackexchange.ts +58 -35
  174. package/src/tools/youtube-transcript.ts +163 -0
  175. package/src/tui/knowledge-search-panel.ts +86 -0
  176. package/src/tui/pulse.ts +86 -0
  177. package/src/tui/research-health.ts +124 -0
  178. package/src/tui/research-panel-color-utils.ts +340 -0
  179. package/src/tui/research-panel-format.ts +54 -0
  180. package/src/tui/research-panel-state.ts +404 -0
  181. package/src/tui/research-panel-wave.ts +280 -0
  182. package/src/tui/research-panel.ts +339 -435
  183. package/src/tui/research-tui-manager.ts +233 -0
  184. package/src/tui/tui-controller.ts +137 -0
  185. package/src/types/extension-context.ts +18 -49
  186. package/src/types/index.ts +61 -0
  187. package/src/types/llm.ts +68 -29
  188. package/src/types/research-panel-types.ts +61 -0
  189. package/src/utils/circuit-breaker.ts +146 -0
  190. package/src/utils/citation-utils.ts +97 -0
  191. package/src/utils/disk-space-checker.ts +55 -0
  192. package/src/utils/error-tracker.ts +265 -0
  193. package/src/utils/host-config.ts +45 -0
  194. package/src/utils/input-validation.ts +8 -10
  195. package/src/utils/json-utils.ts +150 -16
  196. package/src/utils/llm-usage.ts +54 -0
  197. package/src/utils/log-rotation.ts +125 -0
  198. package/src/utils/log-utils.ts +159 -0
  199. package/src/utils/make-resource-loader.ts +3 -1
  200. package/src/utils/metrics-summary.ts +459 -0
  201. package/src/utils/metrics.ts +302 -0
  202. package/src/utils/research-export.ts +28 -12
  203. package/src/utils/safe-unref.ts +31 -0
  204. package/src/utils/shared-links.ts +195 -90
  205. package/src/utils/shutdown-manager.ts +65 -0
  206. package/src/utils/stdio-capture.ts +407 -0
  207. package/src/utils/text-utils.ts +258 -11
  208. package/src/utils/tool-usage-tracker.ts +83 -16
  209. package/src/utils/url-utils.ts +255 -0
  210. package/src/utils/user-agent.ts +28 -0
  211. package/src/web-research/browser-search.ts +179 -0
  212. package/src/web-research/retry-utils.ts +173 -77
  213. package/src/web-research/scraper-types.ts +72 -0
  214. package/src/web-research/scraper-utils.ts +434 -0
  215. package/src/web-research/search.ts +78 -218
  216. package/src/web-research/types.ts +9 -24
  217. package/src/web-research/utils.ts +2 -143
  218. package/src/web-research/web-scraper.ts +477 -0
  219. package/src/youtube/index.ts +154 -0
  220. package/src/youtube/potoken.ts +259 -0
  221. package/src/youtube/transcript-client.ts +390 -0
  222. package/src/youtube/video-id.ts +118 -0
  223. package/config/searxng/default-settings.yml +0 -189
  224. package/config/searxng/limiter.toml +0 -34
  225. package/config/tooling/eslint.config.js +0 -73
  226. package/config/tooling/vitest.config.integration.ts +0 -17
  227. package/config/tooling/vitest.config.ts +0 -40
  228. package/config/tooling/vitest.config.unit.ts +0 -12
  229. package/docs/assets/README-banner.jpg +0 -0
  230. package/scripts/setup.js +0 -212
  231. package/src/infrastructure/searxng-lifecycle.ts +0 -644
  232. package/src/infrastructure/searxng-manager.ts +0 -1183
  233. package/src/infrastructure/state-manager.ts +0 -918
  234. package/src/orchestration/deep-research-reducer.ts +0 -129
  235. package/src/orchestration/id-utils.ts +0 -63
  236. package/src/security/nvd.ts +0 -532
  237. package/src/security/osv.ts +0 -487
  238. package/src/stackexchange/cache.ts +0 -111
  239. package/src/stackexchange/output/index.ts +0 -7
  240. package/src/stackexchange/output/json.ts +0 -21
  241. package/src/tui/searxng-status.ts +0 -80
  242. package/src/utils/inject-date.ts +0 -29
  243. package/src/utils/research-error.ts +0 -205
  244. package/src/utils/searxng-config.ts +0 -94
  245. package/src/utils/session-state.ts +0 -295
  246. package/src/web-research/dependencies.ts +0 -20
  247. package/src/web-research/scrapers.ts +0 -935
  248. package/tsconfig.json +0 -40
package/src/tools/grep.ts CHANGED
@@ -1,335 +1,57 @@
1
1
  /**
2
- * rg_grep Tool (Ripgrep)
2
+ * Grep Tool
3
3
  *
4
- * Standalone grep tool using ripgrep (rg) or fallback to grep.
4
+ * Thin wrapper around the SDK's built-in grep tool that adds
5
+ * ToolUsageTracker budget enforcement before delegating execution.
5
6
  */
6
7
 
7
- import { spawn } from 'node:child_process';
8
- import * as nodePath from 'node:path';
9
- import type { ToolDefinition, AgentToolResult, ExtensionContext } from '@mariozechner/pi-coding-agent';
10
- import { Type } from '@sinclair/typebox';
8
+ import { createGrepToolDefinition } from '@earendil-works/pi-coding-agent';
9
+ import { Value } from 'typebox/value';
10
+ import type { ToolDefinition } from '@earendil-works/pi-coding-agent';
11
11
  import type { ToolUsageTracker } from '../utils/tool-usage-tracker.ts';
12
-
13
- const DEFAULT_MAX_BYTES = 100 * 1024; // 100KB
14
- const DEFAULT_MAX_LINES = 200;
15
- const STREAM_MAX_BYTES = 10 * 1024 * 1024; // 10MB max in stream (prevent RangeError)
16
-
17
- interface RgGrepParams {
18
- pattern: string;
19
- path?: string;
20
- flags?: string;
21
- }
22
-
23
- function truncateHead(content: string, maxBytes: number, maxLines: number): string {
24
- const lines = content.split('\n');
25
-
26
- if (lines.length <= maxLines && Buffer.byteLength(content, 'utf-8') <= maxBytes) {
27
- return content;
28
- }
29
-
30
- const truncatedLines = lines.slice(0, maxLines);
31
- let result = truncatedLines.join('\n');
32
-
33
- if (Buffer.byteLength(result, 'utf-8') > maxBytes) {
34
- let byteCount = 0;
35
- const charArray: string[] = [];
36
-
37
- for (const char of result) {
38
- byteCount += Buffer.byteLength(char, 'utf-8');
39
- if (byteCount > maxBytes) break;
40
- charArray.push(char);
41
- }
42
-
43
- result = charArray.join('');
44
- }
45
-
46
- return result;
47
- }
48
-
49
- async function execCommand(
50
- command: string,
51
- args: string[],
52
- ): Promise<{ stdout: string; stderr: string; exitCode: number | null; wasTruncated: boolean }> {
53
- return new Promise((resolve, reject) => {
54
- const child = spawn(command, args);
55
- let stdout = '';
56
- let stderr = '';
57
- let wasTruncated = false;
58
-
59
- child.stdout?.on('data', (data) => {
60
- // Check if adding this chunk would exceed stream limit
61
- const chunk = data.toString();
62
- if (stdout.length + chunk.length > STREAM_MAX_BYTES) {
63
- // Stop collecting data to prevent RangeError
64
- wasTruncated = true;
65
- child.kill();
66
- return;
67
- }
68
- stdout += chunk;
69
- });
70
-
71
- child.stderr?.on('data', (data) => {
72
- stderr += data.toString();
73
- });
74
-
75
- child.on('close', (code) => {
76
- resolve({ stdout, stderr, exitCode: code, wasTruncated });
77
- });
78
-
79
- child.on('error', (error) => {
80
- reject(error);
81
- });
82
- });
83
- }
84
-
85
- function resolveWorkspacePath(input: string): string {
86
- const root = process.cwd();
87
- const resolved = nodePath.resolve(root, input);
88
- if (resolved !== root && !resolved.startsWith(root + nodePath.sep)) {
89
- throw new Error(`Path outside workspace: ${input}`);
90
- }
91
- return resolved;
92
- }
93
-
94
- function sanitizeInput(input: string): string[] {
95
- // Simple sanitization: split on whitespace, preserve quoted strings
96
- const result: string[] = [];
97
- let current = '';
98
- let inQuotes = false;
99
-
100
- for (const char of input) {
101
- if (char === '"' || char === "'") {
102
- inQuotes = !inQuotes;
103
- } else if (char === ' ' && !inQuotes) {
104
- if (current) {
105
- result.push(current);
106
- current = '';
107
- }
108
- } else {
109
- current += char;
110
- }
111
- }
112
-
113
- if (current) {
114
- result.push(current);
115
- }
116
-
117
- return result;
118
- }
119
-
120
- function normalizeSearchOutput(output: string): string {
121
- return process.platform === 'win32' ? output.replace(/\\/g, '/') : output;
122
- }
12
+ import { MAX_GREP_CALLS } from '../constants.ts';
123
13
 
124
14
  export function createGrepTool(options: {
125
15
  tracker: ToolUsageTracker;
16
+ cwd?: string;
126
17
  }): ToolDefinition {
18
+ const cwd = options.cwd ?? process.cwd();
19
+ const sdkGrep = createGrepToolDefinition(cwd);
20
+ const { execute: sdkExecute } = sdkGrep;
21
+
127
22
  return {
128
- name: 'grep',
23
+ ...sdkGrep,
129
24
  label: 'Code Search',
130
- description: 'Search codebase using ripgrep (rg) or grep fallback. Fast recursive text search.',
131
- promptSnippet: 'Search codebase using ripgrep/grep',
132
25
  promptGuidelines: [
133
- 'Use grep for fast recursive text search in codebases.',
134
- 'Pattern supports regex. Path and flags are optional.',
135
- 'Falls back to grep if rg is not available.',
136
- 'CRITICAL: You are allowed a maximum of 4 gathering calls total across ALL tools. Use them for breadth.',
26
+ 'Available for fast recursive text search in codebases.',
27
+ 'Pattern supports regex. Use glob to scope to file types (e.g., "**/*.ts").',
28
+ 'Options: ignoreCase, literal (disable regex), context (lines around match), limit (max matches).',
29
+ `Local code search — it does NOT consume your web-gathering budget (limit: ${MAX_GREP_CALLS} calls).`,
137
30
  ],
138
- parameters: Type.Object({
139
- pattern: Type.String({ description: 'Search pattern (regex supported)' }),
140
- path: Type.Optional(Type.String({ description: 'Directory to search (default: current directory)' })),
141
- flags: Type.Optional(Type.String({ description: 'Additional flags (e.g., "-i" for case-insensitive)' })),
142
- }),
143
- async execute(
144
- _toolCallId: string,
145
- params: unknown,
146
- _signal: unknown,
147
- _onUpdate: unknown,
148
- _ctx: ExtensionContext,
149
- ): Promise<AgentToolResult<unknown>> {
150
- // Record call in tracker - returns false if limit reached
151
- const allowed = options.tracker.recordCall('grep');
152
- if (!allowed) {
153
- // THROW to prevent researcher from calling again
154
- throw new Error(options.tracker.getLimitMessage('grep'));
31
+ execute: async (toolCallId, params, signal, onUpdate, ctx) => {
32
+ if (!Value.Check(sdkGrep.parameters, params)) {
33
+ return {
34
+ content: [{ type: 'text', text: 'Invalid parameters for grep tool.' }],
35
+ details: { error: 'invalid_parameters' },
36
+ };
155
37
  }
156
38
 
157
- const record = params as RgGrepParams;
158
- const { pattern } = record;
159
- const path = resolveWorkspacePath(record.path ?? '.');
160
- const flags = record.flags ?? '';
161
-
162
- if (!pattern) {
39
+ if (!options.tracker.recordCall('grep')) {
163
40
  return {
164
- content: [{ type: 'text', text: 'Error: pattern is required' }],
165
- details: {},
41
+ content: [{ type: 'text', text: options.tracker.getLimitMessage('grep') }],
42
+ details: { blocked: true, reason: 'limit_reached' },
166
43
  };
167
44
  }
168
45
 
169
- // Sanitize inputs to prevent shell injection
170
- const flagParts = flags ? sanitizeInput(flags) : [];
171
-
172
- // Try rg first
173
46
  try {
174
- const rgArgs = ['--no-heading', '-n', ...flagParts, pattern, path];
175
- const { stdout, stderr, exitCode, wasTruncated } = await execCommand('rg', rgArgs);
176
-
177
- const truncated = truncateHead(normalizeSearchOutput(stdout), DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES);
178
-
179
- let markdown = '# Search Results (rg)\n\n';
180
- markdown += `**Pattern:** \`${pattern}\`\n`;
181
- markdown += `**Path:** \`${path}\`\n`;
182
- if (flags) markdown += `**Flags:** \`${flags}\`\n`;
183
- markdown += `**Exit Code:** ${exitCode}\n\n`;
184
-
185
- if (wasTruncated) {
186
- markdown += '⚠️ **Output was truncated to prevent memory overflow (>10MB)**\n\n';
187
- }
188
-
189
- if (truncated) {
190
- markdown += '```\n' + truncated + '\n```';
191
- } else {
192
- markdown += 'No matches found.';
193
- }
194
-
195
- if (stderr) {
196
- markdown += '\n\n**Stderr:**\n```\n' + stderr + '\n```';
197
- }
198
-
47
+ return await sdkExecute(toolCallId, params as any, signal, onUpdate, ctx);
48
+ } catch (err) {
49
+ const msg = err instanceof Error ? err.message : String(err);
199
50
  return {
200
- content: [{ type: 'text', text: markdown }],
201
- details: {
202
- command: 'rg',
203
- args: rgArgs,
204
- exitCode,
205
- wasTruncated,
206
- },
51
+ content: [{ type: 'text', text: `Error: ${msg}` }],
52
+ details: { error: 'execution_error', message: msg },
207
53
  };
208
- } catch (rgError) {
209
- // rg not found, try grep
210
- try {
211
- const grepArgs = ['-rn', ...flagParts, pattern, path];
212
- const { stdout, stderr, exitCode, wasTruncated } = await execCommand('grep', grepArgs);
213
-
214
- const truncated = truncateHead(normalizeSearchOutput(stdout), DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES);
215
-
216
- let markdown = '# Search Results (grep)\n\n';
217
- markdown += `**Pattern:** \`${pattern}\`\n`;
218
- markdown += `**Path:** \`${path}\`\n`;
219
- if (flags) markdown += `**Flags:** \`${flags}\`\n`;
220
- markdown += `**Exit Code:** ${exitCode}\n\n`;
221
- markdown += '*Note: Using grep fallback (rg not available)*\n\n';
222
-
223
- if (wasTruncated) {
224
- markdown += '⚠️ **Output was truncated to prevent memory overflow (>10MB)**\n\n';
225
- }
226
-
227
- if (truncated) {
228
- markdown += '```\n' + truncated + '\n```';
229
- } else {
230
- markdown += 'No matches found.';
231
- }
232
-
233
- if (stderr) {
234
- markdown += '\n\n**Stderr:**\n```\n' + stderr + '\n```';
235
- }
236
-
237
- return {
238
- content: [{ type: 'text', text: markdown }],
239
- details: {
240
- command: 'grep',
241
- args: grepArgs,
242
- exitCode,
243
- wasTruncated,
244
- },
245
- };
246
- } catch (grepError) {
247
- return {
248
- content: [
249
- {
250
- type: 'text',
251
- text: `Error: Neither rg nor grep is available.\n\nrg error: ${rgError}\ngrep error: ${grepError}`,
252
- },
253
- ],
254
- details: {},
255
- };
256
- }
257
54
  }
258
55
  },
259
- };
260
- }
261
-
262
- /**
263
- * Standalone function to execute rg/grep search
264
- */
265
- export async function grep(pattern: string, path: string = '.', flags: string = ''): Promise<string> {
266
- if (!pattern) {
267
- throw new Error('Pattern is required');
268
- }
269
-
270
- const resolvedPath = resolveWorkspacePath(path);
271
- const flagParts = flags ? sanitizeInput(flags) : [];
272
-
273
- // Try rg first
274
- try {
275
- const rgArgs = ['--no-heading', '-n', ...flagParts, pattern, resolvedPath];
276
- const { stdout, stderr, exitCode, wasTruncated } = await execCommand('rg', rgArgs);
277
-
278
- let markdown = '# Search Results (rg)\n\n';
279
- markdown += `**Pattern:** \`${pattern}\`\n`;
280
- markdown += `**Path:** \`${resolvedPath}\`\n`;
281
- if (flags) markdown += `**Flags:** \`${flags}\`\n`;
282
- markdown += `**Exit Code:** ${exitCode}\n\n`;
283
-
284
- const truncated = truncateHead(normalizeSearchOutput(stdout), DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES);
285
-
286
- if (wasTruncated) {
287
- markdown += '⚠️ **Output was truncated to prevent memory overflow (>10MB)**\n\n';
288
- }
289
-
290
- if (truncated) {
291
- markdown += '```\n' + truncated + '\n```';
292
- } else {
293
- markdown += 'No matches found.';
294
- }
295
-
296
- if (stderr) {
297
- markdown += '\n\n**Stderr:**\n```\n' + stderr + '\n```';
298
- }
299
-
300
- return markdown;
301
- } catch (rgError) {
302
- // rg not found, try grep
303
- try {
304
- const grepArgs = ['-rn', ...flagParts, pattern, resolvedPath];
305
- const { stdout, stderr, exitCode, wasTruncated } = await execCommand('grep', grepArgs);
306
-
307
- let markdown = '# Search Results (grep)\n\n';
308
- markdown += `**Pattern:** \`${pattern}\`\n`;
309
- markdown += `**Path:** \`${resolvedPath}\`\n`;
310
- if (flags) markdown += `**Flags:** \`${flags}\`\n`;
311
- markdown += `**Exit Code:** ${exitCode}\n\n`;
312
- markdown += '*Note: Using grep fallback (rg not available)*\n\n';
313
-
314
- const truncated = truncateHead(normalizeSearchOutput(stdout), DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES);
315
-
316
- if (wasTruncated) {
317
- markdown += '⚠️ **Output was truncated to prevent memory overflow (>10MB)**\n\n';
318
- }
319
-
320
- if (truncated) {
321
- markdown += '```\n' + truncated + '\n```';
322
- } else {
323
- markdown += 'No matches found.';
324
- }
325
-
326
- if (stderr) {
327
- markdown += '\n\n**Stderr:**\n```\n' + stderr + '\n```';
328
- }
329
-
330
- return markdown;
331
- } catch (grepError) {
332
- return `Error: Neither rg nor grep is available.\n\nrg error: ${rgError}\ngrep error: ${grepError}`;
333
- }
334
- }
56
+ } as ToolDefinition;
335
57
  }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Health Tool Definition
3
+ *
4
+ * Provides health check functionality for the research system:
5
+ * - Check system health status across all components (browser pool, knowledge store, GPU lock)
6
+ */
7
+
8
+ import type {
9
+ ToolDefinition,
10
+ AgentToolResult,
11
+ ExtensionContext,
12
+ } from '@earendil-works/pi-coding-agent';
13
+ import { Type, type Static } from 'typebox';
14
+ import { healthRegistry } from '../healthcheck/index.ts';
15
+
16
+ /**
17
+ * Create the health check tool definition
18
+ */
19
+ export function createHealthTool(): ToolDefinition {
20
+ const parameters = Type.Object({
21
+ verbose: Type.Optional(Type.Boolean({
22
+ description: 'Show detailed diagnostic information.',
23
+ default: true,
24
+ })),
25
+ probe: Type.Optional(Type.Boolean({
26
+ description: 'Force liveness checks (browser/GPU models).',
27
+ default: false,
28
+ })),
29
+ });
30
+
31
+ type HealthParams = Static<typeof parameters>;
32
+
33
+ return {
34
+ name: 'health',
35
+ label: 'Health Check',
36
+ description: 'Verify system status (browser pool, knowledge store, GPU lock).',
37
+ promptSnippet: 'Run system health checks',
38
+ parameters,
39
+ renderShell: 'self',
40
+ executionMode: 'parallel',
41
+ async execute(
42
+ _toolCallId: string,
43
+ params: unknown,
44
+ _signal: AbortSignal | undefined,
45
+ _onUpdate: unknown,
46
+ _ctx: ExtensionContext,
47
+ ): Promise<AgentToolResult<unknown>> {
48
+ const { verbose = true, probe = false } = params as HealthParams;
49
+
50
+ const outputLines: string[] = [];
51
+
52
+ // Run health checks
53
+ try {
54
+ const systemHealth = await healthRegistry.runAll({ force: probe });
55
+
56
+ outputLines.push('## System Health Status');
57
+ outputLines.push('');
58
+
59
+ const statusIcon = systemHealth.status === 'healthy' ? '[OK]' :
60
+ systemHealth.status === 'degraded' ? '[WARN]' : '[ERROR]';
61
+ const statusText = systemHealth.status === 'healthy' ? 'All systems operational' :
62
+ systemHealth.status === 'degraded' ? 'System degraded (non-critical issues)' :
63
+ 'System unhealthy (critical failures)';
64
+
65
+ outputLines.push(`**${statusIcon} ${statusText}**`);
66
+ outputLines.push('');
67
+
68
+ for (const component of systemHealth.components) {
69
+ const icon = component.healthy ? '[OK]' : '[FAIL]';
70
+ const criticalMark = healthRegistry.isCritical(component.component) ? ' [CRITICAL]' : '';
71
+ outputLines.push(`${icon} **${component.component}**${criticalMark}`);
72
+
73
+ if (component.error) {
74
+ outputLines.push(` - Error: ${component.error}`);
75
+ }
76
+
77
+ if (verbose && component.diagnostic) {
78
+ const diagnostics = Object.entries(component.diagnostic)
79
+ .map(([k, v]) => `${k}: ${v}`)
80
+ .join(', ');
81
+ if (diagnostics) {
82
+ outputLines.push(` - Diagnostic: ${diagnostics}`);
83
+ }
84
+ }
85
+
86
+ if (verbose) {
87
+ outputLines.push(` - Duration: ${component.durationMs.toFixed(1)}ms`);
88
+ }
89
+ outputLines.push('');
90
+ }
91
+
92
+ outputLines.push(`Checked at: ${new Date(systemHealth.timestamp).toLocaleString()}`);
93
+
94
+ return { content: [{ type: 'text', text: outputLines.join('\n') }], details: { health: systemHealth } };
95
+ } catch (error) {
96
+ const message = error instanceof Error ? error.message : String(error);
97
+ return { content: [{ type: 'text', text: `**Health check failed**\n\n${message}` }], details: { error: message } };
98
+ }
99
+ },
100
+ };
101
+ }
@@ -1,39 +1,53 @@
1
1
  /**
2
2
  * Tool Factory
3
3
  *
4
- * Creates all research tools with properly configured SearXNG URL.
4
+ * Creates all research tools with properly configured browser manager.
5
5
  * These tools are used by both direct tool calls and researcher agents.
6
+ *
7
+ * NOTE: `research_knowledge_search` is NOT included here. It is a top-level tool
8
+ * registered via pi.registerTool() in src/index.ts. Researcher sub-agents
9
+ * already receive knowledge store data via the store_section prompt injection.
6
10
  */
7
11
 
8
- import type { ToolDefinition, ExtensionContext } from '@mariozechner/pi-coding-agent';
12
+ import type { ToolDefinition, ExtensionContext } from '@earendil-works/pi-coding-agent';
9
13
  import type { ToolUsageTracker } from '../utils/tool-usage-tracker.ts';
10
14
  import type { SystemResearchState } from '../orchestration/deep-research-types.ts';
15
+ import { createReadToolDefinition } from '@earendil-works/pi-coding-agent';
11
16
  import { createSearchTool } from './search.ts';
12
17
  import { createScrapeTool } from './scrape.ts';
13
18
  import { createSecuritySearchTool } from './security.ts';
14
19
  import { createStackexchangeTool } from './stackexchange.ts';
15
20
  import { createGrepTool } from './grep.ts';
21
+ import { createYoutubeTranscriptTool } from './youtube-transcript.ts';
22
+ import type { Config } from '../config.ts';
16
23
 
17
24
  interface CreateToolsOptions {
18
- searxngUrl: string;
25
+ cwd: string;
19
26
  ctx: ExtensionContext;
20
27
  tracker: ToolUsageTracker;
21
28
  getGlobalState?: () => SystemResearchState;
22
29
  updateGlobalLinks?: (links: string[]) => void;
23
- /** Returns tokens consumed by this researcher session so far (for context-aware scrape gating). */
30
+ /** Optional researcher ID for per-researcher scrape tracking. */
31
+ researcherId?: string;
32
+ /** Callback invoked during search with cumulative link count found so far */
33
+ onSearchProgress?: (links: number) => void;
34
+ /** Fires for each individual URL as it completes (success or failure). Used for per-URL TUI flash. */
35
+ onUrlScrapeResult?: (url: string, success: boolean) => void;
36
+ /** Returns total tokens used so far in this researcher's session (for context gating). */
24
37
  getTokensUsed?: () => number;
25
- /** Model context window size in tokens; defaults to DEFAULT_MODEL_CONTEXT_WINDOW. */
38
+ /** Context window size in tokens. */
26
39
  contextWindowSize?: number;
40
+ config?: Config;
27
41
  }
28
42
 
29
43
  /**
30
- * Create all research tools
44
+ * Create all research tools for researcher sub-agents.
31
45
  */
32
46
  export function createResearchTools(options: CreateToolsOptions): ToolDefinition[] {
33
47
  // Create a fresh fallback state per tool instance to avoid state leakage between research runs
34
- // This is only used when getGlobalState is not provided (e.g., quick mode with single researcher)
35
48
  const createFallbackState = (): SystemResearchState => ({
36
49
  version: 1,
50
+ researchId: 'fallback',
37
51
  rootQuery: '',
38
52
  complexity: 1,
39
53
  currentRound: 1,
@@ -55,15 +69,29 @@ export function createResearchTools(options: CreateToolsOptions): ToolDefinition
55
69
  };
56
70
 
57
71
  return [
58
- createSearchTool(resolvedOptions),
72
+ createReadToolDefinition(options.cwd) as ToolDefinition,
73
+ createSearchTool({
74
+ ...resolvedOptions,
75
+ onProgress: options.onSearchProgress,
76
+ }),
59
77
  createScrapeTool({
60
78
  ...resolvedOptions,
79
+ onUrlScrapeResult: options.onUrlScrapeResult,
61
80
  getTokensUsed: options.getTokensUsed,
62
81
  contextWindowSize: options.contextWindowSize,
63
82
  }),
64
83
  createSecuritySearchTool(resolvedOptions),
65
84
  createStackexchangeTool(resolvedOptions),
66
- createGrepTool(resolvedOptions),
85
+ createGrepTool({ tracker: resolvedOptions.tracker, cwd: resolvedOptions.cwd }),
86
+ createYoutubeTranscriptTool({
87
+ ctx: resolvedOptions.ctx,
88
+ tracker: resolvedOptions.tracker,
89
+ onUrlScrapeResult: options.onUrlScrapeResult,
90
+ getGlobalState: resolvedOptions.getGlobalState,
91
+ updateGlobalLinks: resolvedOptions.updateGlobalLinks,
92
+ researcherId: resolvedOptions.researcherId,
93
+ config: resolvedOptions.config,
94
+ }),
67
95
  ];
68
96
  }
69
97
 
@@ -75,3 +103,4 @@ export { createScrapeTool } from './scrape.ts';
75
103
  export { createSecuritySearchTool } from './security.ts';
76
104
  export { createStackexchangeTool } from './stackexchange.ts';
77
105
  export { createGrepTool } from './grep.ts';
106
+ export { createYoutubeTranscriptTool } from './youtube-transcript.ts';