open-research 1.1.1 → 1.2.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/builtin-skills/draft-paper/references/formats/imrad.md +150 -0
- package/builtin-skills/draft-paper/references/formats/position-paper.md +170 -0
- package/builtin-skills/draft-paper/references/formats/systematic-review.md +176 -0
- package/builtin-skills/draft-paper/references/venues/acl.md +146 -0
- package/builtin-skills/draft-paper/references/venues/chi.md +131 -0
- package/builtin-skills/draft-paper/references/venues/ieee-transactions.md +173 -0
- package/builtin-skills/draft-paper/references/venues/nature.md +167 -0
- package/builtin-skills/draft-paper/references/venues/neurips.md +115 -0
- package/dist/chunk-32Q63SUH.js +6440 -0
- package/dist/chunk-CRSHN3PQ.js +98 -0
- package/dist/chunk-EW27OWCA.js +272 -0
- package/dist/{chunk-HRVDYJEC.js → chunk-KOBMIIQM.js} +6 -1
- package/dist/chunk-W3MWVV2O.js +533 -0
- package/dist/{chunk-TJA4CAZE.js → chunk-XOSPMXYH.js} +43 -101
- package/dist/cli.js +351 -6499
- package/dist/context-manager-2FA5ANQN.js +28 -0
- package/dist/finish-subagent-NCDLMSBT.js +22 -0
- package/dist/server/serve.js +20 -0
- package/dist/server-PLHMTHCG.js +16 -0
- package/dist/{sessions-KL4LUGD7.js → sessions-SDED5HVO.js} +1 -1
- package/dist/traverse-citations-CPKPE33Y.js +90 -0
- package/dist/{web-search-IBZ6UAXL.js → web-search-OBNKKXV7.js} +87 -14
- package/package.json +3 -1
- package/dist/{manager-queue-FBAUCAGI.js → manager-queue-NK5B47A4.js} +4 -4
- package/dist/{query-agent-WM6UNZ37.js → query-agent-HINWAVC5.js} +3 -3
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {
|
|
2
|
+
compactConversation,
|
|
3
|
+
createSessionUsage,
|
|
4
|
+
estimateConversationTokens,
|
|
5
|
+
estimateMessageTokens,
|
|
6
|
+
estimateTokens,
|
|
7
|
+
getCompactThreshold,
|
|
8
|
+
getContextWindow,
|
|
9
|
+
manualCompact,
|
|
10
|
+
maybeCompact,
|
|
11
|
+
pruneToolOutputs,
|
|
12
|
+
updateUsageFromApi
|
|
13
|
+
} from "./chunk-EW27OWCA.js";
|
|
14
|
+
import "./chunk-GVEVKDGV.js";
|
|
15
|
+
import "./chunk-3RG5ZIWI.js";
|
|
16
|
+
export {
|
|
17
|
+
compactConversation,
|
|
18
|
+
createSessionUsage,
|
|
19
|
+
estimateConversationTokens,
|
|
20
|
+
estimateMessageTokens,
|
|
21
|
+
estimateTokens,
|
|
22
|
+
getCompactThreshold,
|
|
23
|
+
getContextWindow,
|
|
24
|
+
manualCompact,
|
|
25
|
+
maybeCompact,
|
|
26
|
+
pruneToolOutputs,
|
|
27
|
+
updateUsageFromApi
|
|
28
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import "./chunk-3RG5ZIWI.js";
|
|
2
|
+
|
|
3
|
+
// src/lib/agent/tools/finish-subagent.ts
|
|
4
|
+
var FINISH_SUBAGENT_SENTINEL = "__FINISH_SUBAGENT__";
|
|
5
|
+
function executeFinishSubagent(args) {
|
|
6
|
+
const handoff = {
|
|
7
|
+
summary: args.summary || "Task completed.",
|
|
8
|
+
filesCreated: args.files_created ?? [],
|
|
9
|
+
filesModified: args.files_modified ?? [],
|
|
10
|
+
keyFindings: args.key_findings ?? [],
|
|
11
|
+
status: args.status || "completed",
|
|
12
|
+
blockedReason: args.blocked_reason
|
|
13
|
+
};
|
|
14
|
+
return {
|
|
15
|
+
result: FINISH_SUBAGENT_SENTINEL,
|
|
16
|
+
handoff
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export {
|
|
20
|
+
FINISH_SUBAGENT_SENTINEL,
|
|
21
|
+
executeFinishSubagent
|
|
22
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createApp
|
|
3
|
+
} from "../chunk-W3MWVV2O.js";
|
|
4
|
+
import "../chunk-32Q63SUH.js";
|
|
5
|
+
import "../chunk-EW27OWCA.js";
|
|
6
|
+
import "../chunk-KOBMIIQM.js";
|
|
7
|
+
import "../chunk-3GZIDCV2.js";
|
|
8
|
+
import "../chunk-XOSPMXYH.js";
|
|
9
|
+
import "../chunk-CRSHN3PQ.js";
|
|
10
|
+
import "../chunk-77Q5B5H7.js";
|
|
11
|
+
import "../chunk-4HCPHCC2.js";
|
|
12
|
+
import "../chunk-GVEVKDGV.js";
|
|
13
|
+
import "../chunk-3RG5ZIWI.js";
|
|
14
|
+
|
|
15
|
+
// src/server/serve.ts
|
|
16
|
+
import { serve } from "@hono/node-server";
|
|
17
|
+
var port = parseInt(process.env.PORT ?? "3210", 10);
|
|
18
|
+
var { app } = createApp();
|
|
19
|
+
console.log(`Open Research server listening on http://localhost:${port}`);
|
|
20
|
+
serve({ fetch: app.fetch, port });
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createApp
|
|
3
|
+
} from "./chunk-W3MWVV2O.js";
|
|
4
|
+
import "./chunk-32Q63SUH.js";
|
|
5
|
+
import "./chunk-EW27OWCA.js";
|
|
6
|
+
import "./chunk-KOBMIIQM.js";
|
|
7
|
+
import "./chunk-3GZIDCV2.js";
|
|
8
|
+
import "./chunk-XOSPMXYH.js";
|
|
9
|
+
import "./chunk-CRSHN3PQ.js";
|
|
10
|
+
import "./chunk-77Q5B5H7.js";
|
|
11
|
+
import "./chunk-4HCPHCC2.js";
|
|
12
|
+
import "./chunk-GVEVKDGV.js";
|
|
13
|
+
import "./chunk-3RG5ZIWI.js";
|
|
14
|
+
export {
|
|
15
|
+
createApp
|
|
16
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getSemanticScholarApiKey,
|
|
3
|
+
loadOpenResearchConfig
|
|
4
|
+
} from "./chunk-CRSHN3PQ.js";
|
|
5
|
+
import "./chunk-77Q5B5H7.js";
|
|
6
|
+
import "./chunk-4HCPHCC2.js";
|
|
7
|
+
import "./chunk-3RG5ZIWI.js";
|
|
8
|
+
|
|
9
|
+
// src/lib/agent/tools/traverse-citations.ts
|
|
10
|
+
var S2_BASE = "https://api.semanticscholar.org/graph/v1";
|
|
11
|
+
var FIELDS = "title,url,year,venue,citationCount,externalIds,abstract";
|
|
12
|
+
async function executeTraverseCitations(args) {
|
|
13
|
+
const { paper_id, direction, limit = 10 } = args;
|
|
14
|
+
if (!paper_id) return "Error: paper_id is required.";
|
|
15
|
+
if (direction !== "references" && direction !== "citations") {
|
|
16
|
+
return 'Error: direction must be "references" or "citations".';
|
|
17
|
+
}
|
|
18
|
+
const config = await loadOpenResearchConfig().catch(() => null);
|
|
19
|
+
const apiKey = getSemanticScholarApiKey(config);
|
|
20
|
+
let resolvedId = paper_id;
|
|
21
|
+
if (paper_id.startsWith("10.")) {
|
|
22
|
+
resolvedId = `DOI:${paper_id}`;
|
|
23
|
+
} else if (/^\d{4}\.\d{4,5}/.test(paper_id)) {
|
|
24
|
+
resolvedId = `ArXiv:${paper_id}`;
|
|
25
|
+
}
|
|
26
|
+
const endpoint = `${S2_BASE}/paper/${encodeURIComponent(resolvedId)}/${direction}`;
|
|
27
|
+
const url = new URL(endpoint);
|
|
28
|
+
url.searchParams.set("fields", FIELDS);
|
|
29
|
+
url.searchParams.set("limit", String(Math.min(limit, 50)));
|
|
30
|
+
const headers = {
|
|
31
|
+
"Accept": "application/json"
|
|
32
|
+
};
|
|
33
|
+
if (apiKey) headers["x-api-key"] = apiKey;
|
|
34
|
+
try {
|
|
35
|
+
const response = await fetch(url.toString(), {
|
|
36
|
+
headers,
|
|
37
|
+
signal: AbortSignal.timeout(15e3)
|
|
38
|
+
});
|
|
39
|
+
if (!response.ok) {
|
|
40
|
+
if (response.status === 404) return `Paper not found: ${paper_id}`;
|
|
41
|
+
return `Semantic Scholar API error: ${response.status}`;
|
|
42
|
+
}
|
|
43
|
+
const body = await response.json();
|
|
44
|
+
const edges = body.data ?? [];
|
|
45
|
+
const papers = [];
|
|
46
|
+
for (const edge of edges) {
|
|
47
|
+
const paper = direction === "citations" ? edge.citingPaper : edge.citedPaper;
|
|
48
|
+
if (!paper?.title) continue;
|
|
49
|
+
papers.push({
|
|
50
|
+
title: paper.title,
|
|
51
|
+
year: paper.year ?? void 0,
|
|
52
|
+
venue: paper.venue || void 0,
|
|
53
|
+
citationCount: paper.citationCount ?? 0,
|
|
54
|
+
url: paper.url ?? `https://www.semanticscholar.org/paper/${paper.paperId}`,
|
|
55
|
+
doi: paper.externalIds?.DOI,
|
|
56
|
+
arxivId: paper.externalIds?.ArXiv,
|
|
57
|
+
abstract: paper.abstract?.slice(0, 300)
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
papers.sort((a, b) => b.citationCount - a.citationCount);
|
|
61
|
+
if (papers.length === 0) {
|
|
62
|
+
return `No ${direction} found for paper ${paper_id}.`;
|
|
63
|
+
}
|
|
64
|
+
const dirLabel = direction === "citations" ? "Cited by" : "References";
|
|
65
|
+
const lines = [`${dirLabel} (${papers.length} papers, sorted by citation count):
|
|
66
|
+
`];
|
|
67
|
+
for (let i = 0; i < papers.length; i++) {
|
|
68
|
+
const p = papers[i];
|
|
69
|
+
const meta = [];
|
|
70
|
+
if (p.year) meta.push(String(p.year));
|
|
71
|
+
if (p.venue) meta.push(p.venue);
|
|
72
|
+
meta.push(`${p.citationCount} citations`);
|
|
73
|
+
if (p.doi) meta.push(`DOI:${p.doi}`);
|
|
74
|
+
if (p.arxivId) meta.push(`arXiv:${p.arxivId}`);
|
|
75
|
+
lines.push(`${i + 1}. "${p.title}"`);
|
|
76
|
+
lines.push(` ${meta.join(" | ")}`);
|
|
77
|
+
lines.push(` ${p.url}`);
|
|
78
|
+
if (p.abstract) {
|
|
79
|
+
lines.push(` ${p.abstract}${p.abstract.length >= 300 ? "..." : ""}`);
|
|
80
|
+
}
|
|
81
|
+
lines.push("");
|
|
82
|
+
}
|
|
83
|
+
return lines.join("\n");
|
|
84
|
+
} catch (error) {
|
|
85
|
+
return `Error traversing citations: ${error instanceof Error ? error.message : String(error)}`;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export {
|
|
89
|
+
executeTraverseCitations
|
|
90
|
+
};
|
|
@@ -2,12 +2,16 @@ import {
|
|
|
2
2
|
USER_AGENT,
|
|
3
3
|
extractBatch,
|
|
4
4
|
fetchAndParseContent,
|
|
5
|
-
formatExtractionResults
|
|
5
|
+
formatExtractionResults
|
|
6
|
+
} from "./chunk-XOSPMXYH.js";
|
|
7
|
+
import {
|
|
6
8
|
loadOpenResearchConfig
|
|
7
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-CRSHN3PQ.js";
|
|
8
10
|
import "./chunk-77Q5B5H7.js";
|
|
9
11
|
import "./chunk-4HCPHCC2.js";
|
|
10
|
-
import
|
|
12
|
+
import {
|
|
13
|
+
getProviderCatalog
|
|
14
|
+
} from "./chunk-GVEVKDGV.js";
|
|
11
15
|
import "./chunk-3RG5ZIWI.js";
|
|
12
16
|
|
|
13
17
|
// src/lib/search/duckduckgo.ts
|
|
@@ -98,6 +102,48 @@ async function searchBrave(query, apiKey, numResults = 10) {
|
|
|
98
102
|
}
|
|
99
103
|
|
|
100
104
|
// src/lib/agent/tools/web-search.ts
|
|
105
|
+
var ADVERSARIAL_QUERY_PROMPT = `You generate search queries that find evidence AGAINST a research target. Your queries should surface:
|
|
106
|
+
- Direct contradictions or negative results
|
|
107
|
+
- Methodological limitations or criticisms
|
|
108
|
+
- Alternative explanations for the same phenomena
|
|
109
|
+
- Boundary conditions where the target claim fails
|
|
110
|
+
|
|
111
|
+
Do NOT rephrase the target as a question. Generate queries a skeptical reviewer would use to challenge the claim.`;
|
|
112
|
+
var ADVERSARIAL_QUERY_SCHEMA = {
|
|
113
|
+
name: "adversarial_queries",
|
|
114
|
+
schema: {
|
|
115
|
+
type: "object",
|
|
116
|
+
properties: {
|
|
117
|
+
queries: {
|
|
118
|
+
type: "array",
|
|
119
|
+
items: { type: "string" },
|
|
120
|
+
description: "1-2 search queries designed to find contradicting or limiting evidence"
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
required: ["queries"],
|
|
124
|
+
additionalProperties: false
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
async function generateAdversarialWebQueries(target, provider) {
|
|
128
|
+
try {
|
|
129
|
+
const backgroundModel = getProviderCatalog(provider.kind).backgroundModel;
|
|
130
|
+
const response = await provider.callLLM({
|
|
131
|
+
messages: [
|
|
132
|
+
{ role: "system", content: ADVERSARIAL_QUERY_PROMPT },
|
|
133
|
+
{ role: "user", content: target }
|
|
134
|
+
],
|
|
135
|
+
model: backgroundModel,
|
|
136
|
+
reasoningEffort: "low",
|
|
137
|
+
temperature: 0,
|
|
138
|
+
maxTokens: 150,
|
|
139
|
+
jsonSchema: ADVERSARIAL_QUERY_SCHEMA
|
|
140
|
+
});
|
|
141
|
+
const parsed = JSON.parse(response.content);
|
|
142
|
+
return Array.isArray(parsed.queries) ? parsed.queries.slice(0, 2) : [];
|
|
143
|
+
} catch {
|
|
144
|
+
return [];
|
|
145
|
+
}
|
|
146
|
+
}
|
|
101
147
|
async function discoverWebResults(query, numResults) {
|
|
102
148
|
const config = await loadOpenResearchConfig().catch(() => null);
|
|
103
149
|
const braveKey = config?.apiKeys?.brave;
|
|
@@ -117,21 +163,46 @@ async function discoverWebResults(query, numResults) {
|
|
|
117
163
|
};
|
|
118
164
|
}
|
|
119
165
|
async function executeWebSearch(args, provider) {
|
|
120
|
-
|
|
121
|
-
|
|
166
|
+
const userQueries = args.queries ?? (args.query ? [args.query] : []);
|
|
167
|
+
if (!args.target || userQueries.length === 0) {
|
|
168
|
+
return { result: "Error: both target and query/queries are required." };
|
|
122
169
|
}
|
|
123
170
|
const numResults = Math.min(args.num_results ?? 5, 8);
|
|
124
|
-
const
|
|
125
|
-
|
|
171
|
+
const adversarialPromise = provider ? generateAdversarialWebQueries(args.target, provider) : Promise.resolve([]);
|
|
172
|
+
const allSearchPromises = userQueries.map((q) => discoverWebResults(q, numResults));
|
|
173
|
+
const searchResultSets = await Promise.allSettled(allSearchPromises);
|
|
174
|
+
const seenUrls = /* @__PURE__ */ new Set();
|
|
175
|
+
const allHits = [];
|
|
176
|
+
for (const resultSet of searchResultSets) {
|
|
177
|
+
if (resultSet.status === "fulfilled") {
|
|
178
|
+
for (const hit of resultSet.value.results) {
|
|
179
|
+
if (!seenUrls.has(hit.url)) {
|
|
180
|
+
seenUrls.add(hit.url);
|
|
181
|
+
allHits.push({ ...hit, queryIntent: "primary" });
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const adversarialQueries = await adversarialPromise;
|
|
187
|
+
for (const aq of adversarialQueries) {
|
|
188
|
+
const { results } = await discoverWebResults(aq, 3);
|
|
189
|
+
for (const hit of results) {
|
|
190
|
+
if (!seenUrls.has(hit.url)) {
|
|
191
|
+
seenUrls.add(hit.url);
|
|
192
|
+
allHits.push({ ...hit, queryIntent: "adversarial" });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (allHits.length === 0) {
|
|
126
197
|
return { result: "No web results found. Try a different query." };
|
|
127
198
|
}
|
|
128
199
|
if (!provider) {
|
|
129
|
-
const summary =
|
|
200
|
+
const summary = allHits.slice(0, numResults).map((r, i) => `${i + 1}. ${r.title}
|
|
130
201
|
${r.url}
|
|
131
202
|
${r.snippet}`).join("\n\n");
|
|
132
|
-
return { result:
|
|
203
|
+
return { result: summary };
|
|
133
204
|
}
|
|
134
|
-
const toFetch =
|
|
205
|
+
const toFetch = allHits.slice(0, numResults + 3);
|
|
135
206
|
const contentResults = await Promise.allSettled(
|
|
136
207
|
toFetch.map(async (hit) => {
|
|
137
208
|
const content = await fetchAndParseContent(hit.url);
|
|
@@ -140,7 +211,7 @@ async function executeWebSearch(args, provider) {
|
|
|
140
211
|
})
|
|
141
212
|
);
|
|
142
213
|
const extractionInputs = [];
|
|
143
|
-
const
|
|
214
|
+
const hitMap = /* @__PURE__ */ new Map();
|
|
144
215
|
for (const result of contentResults) {
|
|
145
216
|
if (result.status === "fulfilled" && result.value) {
|
|
146
217
|
const { hit, text } = result.value;
|
|
@@ -150,7 +221,7 @@ async function executeWebSearch(args, provider) {
|
|
|
150
221
|
url: hit.url,
|
|
151
222
|
target: args.target
|
|
152
223
|
});
|
|
153
|
-
|
|
224
|
+
hitMap.set(hit.url, hit);
|
|
154
225
|
}
|
|
155
226
|
}
|
|
156
227
|
if (extractionInputs.length === 0) {
|
|
@@ -165,10 +236,12 @@ ${summary}` };
|
|
|
165
236
|
const extracted = [];
|
|
166
237
|
for (const [url, extraction] of extractions) {
|
|
167
238
|
if (extraction.relevanceScore >= 2) {
|
|
239
|
+
const hit = hitMap.get(url);
|
|
168
240
|
extracted.push({
|
|
169
|
-
title:
|
|
241
|
+
title: hit?.title ?? url,
|
|
170
242
|
url,
|
|
171
|
-
extraction
|
|
243
|
+
extraction,
|
|
244
|
+
queryIntent: hit?.queryIntent
|
|
172
245
|
});
|
|
173
246
|
}
|
|
174
247
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "open-research",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Local-first research CLI agent — discover papers, synthesize notes, run analysis, and draft artifacts from your terminal.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -37,10 +37,12 @@
|
|
|
37
37
|
"semantic-scholar"
|
|
38
38
|
],
|
|
39
39
|
"dependencies": {
|
|
40
|
+
"@hono/node-server": "^1.19.13",
|
|
40
41
|
"cheerio": "^1.1.2",
|
|
41
42
|
"commander": "^14.0.1",
|
|
42
43
|
"diff": "^8.0.4",
|
|
43
44
|
"gray-matter": "^4.0.3",
|
|
45
|
+
"hono": "^4.12.12",
|
|
44
46
|
"ink": "^6.3.1",
|
|
45
47
|
"ink-text-input": "^6.0.0",
|
|
46
48
|
"open": "^10.2.0",
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
loadOntology,
|
|
3
|
-
saveOntology
|
|
4
|
-
} from "./chunk-3WM33M3O.js";
|
|
5
1
|
import {
|
|
6
2
|
DEFAULT_CONFIDENCE
|
|
7
3
|
} from "./chunk-KJHM7ZW2.js";
|
|
8
4
|
import {
|
|
9
5
|
getProviderCatalog
|
|
10
6
|
} from "./chunk-GVEVKDGV.js";
|
|
7
|
+
import {
|
|
8
|
+
loadOntology,
|
|
9
|
+
saveOntology
|
|
10
|
+
} from "./chunk-3WM33M3O.js";
|
|
11
11
|
import {
|
|
12
12
|
getConnections,
|
|
13
13
|
getNote,
|