@duckmind/dm-windows-x64 0.62.3 → 0.63.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/dm.exe +0 -0
- package/extensions/.dm-extensions.json +43 -183
- package/extensions/dm-context/src/context.js +1 -2
- package/extensions/dm-context/src/index.js +1 -2
- package/extensions/{greedysearch-dm/bin/cdp.mjs → dm-cua/bin/browser-cdp.mjs} +3 -3
- package/extensions/dm-cua/bin/browser-cua.mjs +1 -1
- package/extensions/{greedysearch-dm/bin/launch.mjs → dm-cua/bin/browser-launch.mjs} +12 -11
- package/extensions/dm-cua/index.js +1 -1
- package/extensions/dm-cua/src/browser-cua-lib.mjs +17 -12
- package/extensions/{greedysearch-dm/src/search → dm-cua/src/browser-helpers}/constants.mjs +11 -11
- package/extensions/{greedysearch-dm/src/utils → dm-cua/src/browser-helpers}/node-runtime.mjs +1 -1
- package/extensions/{greedysearch-dm/src/search → dm-cua/src/browser-helpers}/port-pid.mjs +1 -1
- package/extensions/dm-ponytail/extensions/ponytail.mjs +186 -0
- package/extensions/dm-ponytail/hooks/claude-codex-hooks.json +41 -0
- package/extensions/dm-ponytail/hooks/copilot-hooks.json +21 -0
- package/extensions/dm-ponytail/hooks/ponytail-activate.js +59 -0
- package/extensions/dm-ponytail/hooks/ponytail-config.js +116 -0
- package/extensions/dm-ponytail/hooks/ponytail-instructions.js +81 -0
- package/extensions/dm-ponytail/hooks/ponytail-mode-tracker.js +96 -0
- package/extensions/dm-ponytail/hooks/ponytail-runtime.js +79 -0
- package/extensions/dm-ponytail/hooks/ponytail-statusline.ps1 +24 -0
- package/extensions/dm-ponytail/hooks/ponytail-statusline.sh +18 -0
- package/extensions/dm-ponytail/hooks/ponytail-subagent.js +50 -0
- package/extensions/dm-ponytail/hooks/qoder-hooks.json +26 -0
- package/extensions/dm-ponytail/package.json +19 -0
- package/extensions/dm-ponytail/pi-extension/index.js +186 -0
- package/extensions/dm-ponytail/pi-extension/package.json +8 -0
- package/extensions/dm-ponytail/skills/ponytail/SKILL.md +120 -0
- package/extensions/dm-ponytail/skills/ponytail-audit/SKILL.md +41 -0
- package/extensions/dm-ponytail/skills/ponytail-debt/SKILL.md +44 -0
- package/extensions/dm-ponytail/skills/ponytail-gain/SKILL.md +50 -0
- package/extensions/dm-ponytail/skills/ponytail-help/SKILL.md +71 -0
- package/extensions/dm-ponytail/skills/ponytail-review/SKILL.md +57 -0
- package/package.json +2 -6
- package/extensions/dm-caveman/extensions/caveman.js +0 -300
- package/extensions/dm-caveman/package.json +0 -20
- package/extensions/dm-goal/package.json +0 -23
- package/extensions/dm-goal/src/goal.js +0 -914
- package/extensions/greedysearch-dm/bin/cdp-greedy.mjs +0 -40
- package/extensions/greedysearch-dm/bin/cdp-headless.mjs +0 -5
- package/extensions/greedysearch-dm/bin/cdp-visible.mjs +0 -5
- package/extensions/greedysearch-dm/bin/gschrome.mjs +0 -30
- package/extensions/greedysearch-dm/bin/kill-visible.mjs +0 -7
- package/extensions/greedysearch-dm/bin/launch-visible.mjs +0 -13
- package/extensions/greedysearch-dm/bin/mcp.mjs +0 -400
- package/extensions/greedysearch-dm/bin/search.mjs +0 -635
- package/extensions/greedysearch-dm/bin/visible.mjs +0 -22
- package/extensions/greedysearch-dm/extractors/bing-copilot.mjs +0 -344
- package/extensions/greedysearch-dm/extractors/chatgpt.mjs +0 -354
- package/extensions/greedysearch-dm/extractors/common.mjs +0 -794
- package/extensions/greedysearch-dm/extractors/consensus.mjs +0 -527
- package/extensions/greedysearch-dm/extractors/consent.mjs +0 -449
- package/extensions/greedysearch-dm/extractors/gemini.mjs +0 -291
- package/extensions/greedysearch-dm/extractors/google-ai.mjs +0 -86
- package/extensions/greedysearch-dm/extractors/logically.mjs +0 -589
- package/extensions/greedysearch-dm/extractors/perplexity.mjs +0 -370
- package/extensions/greedysearch-dm/extractors/selectors.mjs +0 -32
- package/extensions/greedysearch-dm/extractors/semantic-scholar.mjs +0 -199
- package/extensions/greedysearch-dm/index.js +0 -123
- package/extensions/greedysearch-dm/package.json +0 -27
- package/extensions/greedysearch-dm/skills/greedy-search/skill.md +0 -18
- package/extensions/greedysearch-dm/src/fetcher.mjs +0 -577
- package/extensions/greedysearch-dm/src/formatters/results.js +0 -95
- package/extensions/greedysearch-dm/src/formatters/sources.js +0 -57
- package/extensions/greedysearch-dm/src/formatters/synthesis.js +0 -49
- package/extensions/greedysearch-dm/src/github.mjs +0 -228
- package/extensions/greedysearch-dm/src/reddit.mjs +0 -154
- package/extensions/greedysearch-dm/src/search/browser-lifecycle.mjs +0 -340
- package/extensions/greedysearch-dm/src/search/challenge-detect.mjs +0 -156
- package/extensions/greedysearch-dm/src/search/chrome.mjs +0 -510
- package/extensions/greedysearch-dm/src/search/defaults.mjs +0 -10
- package/extensions/greedysearch-dm/src/search/engines.mjs +0 -79
- package/extensions/greedysearch-dm/src/search/fetch-source.mjs +0 -470
- package/extensions/greedysearch-dm/src/search/file-sources.mjs +0 -31
- package/extensions/greedysearch-dm/src/search/output.mjs +0 -51
- package/extensions/greedysearch-dm/src/search/paths.mjs +0 -48
- package/extensions/greedysearch-dm/src/search/pdf.mjs +0 -64
- package/extensions/greedysearch-dm/src/search/progress.mjs +0 -109
- package/extensions/greedysearch-dm/src/search/query.mjs +0 -21
- package/extensions/greedysearch-dm/src/search/recovery.mjs +0 -49
- package/extensions/greedysearch-dm/src/search/research.mjs +0 -2255
- package/extensions/greedysearch-dm/src/search/scale-aware.mjs +0 -61
- package/extensions/greedysearch-dm/src/search/simple-research.mjs +0 -418
- package/extensions/greedysearch-dm/src/search/sources.mjs +0 -412
- package/extensions/greedysearch-dm/src/search/synthesis-runner.mjs +0 -127
- package/extensions/greedysearch-dm/src/search/synthesis.mjs +0 -202
- package/extensions/greedysearch-dm/src/tools/greedy-search-handler.js +0 -209
- package/extensions/greedysearch-dm/src/tools/shared.js +0 -226
- package/extensions/greedysearch-dm/src/types.js +0 -0
- package/extensions/greedysearch-dm/src/utils/content.mjs +0 -38
- package/extensions/greedysearch-dm/src/utils/helpers.js +0 -22
- /package/extensions/{greedysearch-dm/src/search → dm-cua/src/browser-helpers}/minimize.mjs +0 -0
- /package/extensions/{greedysearch-dm/src/utils → dm-cua/src/browser-helpers}/system-cmds.mjs +0 -0
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { trimText } from "./sources.mjs";
|
|
2
|
-
import { runGeminiPrompt } from "./synthesis-runner.mjs";
|
|
3
|
-
import { parseStructuredJson } from "./synthesis.mjs";
|
|
4
|
-
const COMPLEXITY_PROMPT_TIMEOUT_MS = 30000;
|
|
5
|
-
function clampInt(value, min, max, fallback) {
|
|
6
|
-
const n = Number.parseInt(String(value ?? ""), 10);
|
|
7
|
-
if (!Number.isFinite(n))
|
|
8
|
-
return fallback;
|
|
9
|
-
return Math.min(max, Math.max(min, n));
|
|
10
|
-
}
|
|
11
|
-
export async function classifyResearchComplexity(query) {
|
|
12
|
-
const prompt = [
|
|
13
|
-
"You are a research complexity classifier.",
|
|
14
|
-
"Classify the following query by research complexity.",
|
|
15
|
-
"",
|
|
16
|
-
"- simple: A narrow factual question (what is X, define X, how does X work).",
|
|
17
|
-
" Answerable with 1-3 search queries and a short synthesis. No sub-questions.",
|
|
18
|
-
"- moderate: A focused comparison, recent change, or best-practice lookup.",
|
|
19
|
-
" Needs 2-4 angles but stays within one domain.",
|
|
20
|
-
"- complex: Multi-faceted survey, landscape analysis, or cross-domain investigation.",
|
|
21
|
-
" Benefits from parallel research directions and iterative deepening.",
|
|
22
|
-
"",
|
|
23
|
-
"Respond ONLY with JSON wrapped in BEGIN_JSON / END_JSON markers:",
|
|
24
|
-
"BEGIN_JSON",
|
|
25
|
-
JSON.stringify({
|
|
26
|
-
complexity: "simple",
|
|
27
|
-
reasoning: "narrow factual question",
|
|
28
|
-
suggestedBreadth: 1,
|
|
29
|
-
suggestedIterations: 1,
|
|
30
|
-
needsAcademicSources: false
|
|
31
|
-
}, null, 2),
|
|
32
|
-
"END_JSON",
|
|
33
|
-
"",
|
|
34
|
-
"Query: " + query
|
|
35
|
-
].join(`
|
|
36
|
-
`);
|
|
37
|
-
try {
|
|
38
|
-
const raw = await runGeminiPrompt(prompt, {
|
|
39
|
-
timeoutMs: COMPLEXITY_PROMPT_TIMEOUT_MS
|
|
40
|
-
});
|
|
41
|
-
const parsed = parseStructuredJson(raw?.answer || "") || {};
|
|
42
|
-
const complexity = ["simple", "moderate", "complex"].includes(parsed.complexity) ? parsed.complexity : "moderate";
|
|
43
|
-
return {
|
|
44
|
-
complexity,
|
|
45
|
-
reasoning: trimText(parsed.reasoning || "", 200),
|
|
46
|
-
suggestedBreadth: clampInt(parsed.suggestedBreadth, 1, 5, complexity === "simple" ? 1 : 3),
|
|
47
|
-
suggestedIterations: clampInt(parsed.suggestedIterations, 1, 3, complexity === "simple" ? 1 : 2),
|
|
48
|
-
needsAcademicSources: parsed.needsAcademicSources === true
|
|
49
|
-
};
|
|
50
|
-
} catch (error) {
|
|
51
|
-
process.stderr.write(`[greedysearch] Complexity classification failed, defaulting to moderate: ${error.message}
|
|
52
|
-
`);
|
|
53
|
-
return {
|
|
54
|
-
complexity: "moderate",
|
|
55
|
-
reasoning: "classification failed",
|
|
56
|
-
suggestedBreadth: 3,
|
|
57
|
-
suggestedIterations: 2,
|
|
58
|
-
needsAcademicSources: false
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
}
|
|
@@ -1,418 +0,0 @@
|
|
|
1
|
-
import { ALL_ENGINES, RESEARCH_ENGINES } from "./constants.mjs";
|
|
2
|
-
import {
|
|
3
|
-
buildSourceRegistry,
|
|
4
|
-
mergeFetchDataIntoSources,
|
|
5
|
-
normalizeUrl,
|
|
6
|
-
trimText
|
|
7
|
-
} from "./sources.mjs";
|
|
8
|
-
import {
|
|
9
|
-
auditCitations,
|
|
10
|
-
buildFinalReportPrompt,
|
|
11
|
-
buildSynthesisFromEvidencePrompt,
|
|
12
|
-
computeResearchFloor,
|
|
13
|
-
createQuestionLedger,
|
|
14
|
-
extractEvidenceFromSources,
|
|
15
|
-
reconcileQuestionsFromSynthesis,
|
|
16
|
-
runCitationUrlCheck,
|
|
17
|
-
writeResearchBundle
|
|
18
|
-
} from "./research.mjs";
|
|
19
|
-
import { parseStructuredJson } from "./synthesis.mjs";
|
|
20
|
-
import { writeSourcesToFiles } from "./file-sources.mjs";
|
|
21
|
-
import { fetchMultipleSources } from "./fetch-source.mjs";
|
|
22
|
-
import { runGeminiPrompt } from "./synthesis-runner.mjs";
|
|
23
|
-
import { createProgressTracker } from "./progress.mjs";
|
|
24
|
-
import { spawn } from "node:child_process";
|
|
25
|
-
import { nodeRuntimeCommand } from "../utils/node-runtime.mjs";
|
|
26
|
-
import { join } from "node:path";
|
|
27
|
-
import { fileURLToPath } from "node:url";
|
|
28
|
-
const __dir = fileURLToPath(new URL(".", import.meta.url)).replace(/^\/([A-Z]:)/, "$1");
|
|
29
|
-
const SEARCH_BIN = join(__dir, "..", "..", "bin", "search.mjs");
|
|
30
|
-
function uniqueStrings(items, limit = 1 / 0) {
|
|
31
|
-
const seen = new Set;
|
|
32
|
-
const out = [];
|
|
33
|
-
for (const item of items || []) {
|
|
34
|
-
const clean = trimText(String(item || ""), 1000);
|
|
35
|
-
if (!clean || seen.has(clean))
|
|
36
|
-
continue;
|
|
37
|
-
seen.add(clean);
|
|
38
|
-
out.push(clean);
|
|
39
|
-
if (out.length >= limit)
|
|
40
|
-
break;
|
|
41
|
-
}
|
|
42
|
-
return out;
|
|
43
|
-
}
|
|
44
|
-
export function buildSearchAngles(query) {
|
|
45
|
-
const trimmed = String(query || "").trim();
|
|
46
|
-
if (!trimmed)
|
|
47
|
-
return [];
|
|
48
|
-
return [
|
|
49
|
-
`${trimmed} — definition and overview`,
|
|
50
|
-
`${trimmed} — how it works, mechanism, or key details`,
|
|
51
|
-
`${trimmed} — current usage, comparison, or best practices`
|
|
52
|
-
];
|
|
53
|
-
}
|
|
54
|
-
export function mergeSourcesByUrl(existing, incoming) {
|
|
55
|
-
const urlMap = new Map;
|
|
56
|
-
for (const s of existing || []) {
|
|
57
|
-
const key = s?.canonicalUrl || s?.finalUrl || s?.url;
|
|
58
|
-
if (key)
|
|
59
|
-
urlMap.set(key, s);
|
|
60
|
-
}
|
|
61
|
-
for (const s of incoming || []) {
|
|
62
|
-
const key = s?.canonicalUrl || s?.finalUrl || s?.url;
|
|
63
|
-
if (!key)
|
|
64
|
-
continue;
|
|
65
|
-
if (urlMap.has(key)) {
|
|
66
|
-
const merged = {
|
|
67
|
-
...urlMap.get(key),
|
|
68
|
-
angles: [
|
|
69
|
-
...urlMap.get(key).angles || [urlMap.get(key).query || ""],
|
|
70
|
-
s.query || ""
|
|
71
|
-
]
|
|
72
|
-
};
|
|
73
|
-
urlMap.set(key, merged);
|
|
74
|
-
} else {
|
|
75
|
-
urlMap.set(key, s);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
return Array.from(urlMap.values());
|
|
79
|
-
}
|
|
80
|
-
const _enginePattern = ALL_ENGINES.join("|");
|
|
81
|
-
const _engineRegex = new RegExp(`^\\[(${_enginePattern})\\]`);
|
|
82
|
-
function shouldForwardChildStderr(line) {
|
|
83
|
-
return /^PROGRESS:/.test(line) || /^\[greedysearch\]/.test(line) || _engineRegex.test(line) || /^GreedySearch Chrome/.test(line) || /^Launching GreedySearch Chrome/.test(line) || /^Headless mode/.test(line) || /^Ready\.?$/.test(line);
|
|
84
|
-
}
|
|
85
|
-
async function runFastAllSearch(query, { locale = null, short = true } = {}) {
|
|
86
|
-
const args = [SEARCH_BIN, "all", "--inline", "--stdin", "--fast"];
|
|
87
|
-
if (!short)
|
|
88
|
-
args.push("--full");
|
|
89
|
-
if (locale)
|
|
90
|
-
args.push("--locale", locale);
|
|
91
|
-
return new Promise((resolve, reject) => {
|
|
92
|
-
const proc = spawn(nodeRuntimeCommand(), args, {
|
|
93
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
94
|
-
env: { ...process.env, GREEDY_SEARCH_RESEARCH_CHILD: "1" }
|
|
95
|
-
});
|
|
96
|
-
proc.stdin.write(query);
|
|
97
|
-
proc.stdin.end();
|
|
98
|
-
let out = "";
|
|
99
|
-
let err = "";
|
|
100
|
-
let stderrBuffer = "";
|
|
101
|
-
proc.stdout.on("data", (d) => out += d);
|
|
102
|
-
proc.stderr.on("data", (d) => {
|
|
103
|
-
err += d;
|
|
104
|
-
stderrBuffer += d.toString();
|
|
105
|
-
const lines = stderrBuffer.split(`
|
|
106
|
-
`);
|
|
107
|
-
stderrBuffer = lines.pop() || "";
|
|
108
|
-
for (const line of lines) {
|
|
109
|
-
if (shouldForwardChildStderr(line)) {
|
|
110
|
-
process.stderr.write(`${line}
|
|
111
|
-
`);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
const t = setTimeout(() => {
|
|
116
|
-
proc.kill();
|
|
117
|
-
reject(new Error(`research child search timed out for: ${query}`));
|
|
118
|
-
}, 140000);
|
|
119
|
-
proc.on("close", (code) => {
|
|
120
|
-
clearTimeout(t);
|
|
121
|
-
if (code !== 0) {
|
|
122
|
-
reject(new Error(err.trim() || `search child exited with code ${code}`));
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
try {
|
|
126
|
-
resolve(JSON.parse(out.trim()));
|
|
127
|
-
} catch {
|
|
128
|
-
reject(new Error(`Invalid JSON from research child: ${out.slice(0, 200)}`));
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
function annotateFetchedSourcesWithIds(fetchedSources, sources) {
|
|
134
|
-
const byUrl = new Map;
|
|
135
|
-
for (const source of sources || []) {
|
|
136
|
-
const key = normalizeUrl(source?.canonicalUrl || source?.finalUrl || source?.url);
|
|
137
|
-
if (key && source?.id)
|
|
138
|
-
byUrl.set(key, source.id);
|
|
139
|
-
}
|
|
140
|
-
return (fetchedSources || []).map((source, index) => {
|
|
141
|
-
const key = normalizeUrl(source?.finalUrl || source?.canonicalUrl || source?.url);
|
|
142
|
-
return {
|
|
143
|
-
...source,
|
|
144
|
-
id: source?.id || byUrl.get(key) || `F${index + 1}`
|
|
145
|
-
};
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
function questionProgress(questions) {
|
|
149
|
-
const total = questions.length;
|
|
150
|
-
const closed = questions.filter((q) => q.status === "closed").length;
|
|
151
|
-
return { total, closed, open: Math.max(0, total - closed) };
|
|
152
|
-
}
|
|
153
|
-
export async function runSimpleResearchMode({
|
|
154
|
-
query,
|
|
155
|
-
locale = null,
|
|
156
|
-
maxSources = 5,
|
|
157
|
-
qualityThreshold = 8.5,
|
|
158
|
-
writeBundle = process.env.GREEDY_RESEARCH_BUNDLE !== "0",
|
|
159
|
-
researchOutDir = null
|
|
160
|
-
} = {}) {
|
|
161
|
-
const startedAt = new Date().toISOString();
|
|
162
|
-
const startMs = Date.now();
|
|
163
|
-
const questions = createQuestionLedger(query);
|
|
164
|
-
const extractedSourceKeys = new Set;
|
|
165
|
-
process.stderr.write(`[greedysearch] Simple research mode: single-pass for "${trimText(query, 80)}"
|
|
166
|
-
`);
|
|
167
|
-
const searchAnglesCount = 3;
|
|
168
|
-
const totalSteps = searchAnglesCount + 1 + 2;
|
|
169
|
-
const progressTracker = createProgressTracker({
|
|
170
|
-
totalActions: totalSteps,
|
|
171
|
-
totalRounds: 1,
|
|
172
|
-
totalFetches: 1,
|
|
173
|
-
silent: process.env.GREEDY_RESEARCH_QUIET === "1"
|
|
174
|
-
});
|
|
175
|
-
progressTracker.startRound(1);
|
|
176
|
-
let combinedSources = [];
|
|
177
|
-
let fetchedSources = [];
|
|
178
|
-
const searchAngles = buildSearchAngles(query);
|
|
179
|
-
const searchResults = [];
|
|
180
|
-
progressTracker.startAction("search", `${searchAngles.length} angles in parallel`);
|
|
181
|
-
const angleOutcomes = await Promise.allSettled(searchAngles.map((angle) => runFastAllSearch(angle, { locale, short: true })));
|
|
182
|
-
for (let i = 0;i < searchAngles.length; i++) {
|
|
183
|
-
const angle = searchAngles[i];
|
|
184
|
-
const outcome = angleOutcomes[i];
|
|
185
|
-
progressTracker.endAction();
|
|
186
|
-
if (outcome.status === "fulfilled") {
|
|
187
|
-
const result = outcome.value;
|
|
188
|
-
searchResults.push({ angle, result });
|
|
189
|
-
const sources = buildSourceRegistry(result, angle);
|
|
190
|
-
combinedSources = mergeSourcesByUrl(combinedSources, sources);
|
|
191
|
-
} else {
|
|
192
|
-
process.stderr.write(`[greedysearch] Simple search angle "${angle}" failed: ${outcome.reason.message}
|
|
193
|
-
`);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
process.stderr.write(`PROGRESS:research:simple:fetching
|
|
197
|
-
`);
|
|
198
|
-
if (combinedSources.length > 0) {
|
|
199
|
-
try {
|
|
200
|
-
progressTracker.startFetch(`top ${Math.min(maxSources, combinedSources.length)} sources`);
|
|
201
|
-
fetchedSources = await fetchMultipleSources(combinedSources, Math.min(maxSources, combinedSources.length), 8000, Math.min(3, maxSources));
|
|
202
|
-
progressTracker.endFetch(true);
|
|
203
|
-
combinedSources = mergeFetchDataIntoSources(combinedSources, fetchedSources);
|
|
204
|
-
} catch (error) {
|
|
205
|
-
progressTracker.endFetch(false);
|
|
206
|
-
process.stderr.write(`[greedysearch] Source fetching failed: ${error.message}
|
|
207
|
-
`);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
fetchedSources = annotateFetchedSourcesWithIds(fetchedSources, combinedSources);
|
|
211
|
-
process.stderr.write(`PROGRESS:research:simple:evidence
|
|
212
|
-
`);
|
|
213
|
-
let evidenceItems = [];
|
|
214
|
-
try {
|
|
215
|
-
const evidenceRun = await extractEvidenceFromSources({
|
|
216
|
-
query,
|
|
217
|
-
questions,
|
|
218
|
-
fetchedSources,
|
|
219
|
-
extractedSourceKeys
|
|
220
|
-
});
|
|
221
|
-
evidenceItems = evidenceRun.evidence || [];
|
|
222
|
-
for (const evidence of evidenceRun.evidence) {
|
|
223
|
-
const answered = Array.isArray(evidence.answers) ? evidence.answers : [];
|
|
224
|
-
for (const ans of answered) {
|
|
225
|
-
const id = ans?.id || ans?.question;
|
|
226
|
-
if (id) {
|
|
227
|
-
const target = questions.find((q) => q.id === id);
|
|
228
|
-
if (target) {
|
|
229
|
-
target.status = "closed";
|
|
230
|
-
target.closedRound = 1;
|
|
231
|
-
if (ans.evidence)
|
|
232
|
-
target.evidence = uniqueStrings([...target.evidence || [], ans.evidence], 4);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
const newQs = Array.isArray(evidence.newQuestions) ? evidence.newQuestions : [];
|
|
237
|
-
for (const q of newQs) {
|
|
238
|
-
const clean = trimText(String(q), 320);
|
|
239
|
-
if (clean && !questions.some((x) => x.question === clean)) {
|
|
240
|
-
questions.push({
|
|
241
|
-
id: `Q${questions.length + 1}`,
|
|
242
|
-
question: clean,
|
|
243
|
-
status: "open",
|
|
244
|
-
reason: "Discovered gap/follow-up",
|
|
245
|
-
createdRound: 1,
|
|
246
|
-
evidence: [],
|
|
247
|
-
sourceIds: []
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
} catch (error) {
|
|
253
|
-
process.stderr.write(`[greedysearch] Evidence extraction failed: ${error.message}
|
|
254
|
-
`);
|
|
255
|
-
}
|
|
256
|
-
process.stderr.write(`PROGRESS:research:simple:synthesizing
|
|
257
|
-
`);
|
|
258
|
-
let synthesis = {
|
|
259
|
-
answer: "",
|
|
260
|
-
agreement: { level: "mixed", summary: "Single-pass synthesis." },
|
|
261
|
-
differences: [],
|
|
262
|
-
caveats: [],
|
|
263
|
-
claims: [],
|
|
264
|
-
recommendedSources: combinedSources.slice(0, 4).map((s) => s.id),
|
|
265
|
-
synthesized: false
|
|
266
|
-
};
|
|
267
|
-
if (evidenceItems.length > 0) {
|
|
268
|
-
try {
|
|
269
|
-
progressTracker.startAction("synth-evidence", "from evidence");
|
|
270
|
-
const rawReport = await runGeminiPrompt(buildSynthesisFromEvidencePrompt(query, combinedSources, questions, evidenceItems), { timeoutMs: 120000 });
|
|
271
|
-
progressTracker.endAction();
|
|
272
|
-
synthesis = {
|
|
273
|
-
...synthesis,
|
|
274
|
-
...parseStructuredJson(rawReport?.answer || "") || {}
|
|
275
|
-
};
|
|
276
|
-
synthesis.synthesized = Array.isArray(synthesis.claims) && synthesis.claims.length > 0;
|
|
277
|
-
} catch (error) {
|
|
278
|
-
process.stderr.write(`[greedysearch] Evidence synthesis failed: ${error.message}
|
|
279
|
-
`);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
if (!synthesis.synthesized && combinedSources.length > 0) {
|
|
283
|
-
try {
|
|
284
|
-
progressTracker.startAction("synth-final", "fallback report");
|
|
285
|
-
const rawReport = await runGeminiPrompt(buildFinalReportPrompt(query, [{ round: 1, learnings: [], gaps: [], actions: [] }], combinedSources, questions, evidenceItems), { timeoutMs: 120000 });
|
|
286
|
-
progressTracker.endAction();
|
|
287
|
-
synthesis = {
|
|
288
|
-
...synthesis,
|
|
289
|
-
...parseStructuredJson(rawReport?.answer || "") || {}
|
|
290
|
-
};
|
|
291
|
-
synthesis.synthesized = Array.isArray(synthesis.claims) && synthesis.claims.length > 0;
|
|
292
|
-
} catch (error) {
|
|
293
|
-
process.stderr.write(`[greedysearch] Final synthesis failed: ${error.message}
|
|
294
|
-
`);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
process.stderr.write(`PROGRESS:research:simple:audit
|
|
298
|
-
`);
|
|
299
|
-
const citationAudit = auditCitations(synthesis.answer || "", combinedSources);
|
|
300
|
-
const citationUrls = await runCitationUrlCheck(combinedSources, citationAudit);
|
|
301
|
-
reconcileQuestionsFromSynthesis(questions, synthesis, citationAudit);
|
|
302
|
-
const allGaps = uniqueStrings(synthesis.caveats || []);
|
|
303
|
-
const floor = computeResearchFloor({
|
|
304
|
-
sources: combinedSources,
|
|
305
|
-
fetchedSources,
|
|
306
|
-
synthesis,
|
|
307
|
-
citationAudit,
|
|
308
|
-
gaps: allGaps,
|
|
309
|
-
questions,
|
|
310
|
-
rounds: [{ round: 1, actions: [], learnings: [], gaps: allGaps }],
|
|
311
|
-
qualityScore: synthesis.synthesized ? 8 : 5,
|
|
312
|
-
qualityThreshold,
|
|
313
|
-
maxSources
|
|
314
|
-
});
|
|
315
|
-
const finishedAt = new Date().toISOString();
|
|
316
|
-
const durationMs = Date.now() - startMs;
|
|
317
|
-
const baseManifest = {
|
|
318
|
-
startedAt,
|
|
319
|
-
finishedAt,
|
|
320
|
-
durationMs,
|
|
321
|
-
rounds: 1,
|
|
322
|
-
terminationReason: "simple_single_pass"
|
|
323
|
-
};
|
|
324
|
-
let bundle = null;
|
|
325
|
-
let fetchedFiles;
|
|
326
|
-
if (writeBundle) {
|
|
327
|
-
process.stderr.write(`PROGRESS:research:simple:bundle
|
|
328
|
-
`);
|
|
329
|
-
try {
|
|
330
|
-
bundle = await writeResearchBundle({
|
|
331
|
-
query,
|
|
332
|
-
rounds: [
|
|
333
|
-
{
|
|
334
|
-
round: 1,
|
|
335
|
-
actions: [],
|
|
336
|
-
learnings: [],
|
|
337
|
-
gaps: allGaps,
|
|
338
|
-
evidence: evidenceItems
|
|
339
|
-
}
|
|
340
|
-
],
|
|
341
|
-
sources: combinedSources,
|
|
342
|
-
fetchedSources,
|
|
343
|
-
evidenceItems,
|
|
344
|
-
synthesis,
|
|
345
|
-
citationAudit,
|
|
346
|
-
citationUrls,
|
|
347
|
-
floor,
|
|
348
|
-
manifest: {
|
|
349
|
-
...baseManifest,
|
|
350
|
-
engines: RESEARCH_ENGINES,
|
|
351
|
-
synthesizer: "gemini",
|
|
352
|
-
actionsRun: 1,
|
|
353
|
-
searches: 1,
|
|
354
|
-
fetches: fetchedSources.length,
|
|
355
|
-
sourcesFetched: fetchedSources.filter((s) => s?.contentChars > 100).length,
|
|
356
|
-
engineFailures: [],
|
|
357
|
-
floorMet: floor.floorMet
|
|
358
|
-
},
|
|
359
|
-
allGaps,
|
|
360
|
-
questions,
|
|
361
|
-
outDir: researchOutDir
|
|
362
|
-
});
|
|
363
|
-
fetchedFiles = bundle.sourceFiles;
|
|
364
|
-
delete bundle.sourceFiles;
|
|
365
|
-
} catch (error) {
|
|
366
|
-
process.stderr.write(`[greedysearch] Research bundle write failed: ${error.message}
|
|
367
|
-
`);
|
|
368
|
-
bundle = { error: error.message || String(error) };
|
|
369
|
-
fetchedFiles = await writeSourcesToFiles(fetchedSources);
|
|
370
|
-
}
|
|
371
|
-
} else {
|
|
372
|
-
fetchedFiles = await writeSourcesToFiles(fetchedSources);
|
|
373
|
-
}
|
|
374
|
-
process.stderr.write(`PROGRESS:research:done
|
|
375
|
-
`);
|
|
376
|
-
progressTracker.endRound();
|
|
377
|
-
progressTracker.finish();
|
|
378
|
-
return {
|
|
379
|
-
query,
|
|
380
|
-
_research: {
|
|
381
|
-
mode: "simple",
|
|
382
|
-
breadth: 1,
|
|
383
|
-
iterations: 1,
|
|
384
|
-
maxSources,
|
|
385
|
-
rounds: [
|
|
386
|
-
{
|
|
387
|
-
round: 1,
|
|
388
|
-
actions: [],
|
|
389
|
-
learnings: [],
|
|
390
|
-
gaps: allGaps,
|
|
391
|
-
evidence: evidenceItems
|
|
392
|
-
}
|
|
393
|
-
],
|
|
394
|
-
learnings: [],
|
|
395
|
-
gaps: allGaps,
|
|
396
|
-
evidence: evidenceItems,
|
|
397
|
-
questions,
|
|
398
|
-
questionProgress: questionProgress(questions),
|
|
399
|
-
qualityHistory: [synthesis.synthesized ? 8 : 5],
|
|
400
|
-
terminationReason: "simple_single_pass",
|
|
401
|
-
qualityThreshold,
|
|
402
|
-
floor,
|
|
403
|
-
bundle,
|
|
404
|
-
manifest: baseManifest
|
|
405
|
-
},
|
|
406
|
-
_citationAudit: citationAudit,
|
|
407
|
-
_citationUrls: citationUrls,
|
|
408
|
-
_sources: combinedSources,
|
|
409
|
-
_fetchedSources: fetchedFiles,
|
|
410
|
-
_synthesis: synthesis,
|
|
411
|
-
_confidence: {
|
|
412
|
-
sourcesCount: combinedSources.length,
|
|
413
|
-
fetchedSourceSuccessRate: fetchedSources.length > 0 ? fetchedSources.filter((source) => source.contentChars > 100).length / fetchedSources.length : 0,
|
|
414
|
-
agreementLevel: synthesis.agreement?.level || "mixed",
|
|
415
|
-
floorMet: floor.floorMet
|
|
416
|
-
}
|
|
417
|
-
};
|
|
418
|
-
}
|