@apmantza/greedysearch-pi 1.8.2 → 1.8.4
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/CHANGELOG.md +17 -0
- package/README.md +10 -1
- package/bin/launch.mjs +366 -366
- package/bin/search.mjs +388 -388
- package/extractors/common.mjs +291 -291
- package/extractors/gemini.mjs +146 -146
- package/extractors/google-ai.mjs +125 -125
- package/extractors/perplexity.mjs +147 -145
- package/extractors/selectors.mjs +54 -54
- package/index.ts +256 -278
- package/package.json +1 -1
- package/src/github.mjs +237 -237
- package/src/reddit.mjs +210 -0
- package/src/search/chrome.mjs +222 -222
- package/src/search/constants.mjs +37 -37
- package/src/search/defaults.mjs +14 -14
- package/src/search/engines.mjs +62 -62
- package/src/search/fetch-source.mjs +35 -3
- package/src/search/output.mjs +58 -58
- package/src/search/sources.mjs +445 -445
- package/src/search/synthesis-runner.mjs +63 -63
- package/src/search/synthesis.mjs +223 -223
- package/src/tools/deep-research-handler.ts +36 -36
- package/src/tools/greedy-search-handler.ts +53 -57
- package/src/tools/shared.ts +135 -130
- package/src/types.ts +103 -103
- package/test.mjs +423 -377
|
@@ -1,58 +1,54 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* greedy_search tool handler — multi-engine AI web search
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
6
|
-
import { Type } from "@sinclair/typebox";
|
|
7
|
-
import { formatResults } from "../formatters/results.js";
|
|
8
|
-
import { ALL_ENGINES, cdpAvailable, cdpMissingResult, errorResult, makeProgressTracker, runSearch } from "./shared.js";
|
|
9
|
-
|
|
10
|
-
export function registerGreedySearchTool(pi: ExtensionAPI, baseDir: string) {
|
|
11
|
-
pi.registerTool({
|
|
12
|
-
name: "greedy_search",
|
|
13
|
-
label: "Greedy Search",
|
|
14
|
-
description:
|
|
15
|
-
"WEB SEARCH ONLY — searches live web via Perplexity, Bing Copilot, and Google AI in parallel. " +
|
|
16
|
-
"Optionally synthesizes results with Gemini, deduplicates sources by consensus. " +
|
|
17
|
-
"Use for: library docs, recent framework changes, error messages, best practices, current events. " +
|
|
18
|
-
"Reports streaming progress as each engine completes.",
|
|
19
|
-
promptSnippet: "Multi-engine AI web search with streaming progress",
|
|
20
|
-
parameters: Type.Object({
|
|
21
|
-
query: Type.String({ description: "The search query" }),
|
|
22
|
-
engine: Type.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return errorResult("Search failed", e);
|
|
55
|
-
}
|
|
56
|
-
},
|
|
57
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* greedy_search tool handler — multi-engine AI web search
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
6
|
+
import { Type } from "@sinclair/typebox";
|
|
7
|
+
import { formatResults } from "../formatters/results.js";
|
|
8
|
+
import { ALL_ENGINES, cdpAvailable, cdpMissingResult, errorResult, makeProgressTracker, runSearch, stripQuotes } from "./shared.js";
|
|
9
|
+
|
|
10
|
+
export function registerGreedySearchTool(pi: ExtensionAPI, baseDir: string) {
|
|
11
|
+
pi.registerTool({
|
|
12
|
+
name: "greedy_search",
|
|
13
|
+
label: "Greedy Search",
|
|
14
|
+
description:
|
|
15
|
+
"WEB SEARCH ONLY — searches live web via Perplexity, Bing Copilot, and Google AI in parallel. " +
|
|
16
|
+
"Optionally synthesizes results with Gemini, deduplicates sources by consensus. " +
|
|
17
|
+
"Use for: library docs, recent framework changes, error messages, best practices, current events. " +
|
|
18
|
+
"Reports streaming progress as each engine completes.",
|
|
19
|
+
promptSnippet: "Multi-engine AI web search with streaming progress",
|
|
20
|
+
parameters: Type.Object({
|
|
21
|
+
query: Type.String({ description: "The search query" }),
|
|
22
|
+
engine: Type.String({ description: 'Engine to use: "all" (default), "perplexity", "bing", "google", "gemini", "gem". "all" fans out to Perplexity, Bing, and Google in parallel.', default: "all" }),
|
|
23
|
+
depth: Type.String({ description: 'Search depth: "fast" (single engine, ~15-30s), "standard" (3 engines + synthesis, ~30-90s), "deep" (3 engines + source fetching + synthesis + confidence, ~60-180s). Default: "standard".', default: "standard" }),
|
|
24
|
+
fullAnswer: Type.Optional(Type.Boolean({ description: "When true, returns the complete answer instead of a truncated preview (default: false, answers are shortened to ~300 chars to save tokens).", default: false })),
|
|
25
|
+
}),
|
|
26
|
+
execute: async (_toolCallId, params, signal, onUpdate) => {
|
|
27
|
+
const { query, fullAnswer: fullAnswerParam } = params as {
|
|
28
|
+
query: string; engine: string; depth?: "fast" | "standard" | "deep"; fullAnswer?: boolean;
|
|
29
|
+
};
|
|
30
|
+
const engine = stripQuotes((params as any).engine ?? "all") || "all";
|
|
31
|
+
const depth = (stripQuotes((params as any).depth ?? "standard") || "standard") as "fast" | "standard" | "deep";
|
|
32
|
+
|
|
33
|
+
if (!cdpAvailable(baseDir)) return cdpMissingResult();
|
|
34
|
+
|
|
35
|
+
const flags: string[] = [];
|
|
36
|
+
const fullAnswer = fullAnswerParam ?? (engine !== "all");
|
|
37
|
+
if (fullAnswer) flags.push("--full");
|
|
38
|
+
if (depth === "deep") flags.push("--depth", "deep");
|
|
39
|
+
else if (depth === "standard" && engine === "all") flags.push("--synthesize");
|
|
40
|
+
|
|
41
|
+
const onProgress = engine === "all"
|
|
42
|
+
? makeProgressTracker(ALL_ENGINES, onUpdate, "Searching", depth)
|
|
43
|
+
: undefined;
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const data = await runSearch(engine, query, flags, `${baseDir}/bin/search.mjs`, signal, onProgress);
|
|
47
|
+
const text = formatResults(engine, data);
|
|
48
|
+
return { content: [{ type: "text", text: text || "No results returned." }], details: { raw: data } };
|
|
49
|
+
} catch (e) {
|
|
50
|
+
return errorResult("Search failed", e);
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
});
|
|
58
54
|
}
|
package/src/tools/shared.ts
CHANGED
|
@@ -1,131 +1,136 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared types, utilities, and runSearch for Pi tool handlers
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { spawn } from "node:child_process";
|
|
6
|
-
import { existsSync } from "node:fs";
|
|
7
|
-
import { join } from "node:path";
|
|
8
|
-
import type { ProgressUpdate, ToolResult } from "../types.js";
|
|
9
|
-
|
|
10
|
-
export type { ProgressUpdate, ToolResult } from "../types.js";
|
|
11
|
-
|
|
12
|
-
// Canonical source is src/search/constants.mjs — keep in sync
|
|
13
|
-
const ALL_ENGINES = ["perplexity", "bing", "google"] as const;
|
|
14
|
-
export { ALL_ENGINES };
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Shared types, utilities, and runSearch for Pi tool handlers
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { spawn } from "node:child_process";
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import type { ProgressUpdate, ToolResult } from "../types.js";
|
|
9
|
+
|
|
10
|
+
export type { ProgressUpdate, ToolResult } from "../types.js";
|
|
11
|
+
|
|
12
|
+
// Canonical source is src/search/constants.mjs — keep in sync
|
|
13
|
+
const ALL_ENGINES = ["perplexity", "bing", "google"] as const;
|
|
14
|
+
export { ALL_ENGINES };
|
|
15
|
+
|
|
16
|
+
/** Strip surrounding double-quotes that some framework versions inject into string params */
|
|
17
|
+
export function stripQuotes(val: string): string {
|
|
18
|
+
return val.replace(/^"|"$/g, "");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Check if the CDP module is available in the package directory
|
|
23
|
+
*/
|
|
24
|
+
export function cdpAvailable(baseDir: string): boolean {
|
|
25
|
+
return existsSync(join(baseDir, "bin", "cdp.mjs"));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Create a "cdp missing" error result
|
|
30
|
+
*/
|
|
31
|
+
export function cdpMissingResult(): ToolResult {
|
|
32
|
+
return {
|
|
33
|
+
content: [
|
|
34
|
+
{
|
|
35
|
+
type: "text",
|
|
36
|
+
text: "cdp.mjs missing — try reinstalling: pi install git:github.com/apmantza/GreedySearch-pi",
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
details: {} as Record<string, unknown>,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Create an error result with a message
|
|
45
|
+
*/
|
|
46
|
+
export function errorResult(prefix: string, e: unknown): ToolResult {
|
|
47
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
48
|
+
return {
|
|
49
|
+
content: [{ type: "text", text: `${prefix}: ${msg}` }],
|
|
50
|
+
details: {} as Record<string, unknown>,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Spawn search.mjs and collect JSON results, with progress streaming via stderr.
|
|
56
|
+
* Shared by greedy_search and deep_research tool handlers.
|
|
57
|
+
*/
|
|
58
|
+
export function runSearch(
|
|
59
|
+
engine: string,
|
|
60
|
+
query: string,
|
|
61
|
+
flags: string[],
|
|
62
|
+
searchBin: string,
|
|
63
|
+
signal?: AbortSignal,
|
|
64
|
+
onProgress?: (engine: string, status: "done" | "error") => void,
|
|
65
|
+
): Promise<Record<string, unknown>> {
|
|
66
|
+
return new Promise((resolve, reject) => {
|
|
67
|
+
const proc = spawn(
|
|
68
|
+
"node",
|
|
69
|
+
[searchBin, engine, "--inline", ...flags, query],
|
|
70
|
+
{ stdio: ["ignore", "pipe", "pipe"] },
|
|
71
|
+
);
|
|
72
|
+
let out = "";
|
|
73
|
+
let err = "";
|
|
74
|
+
|
|
75
|
+
const onAbort = () => {
|
|
76
|
+
proc.kill("SIGTERM");
|
|
77
|
+
reject(new Error("Aborted"));
|
|
78
|
+
};
|
|
79
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
80
|
+
|
|
81
|
+
proc.stderr.on("data", (d: Buffer) => {
|
|
82
|
+
err += d;
|
|
83
|
+
for (const line of d.toString().split("\n")) {
|
|
84
|
+
const match = line.match(/^PROGRESS:(\w+):(done|error)$/);
|
|
85
|
+
if (match && onProgress) {
|
|
86
|
+
onProgress(match[1], match[2] as "done" | "error");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
proc.stdout.on("data", (d: Buffer) => (out += d));
|
|
92
|
+
proc.on("close", (code: number) => {
|
|
93
|
+
signal?.removeEventListener("abort", onAbort);
|
|
94
|
+
if (code !== 0) {
|
|
95
|
+
reject(new Error(err.trim() || `search.mjs exited with code ${code}`));
|
|
96
|
+
} else {
|
|
97
|
+
try {
|
|
98
|
+
resolve(JSON.parse(out.trim()));
|
|
99
|
+
} catch {
|
|
100
|
+
reject(new Error(`Invalid JSON from search.mjs: ${out.slice(0, 200)}`));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Build a progress callback that tracks completed engines.
|
|
109
|
+
* Returns an onProgress function suitable for runSearch.
|
|
110
|
+
*/
|
|
111
|
+
export function makeProgressTracker(
|
|
112
|
+
engines: readonly string[],
|
|
113
|
+
onUpdate: ((update: ProgressUpdate) => void) | undefined,
|
|
114
|
+
suffix: "Searching" | "Researching",
|
|
115
|
+
depth: string,
|
|
116
|
+
) {
|
|
117
|
+
const completed = new Set<string>();
|
|
118
|
+
|
|
119
|
+
return (eng: string, _status: "done" | "error") => {
|
|
120
|
+
completed.add(eng);
|
|
121
|
+
const parts: string[] = [];
|
|
122
|
+
for (const e of engines) {
|
|
123
|
+
if (completed.has(e)) parts.push(`✅ ${e} done`);
|
|
124
|
+
else parts.push(`⏳ ${e}`);
|
|
125
|
+
}
|
|
126
|
+
if (depth !== "fast" && completed.size >= 3)
|
|
127
|
+
parts.push("🔄 synthesizing");
|
|
128
|
+
|
|
129
|
+
onUpdate?.({
|
|
130
|
+
content: [
|
|
131
|
+
{ type: "text", text: `**${suffix}...** ${parts.join(" · ")}` },
|
|
132
|
+
],
|
|
133
|
+
details: { _progress: true },
|
|
134
|
+
} satisfies ProgressUpdate);
|
|
135
|
+
};
|
|
131
136
|
}
|
package/src/types.ts
CHANGED
|
@@ -1,104 +1,104 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TypeScript interfaces for GreedySearch data structures
|
|
3
|
-
*
|
|
4
|
-
* These types document the shape of data flowing between modules.
|
|
5
|
-
* They can be imported by TypeScript files (index.ts, tool handlers, formatters)
|
|
6
|
-
* and used for type safety without runtime overhead.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
// ============================================================================
|
|
10
|
-
// Search Result Types
|
|
11
|
-
// ============================================================================
|
|
12
|
-
|
|
13
|
-
/** A single source extracted from search results */
|
|
14
|
-
export interface Source {
|
|
15
|
-
url: string;
|
|
16
|
-
title: string;
|
|
17
|
-
type?: "official-docs" | "maintainer-blog" | "repo" | "community" | "website";
|
|
18
|
-
domain?: string;
|
|
19
|
-
snippet?: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/** Result from a single search engine */
|
|
23
|
-
export interface SearchResult {
|
|
24
|
-
engine: string;
|
|
25
|
-
answer: string;
|
|
26
|
-
sources: Source[];
|
|
27
|
-
url?: string;
|
|
28
|
-
query?: string;
|
|
29
|
-
error?: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** Synthesis result combining multiple engine results */
|
|
33
|
-
export interface SynthesisResult {
|
|
34
|
-
answer: string;
|
|
35
|
-
agreementLevel?: "consensus" | "majority" | "mixed" | "conflicting";
|
|
36
|
-
claims?: Claim[];
|
|
37
|
-
sourceIds?: string[];
|
|
38
|
-
confidence?: ConfidenceMetrics;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** A single claim within a synthesis */
|
|
42
|
-
export interface Claim {
|
|
43
|
-
text: string;
|
|
44
|
-
sourceIds: string[];
|
|
45
|
-
confidence?: "high" | "medium" | "low";
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** Confidence metrics for a synthesis */
|
|
49
|
-
export interface ConfidenceMetrics {
|
|
50
|
-
overall: number; // 0-1
|
|
51
|
-
consensus: number; // fraction of engines agreeing
|
|
52
|
-
sourceCount: number;
|
|
53
|
-
engineCount: number;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// ============================================================================
|
|
57
|
-
// Source Registry Types
|
|
58
|
-
// ============================================================================
|
|
59
|
-
|
|
60
|
-
/** A classified source in the registry */
|
|
61
|
-
export interface ClassifiedSource extends Source {
|
|
62
|
-
engineOrigin: string[];
|
|
63
|
-
isOfficial: boolean;
|
|
64
|
-
consensus: number; // fraction of engines citing this source
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// ============================================================================
|
|
68
|
-
// Tool Result Types
|
|
69
|
-
// ============================================================================
|
|
70
|
-
|
|
71
|
-
/** Progress update sent via onUpdate during long-running searches */
|
|
72
|
-
export interface ProgressUpdate {
|
|
73
|
-
content: Array<{ type: "text"; text: string }>;
|
|
74
|
-
details: { _progress: true };
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/** Pi tool result format */
|
|
78
|
-
export interface ToolResult {
|
|
79
|
-
content: Array<{ type: "text"; text: string }>;
|
|
80
|
-
details: Record<string, unknown>;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// ============================================================================
|
|
84
|
-
// Engine Configuration Types
|
|
85
|
-
// ============================================================================
|
|
86
|
-
|
|
87
|
-
/** Engine definition for the ENGINES map */
|
|
88
|
-
export interface EngineConfig {
|
|
89
|
-
/** Extractor script filename (e.g. "perplexity.mjs") */
|
|
90
|
-
script: string;
|
|
91
|
-
/** Human-readable label for progress messages */
|
|
92
|
-
label: string;
|
|
93
|
-
/** Domain pattern for source matching */
|
|
94
|
-
domain: string;
|
|
95
|
-
/** URL pattern for the engine */
|
|
96
|
-
url: string;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// ============================================================================
|
|
100
|
-
// Constants
|
|
101
|
-
// ============================================================================
|
|
102
|
-
|
|
103
|
-
// Runtime defaults are in src/search/defaults.mjs (since .ts files can't be
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript interfaces for GreedySearch data structures
|
|
3
|
+
*
|
|
4
|
+
* These types document the shape of data flowing between modules.
|
|
5
|
+
* They can be imported by TypeScript files (index.ts, tool handlers, formatters)
|
|
6
|
+
* and used for type safety without runtime overhead.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// Search Result Types
|
|
11
|
+
// ============================================================================
|
|
12
|
+
|
|
13
|
+
/** A single source extracted from search results */
|
|
14
|
+
export interface Source {
|
|
15
|
+
url: string;
|
|
16
|
+
title: string;
|
|
17
|
+
type?: "official-docs" | "maintainer-blog" | "repo" | "community" | "website";
|
|
18
|
+
domain?: string;
|
|
19
|
+
snippet?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Result from a single search engine */
|
|
23
|
+
export interface SearchResult {
|
|
24
|
+
engine: string;
|
|
25
|
+
answer: string;
|
|
26
|
+
sources: Source[];
|
|
27
|
+
url?: string;
|
|
28
|
+
query?: string;
|
|
29
|
+
error?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Synthesis result combining multiple engine results */
|
|
33
|
+
export interface SynthesisResult {
|
|
34
|
+
answer: string;
|
|
35
|
+
agreementLevel?: "consensus" | "majority" | "mixed" | "conflicting";
|
|
36
|
+
claims?: Claim[];
|
|
37
|
+
sourceIds?: string[];
|
|
38
|
+
confidence?: ConfidenceMetrics;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** A single claim within a synthesis */
|
|
42
|
+
export interface Claim {
|
|
43
|
+
text: string;
|
|
44
|
+
sourceIds: string[];
|
|
45
|
+
confidence?: "high" | "medium" | "low";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Confidence metrics for a synthesis */
|
|
49
|
+
export interface ConfidenceMetrics {
|
|
50
|
+
overall: number; // 0-1
|
|
51
|
+
consensus: number; // fraction of engines agreeing
|
|
52
|
+
sourceCount: number;
|
|
53
|
+
engineCount: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ============================================================================
|
|
57
|
+
// Source Registry Types
|
|
58
|
+
// ============================================================================
|
|
59
|
+
|
|
60
|
+
/** A classified source in the registry */
|
|
61
|
+
export interface ClassifiedSource extends Source {
|
|
62
|
+
engineOrigin: string[];
|
|
63
|
+
isOfficial: boolean;
|
|
64
|
+
consensus: number; // fraction of engines citing this source
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ============================================================================
|
|
68
|
+
// Tool Result Types
|
|
69
|
+
// ============================================================================
|
|
70
|
+
|
|
71
|
+
/** Progress update sent via onUpdate during long-running searches */
|
|
72
|
+
export interface ProgressUpdate {
|
|
73
|
+
content: Array<{ type: "text"; text: string }>;
|
|
74
|
+
details: { _progress: true };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Pi tool result format */
|
|
78
|
+
export interface ToolResult {
|
|
79
|
+
content: Array<{ type: "text"; text: string }>;
|
|
80
|
+
details: Record<string, unknown>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ============================================================================
|
|
84
|
+
// Engine Configuration Types
|
|
85
|
+
// ============================================================================
|
|
86
|
+
|
|
87
|
+
/** Engine definition for the ENGINES map */
|
|
88
|
+
export interface EngineConfig {
|
|
89
|
+
/** Extractor script filename (e.g. "perplexity.mjs") */
|
|
90
|
+
script: string;
|
|
91
|
+
/** Human-readable label for progress messages */
|
|
92
|
+
label: string;
|
|
93
|
+
/** Domain pattern for source matching */
|
|
94
|
+
domain: string;
|
|
95
|
+
/** URL pattern for the engine */
|
|
96
|
+
url: string;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ============================================================================
|
|
100
|
+
// Constants
|
|
101
|
+
// ============================================================================
|
|
102
|
+
|
|
103
|
+
// Runtime defaults are in src/search/defaults.mjs (since .ts files can't be
|
|
104
104
|
// imported directly by Node.js). Import DEFAULTS from there for runtime values.
|