open-research 1.1.0 → 1.1.2
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/README.md
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
<h3 align="center">The research-native CLI agent.</h3>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
+
<a href="https://open-research.info">open-research.info</a> ·
|
|
8
9
|
<a href="https://www.npmjs.com/package/open-research"><img src="https://img.shields.io/npm/v/open-research.svg" alt="npm" /></a>
|
|
9
10
|
<a href="https://github.com/gangj277/open-research/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/open-research.svg" alt="license" /></a>
|
|
10
11
|
</p>
|
|
@@ -227,10 +227,14 @@ async function executeFetchUrl(args, signal) {
|
|
|
227
227
|
|
|
228
228
|
// src/lib/fs/pdf.ts
|
|
229
229
|
import fs from "fs/promises";
|
|
230
|
+
import path from "path";
|
|
231
|
+
import { createRequire } from "module";
|
|
232
|
+
var require2 = createRequire(import.meta.url);
|
|
233
|
+
var standardFontDataUrl = path.join(path.dirname(require2.resolve("pdfjs-dist/package.json")), "standard_fonts") + "/";
|
|
230
234
|
async function extractPdfText(filePath, options) {
|
|
231
235
|
const pdfjs = await import("pdfjs-dist/legacy/build/pdf.mjs");
|
|
232
236
|
const buffer = await fs.readFile(filePath);
|
|
233
|
-
const document = await pdfjs.getDocument({ data: new Uint8Array(buffer) }).promise;
|
|
237
|
+
const document = await pdfjs.getDocument({ data: new Uint8Array(buffer), standardFontDataUrl }).promise;
|
|
234
238
|
const totalPages = document.numPages;
|
|
235
239
|
const start = Math.max(1, options?.startPage ?? 1);
|
|
236
240
|
const end = Math.min(totalPages, options?.endPage ?? totalPages);
|
|
@@ -245,7 +249,7 @@ async function extractPdfText(filePath, options) {
|
|
|
245
249
|
}
|
|
246
250
|
async function extractPdfTextFromBuffer(buffer, options) {
|
|
247
251
|
const pdfjs = await import("pdfjs-dist/legacy/build/pdf.mjs");
|
|
248
|
-
const document = await pdfjs.getDocument({ data: buffer }).promise;
|
|
252
|
+
const document = await pdfjs.getDocument({ data: buffer, standardFontDataUrl }).promise;
|
|
249
253
|
const totalPages = document.numPages;
|
|
250
254
|
const end = Math.min(totalPages, options?.maxPages ?? 20);
|
|
251
255
|
const pages = [];
|
package/dist/cli.js
CHANGED
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
loadOpenResearchConfig,
|
|
27
27
|
saveOpenResearchConfig,
|
|
28
28
|
themeValues
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-TJA4CAZE.js";
|
|
30
30
|
import {
|
|
31
31
|
readJsonFile,
|
|
32
32
|
writeJsonFile
|
|
@@ -828,7 +828,7 @@ function formatDateTime(value) {
|
|
|
828
828
|
}
|
|
829
829
|
|
|
830
830
|
// src/lib/cli/version.ts
|
|
831
|
-
var PACKAGE_VERSION = "1.1.
|
|
831
|
+
var PACKAGE_VERSION = "1.1.2";
|
|
832
832
|
function getPackageVersion() {
|
|
833
833
|
return PACKAGE_VERSION;
|
|
834
834
|
}
|
|
@@ -5927,11 +5927,11 @@ async function discoverScholarlySources({
|
|
|
5927
5927
|
const openAlexSearch = clients.searchOpenAlexWorks ?? searchOpenAlexWorks;
|
|
5928
5928
|
const semanticScholarSearch = clients.searchSemanticScholarPapers ?? searchSemanticScholarPapers;
|
|
5929
5929
|
const arxivSearch = clients.searchArxivPapers ?? searchArxivPapers;
|
|
5930
|
-
const
|
|
5930
|
+
const tasks = [];
|
|
5931
5931
|
if (openAlexApiKey?.trim()) {
|
|
5932
5932
|
for (let qi = 0; qi < searchQueries.length; qi++) {
|
|
5933
5933
|
const qIdx = qi;
|
|
5934
|
-
|
|
5934
|
+
tasks.push(
|
|
5935
5935
|
openAlexSearch({
|
|
5936
5936
|
query: searchQueries[qIdx],
|
|
5937
5937
|
apiKey: openAlexApiKey.trim(),
|
|
@@ -5950,7 +5950,7 @@ async function discoverScholarlySources({
|
|
|
5950
5950
|
}
|
|
5951
5951
|
for (let qi = 0; qi < searchQueries.length; qi++) {
|
|
5952
5952
|
const qIdx = qi;
|
|
5953
|
-
|
|
5953
|
+
tasks.push(
|
|
5954
5954
|
semanticScholarSearch({
|
|
5955
5955
|
query: searchQueries[qIdx],
|
|
5956
5956
|
apiKey: semanticScholarApiKey?.trim(),
|
|
@@ -5975,7 +5975,7 @@ async function discoverScholarlySources({
|
|
|
5975
5975
|
} : void 0;
|
|
5976
5976
|
for (let qi = 0; qi < searchQueries.length; qi++) {
|
|
5977
5977
|
const qIdx = qi;
|
|
5978
|
-
|
|
5978
|
+
tasks.push(
|
|
5979
5979
|
arxivSearch({
|
|
5980
5980
|
query: searchQueries[qIdx],
|
|
5981
5981
|
maxResults: perQueryResults,
|
|
@@ -5988,7 +5988,7 @@ async function discoverScholarlySources({
|
|
|
5988
5988
|
);
|
|
5989
5989
|
}
|
|
5990
5990
|
}
|
|
5991
|
-
const settled = await Promise.allSettled(
|
|
5991
|
+
const settled = await Promise.allSettled(tasks);
|
|
5992
5992
|
const candidates = settled.flatMap(
|
|
5993
5993
|
(result) => result.status === "fulfilled" ? result.value : []
|
|
5994
5994
|
);
|
|
@@ -6310,59 +6310,100 @@ function describeSubAgentTool(name, args) {
|
|
|
6310
6310
|
// src/lib/agent/tools/tasks.ts
|
|
6311
6311
|
import path11 from "path";
|
|
6312
6312
|
import fs11 from "fs/promises";
|
|
6313
|
-
var
|
|
6313
|
+
var currentTasks = [];
|
|
6314
6314
|
var storePath = null;
|
|
6315
|
+
var currentSessionId = null;
|
|
6316
|
+
var pendingWrite = Promise.resolve();
|
|
6315
6317
|
function shortId() {
|
|
6316
6318
|
return crypto.randomUUID().replace(/-/g, "").slice(0, 8);
|
|
6317
6319
|
}
|
|
6318
|
-
|
|
6319
|
-
|
|
6320
|
-
const live = tasks.filter((t) => t.status !== "deleted");
|
|
6321
|
-
const tmpPath = storePath + ".tmp";
|
|
6322
|
-
await fs11.mkdir(path11.dirname(storePath), { recursive: true });
|
|
6323
|
-
await fs11.writeFile(tmpPath, JSON.stringify({ version: 1, tasks: live }, null, 2));
|
|
6324
|
-
await fs11.rename(tmpPath, storePath);
|
|
6320
|
+
function cloneTasks(tasks) {
|
|
6321
|
+
return tasks.map((task) => ({ ...task }));
|
|
6325
6322
|
}
|
|
6326
|
-
async function
|
|
6323
|
+
async function persistSnapshot(snapshot) {
|
|
6324
|
+
const data = await readJsonFile(snapshot.storePath, { version: 1, tasks: [] });
|
|
6325
|
+
const foreignTasks = (data.tasks ?? []).filter(
|
|
6326
|
+
(task) => task.status !== "deleted" && task.sessionId !== snapshot.sessionId
|
|
6327
|
+
);
|
|
6328
|
+
const liveSessionTasks = snapshot.tasks.filter((task) => task.status !== "deleted");
|
|
6329
|
+
const mergedTasks = [...foreignTasks, ...liveSessionTasks];
|
|
6330
|
+
const tmpPath = snapshot.storePath + ".tmp";
|
|
6331
|
+
await fs11.mkdir(path11.dirname(snapshot.storePath), { recursive: true });
|
|
6332
|
+
await fs11.writeFile(
|
|
6333
|
+
tmpPath,
|
|
6334
|
+
JSON.stringify({ version: 1, tasks: mergedTasks }, null, 2)
|
|
6335
|
+
);
|
|
6336
|
+
await fs11.rename(tmpPath, snapshot.storePath);
|
|
6337
|
+
}
|
|
6338
|
+
function schedulePersist() {
|
|
6339
|
+
if (!storePath || !currentSessionId) return pendingWrite;
|
|
6340
|
+
const snapshot = {
|
|
6341
|
+
storePath,
|
|
6342
|
+
sessionId: currentSessionId,
|
|
6343
|
+
tasks: cloneTasks(currentTasks)
|
|
6344
|
+
};
|
|
6345
|
+
pendingWrite = pendingWrite.catch(() => void 0).then(() => persistSnapshot(snapshot));
|
|
6346
|
+
void pendingWrite.catch(() => void 0);
|
|
6347
|
+
return pendingWrite;
|
|
6348
|
+
}
|
|
6349
|
+
function ensureSessionId() {
|
|
6350
|
+
if (!currentSessionId) {
|
|
6351
|
+
currentSessionId = crypto.randomUUID();
|
|
6352
|
+
}
|
|
6353
|
+
return currentSessionId;
|
|
6354
|
+
}
|
|
6355
|
+
async function initTaskStore(workspaceDir, sessionId2 = crypto.randomUUID()) {
|
|
6327
6356
|
storePath = path11.join(workspaceDir, ".open-research", "tasks.json");
|
|
6328
6357
|
const data = await readJsonFile(storePath, { version: 1, tasks: [] });
|
|
6329
|
-
|
|
6358
|
+
currentSessionId = sessionId2;
|
|
6359
|
+
currentTasks = (data.tasks ?? []).filter(
|
|
6360
|
+
(task) => task.status !== "deleted" && task.sessionId === currentSessionId
|
|
6361
|
+
);
|
|
6330
6362
|
}
|
|
6331
6363
|
function clearAllTasks() {
|
|
6332
|
-
|
|
6333
|
-
void
|
|
6364
|
+
currentTasks = [];
|
|
6365
|
+
void schedulePersist();
|
|
6334
6366
|
}
|
|
6335
6367
|
function executeCreateTasks(args) {
|
|
6368
|
+
const sessionId2 = ensureSessionId();
|
|
6336
6369
|
const created = [];
|
|
6337
6370
|
for (const item of args.tasks) {
|
|
6338
6371
|
const task = {
|
|
6339
6372
|
id: shortId(),
|
|
6373
|
+
sessionId: sessionId2,
|
|
6340
6374
|
subject: item.subject,
|
|
6341
6375
|
activeForm: item.activeForm,
|
|
6342
6376
|
status: "pending",
|
|
6343
6377
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6344
6378
|
};
|
|
6345
|
-
|
|
6379
|
+
currentTasks.push(task);
|
|
6346
6380
|
created.push(task);
|
|
6347
6381
|
}
|
|
6348
|
-
void
|
|
6382
|
+
void schedulePersist();
|
|
6349
6383
|
return created.map((t) => `[${t.id}] ${t.subject}`).join("\n");
|
|
6350
6384
|
}
|
|
6351
6385
|
function executeUpdateTask(args) {
|
|
6352
|
-
const task =
|
|
6386
|
+
const task = currentTasks.find((t) => t.id === args.taskId);
|
|
6353
6387
|
if (!task) return `Task not found: ${args.taskId}`;
|
|
6354
6388
|
if (args.status) {
|
|
6355
6389
|
task.status = args.status;
|
|
6356
|
-
if (args.status === "completed")
|
|
6390
|
+
if (args.status === "completed") {
|
|
6391
|
+
task.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6392
|
+
} else {
|
|
6393
|
+
delete task.completedAt;
|
|
6394
|
+
}
|
|
6357
6395
|
}
|
|
6358
6396
|
if (args.subject !== void 0) task.subject = args.subject;
|
|
6359
6397
|
if (args.activeForm !== void 0) task.activeForm = args.activeForm;
|
|
6360
|
-
|
|
6398
|
+
if (task.status === "deleted") {
|
|
6399
|
+
currentTasks = currentTasks.filter((candidate) => candidate.id !== task.id);
|
|
6400
|
+
}
|
|
6401
|
+
void schedulePersist();
|
|
6361
6402
|
const label = task.status === "deleted" ? "deleted" : `${task.subject} \u2192 ${task.status}`;
|
|
6362
6403
|
return `Task updated: ${label}`;
|
|
6363
6404
|
}
|
|
6364
6405
|
function getTaskContextBlock() {
|
|
6365
|
-
const live =
|
|
6406
|
+
const live = currentTasks.filter((t) => t.status !== "deleted");
|
|
6366
6407
|
if (live.length === 0) return null;
|
|
6367
6408
|
const lines = live.map((t) => {
|
|
6368
6409
|
if (t.status === "completed") return `[x] ${t.subject}`;
|
|
@@ -6373,7 +6414,7 @@ function getTaskContextBlock() {
|
|
|
6373
6414
|
${lines.join("\n")}`;
|
|
6374
6415
|
}
|
|
6375
6416
|
function getVisibleTasks() {
|
|
6376
|
-
return
|
|
6417
|
+
return currentTasks.filter((t) => t.status !== "deleted");
|
|
6377
6418
|
}
|
|
6378
6419
|
|
|
6379
6420
|
// src/lib/agent/tool-dispatcher.ts
|
|
@@ -6453,7 +6494,7 @@ async function executeTool(name, args, ctx, activeSkills, homeDir, signal, provi
|
|
|
6453
6494
|
return { result: out.result, searchResults: out.sources };
|
|
6454
6495
|
}
|
|
6455
6496
|
case "web_search": {
|
|
6456
|
-
const { executeWebSearch } = await import("./web-search-
|
|
6497
|
+
const { executeWebSearch } = await import("./web-search-IBZ6UAXL.js");
|
|
6457
6498
|
const out = await executeWebSearch(
|
|
6458
6499
|
args,
|
|
6459
6500
|
provider
|
|
@@ -7269,8 +7310,8 @@ var TOOL_DESCRIPTIONS = {
|
|
|
7269
7310
|
return `Sub-agent (${type ?? "explore"}): ${goal?.slice(0, 60) ?? "task"}`;
|
|
7270
7311
|
},
|
|
7271
7312
|
create_tasks: (a) => {
|
|
7272
|
-
const
|
|
7273
|
-
return `Creating ${
|
|
7313
|
+
const tasks = a.tasks;
|
|
7314
|
+
return `Creating ${tasks?.length ?? 0} task${(tasks?.length ?? 0) !== 1 ? "s" : ""}`;
|
|
7274
7315
|
},
|
|
7275
7316
|
update_task: (a) => {
|
|
7276
7317
|
const status = a.status;
|
|
@@ -8341,15 +8382,15 @@ function ThinkingIndicator({ frame, width }) {
|
|
|
8341
8382
|
] });
|
|
8342
8383
|
}
|
|
8343
8384
|
function TaskPanel({
|
|
8344
|
-
tasks
|
|
8385
|
+
tasks,
|
|
8345
8386
|
frame,
|
|
8346
8387
|
width
|
|
8347
8388
|
}) {
|
|
8348
8389
|
const theme = useTheme();
|
|
8349
8390
|
const contentWidth = resolveWidth(width);
|
|
8350
|
-
const completed =
|
|
8351
|
-
const active =
|
|
8352
|
-
if (
|
|
8391
|
+
const completed = tasks.filter((t) => t.status === "completed");
|
|
8392
|
+
const active = tasks.filter((t) => t.status !== "completed");
|
|
8393
|
+
if (tasks.length === 0) return null;
|
|
8353
8394
|
return /* @__PURE__ */ jsxs3(Box4, { flexDirection: "column", marginBottom: 1, marginLeft: 2, width: indentedWidth(contentWidth), children: [
|
|
8354
8395
|
active.map((task) => {
|
|
8355
8396
|
const icon = task.status === "in_progress" ? frame : GUTTER.pending;
|
|
@@ -9090,26 +9131,127 @@ function createSentenceStreamBuffer({
|
|
|
9090
9131
|
|
|
9091
9132
|
// src/tui/hooks/use-animated-frame.ts
|
|
9092
9133
|
import { useState as useState5, useEffect as useEffect2 } from "react";
|
|
9134
|
+
import { useStdout as useStdout2 } from "ink";
|
|
9135
|
+
|
|
9136
|
+
// src/tui/ink-stdout.ts
|
|
9137
|
+
var RAW_COLUMNS = /* @__PURE__ */ Symbol("open-research.raw-columns");
|
|
9138
|
+
var RAW_ROWS = /* @__PURE__ */ Symbol("open-research.raw-rows");
|
|
9139
|
+
function getStableRow(current, fallback) {
|
|
9140
|
+
return typeof current === "number" && current > 0 ? current : fallback;
|
|
9141
|
+
}
|
|
9142
|
+
function getStableColumn(current, fallback) {
|
|
9143
|
+
return typeof current === "number" && current >= MIN_TERMINAL_WIDTH ? current : fallback;
|
|
9144
|
+
}
|
|
9145
|
+
function getRawDimension(current) {
|
|
9146
|
+
return typeof current === "number" && Number.isFinite(current) ? current : void 0;
|
|
9147
|
+
}
|
|
9148
|
+
function getRawTerminalDimensions(stdout) {
|
|
9149
|
+
const stream = stdout;
|
|
9150
|
+
return {
|
|
9151
|
+
columns: getRawDimension(stream[RAW_COLUMNS] ?? stream.columns),
|
|
9152
|
+
rows: getRawDimension(stream[RAW_ROWS] ?? stream.rows)
|
|
9153
|
+
};
|
|
9154
|
+
}
|
|
9155
|
+
function hasRenderableTerminalDimensions(stdout) {
|
|
9156
|
+
const { columns, rows } = getRawTerminalDimensions(stdout);
|
|
9157
|
+
const hasRows = rows === void 0 || rows > 0;
|
|
9158
|
+
return hasRows && typeof columns === "number" && columns >= MIN_TERMINAL_WIDTH;
|
|
9159
|
+
}
|
|
9160
|
+
function createStableInkStdout(stdout, options) {
|
|
9161
|
+
const forceFullRedraw = options?.forceFullRedraw ?? process.env.OPEN_RESEARCH_FORCE_FULL_REDRAW === "1";
|
|
9162
|
+
const initialRows = getRawDimension(stdout.rows);
|
|
9163
|
+
const initialColumns = getRawDimension(stdout.columns);
|
|
9164
|
+
let lastRows = getStableRow(initialRows, 24);
|
|
9165
|
+
let lastColumns = typeof initialColumns === "number" && initialColumns > 0 ? Math.max(MIN_TERMINAL_WIDTH, initialColumns) : 80;
|
|
9166
|
+
const resizeListeners = /* @__PURE__ */ new Map();
|
|
9167
|
+
return new Proxy(stdout, {
|
|
9168
|
+
get(target, prop, receiver) {
|
|
9169
|
+
if (prop === RAW_ROWS) {
|
|
9170
|
+
return getRawDimension(Reflect.get(target, "rows", receiver));
|
|
9171
|
+
}
|
|
9172
|
+
if (prop === RAW_COLUMNS) {
|
|
9173
|
+
return getRawDimension(Reflect.get(target, "columns", receiver));
|
|
9174
|
+
}
|
|
9175
|
+
if (prop === "rows") {
|
|
9176
|
+
if (forceFullRedraw) {
|
|
9177
|
+
return 0;
|
|
9178
|
+
}
|
|
9179
|
+
const rows = Reflect.get(target, prop, receiver);
|
|
9180
|
+
lastRows = getStableRow(rows, lastRows);
|
|
9181
|
+
return lastRows;
|
|
9182
|
+
}
|
|
9183
|
+
if (prop === "columns") {
|
|
9184
|
+
const columns = Reflect.get(target, prop, receiver);
|
|
9185
|
+
lastColumns = getStableColumn(columns, lastColumns);
|
|
9186
|
+
return lastColumns;
|
|
9187
|
+
}
|
|
9188
|
+
if (prop === "on" || prop === "addListener" || prop === "once" || prop === "prependListener") {
|
|
9189
|
+
const method = prop;
|
|
9190
|
+
return (eventName, listener) => {
|
|
9191
|
+
if (eventName === "resize" && !forceFullRedraw) {
|
|
9192
|
+
const wrapped = (...args) => {
|
|
9193
|
+
const rawColumns = getRawDimension(Reflect.get(target, "columns", receiver));
|
|
9194
|
+
const rawRows = getRawDimension(Reflect.get(target, "rows", receiver));
|
|
9195
|
+
lastColumns = getStableColumn(rawColumns, lastColumns);
|
|
9196
|
+
lastRows = getStableRow(rawRows, lastRows);
|
|
9197
|
+
if (!hasRenderableTerminalDimensions({
|
|
9198
|
+
[RAW_COLUMNS]: rawColumns,
|
|
9199
|
+
[RAW_ROWS]: rawRows
|
|
9200
|
+
})) {
|
|
9201
|
+
return;
|
|
9202
|
+
}
|
|
9203
|
+
listener(...args);
|
|
9204
|
+
};
|
|
9205
|
+
resizeListeners.set(listener, wrapped);
|
|
9206
|
+
Reflect.get(target, method, receiver).call(target, eventName, wrapped);
|
|
9207
|
+
return receiver;
|
|
9208
|
+
}
|
|
9209
|
+
Reflect.get(target, method, receiver).call(target, eventName, listener);
|
|
9210
|
+
return receiver;
|
|
9211
|
+
};
|
|
9212
|
+
}
|
|
9213
|
+
if (prop === "off" || prop === "removeListener") {
|
|
9214
|
+
const method = prop;
|
|
9215
|
+
return (eventName, listener) => {
|
|
9216
|
+
const wrapped = eventName === "resize" ? resizeListeners.get(listener) ?? listener : listener;
|
|
9217
|
+
resizeListeners.delete(listener);
|
|
9218
|
+
Reflect.get(target, method, receiver).call(target, eventName, wrapped);
|
|
9219
|
+
return receiver;
|
|
9220
|
+
};
|
|
9221
|
+
}
|
|
9222
|
+
const value = Reflect.get(target, prop, receiver);
|
|
9223
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
9224
|
+
}
|
|
9225
|
+
});
|
|
9226
|
+
}
|
|
9227
|
+
|
|
9228
|
+
// src/tui/hooks/use-animated-frame.ts
|
|
9093
9229
|
var SPINNER_FRAMES = ["\u25D0", "\u25D3", "\u25D1", "\u25D2"];
|
|
9094
9230
|
function useAnimatedFrame(active) {
|
|
9231
|
+
const { stdout } = useStdout2();
|
|
9095
9232
|
const [index, setIndex] = useState5(0);
|
|
9096
9233
|
useEffect2(() => {
|
|
9097
9234
|
if (!active) {
|
|
9098
9235
|
setIndex(0);
|
|
9099
9236
|
return;
|
|
9100
9237
|
}
|
|
9101
|
-
const timer = setInterval(() =>
|
|
9238
|
+
const timer = setInterval(() => {
|
|
9239
|
+
if (!hasRenderableTerminalDimensions(stdout)) {
|
|
9240
|
+
return;
|
|
9241
|
+
}
|
|
9242
|
+
setIndex((v) => (v + 1) % SPINNER_FRAMES.length);
|
|
9243
|
+
}, 120);
|
|
9102
9244
|
return () => clearInterval(timer);
|
|
9103
|
-
}, [active]);
|
|
9245
|
+
}, [active, stdout]);
|
|
9104
9246
|
return SPINNER_FRAMES[index] ?? SPINNER_FRAMES[0];
|
|
9105
9247
|
}
|
|
9106
9248
|
|
|
9107
9249
|
// src/tui/hooks/use-terminal-width.ts
|
|
9108
9250
|
import { useState as useState6, useEffect as useEffect3 } from "react";
|
|
9109
|
-
import { useStdout as
|
|
9251
|
+
import { useStdout as useStdout3 } from "ink";
|
|
9110
9252
|
var RESIZE_DEBOUNCE_MS = 50;
|
|
9111
9253
|
function useTerminalWidth() {
|
|
9112
|
-
const { stdout } =
|
|
9254
|
+
const { stdout } = useStdout3();
|
|
9113
9255
|
const [terminalWidth, setTerminalWidth] = useState6(
|
|
9114
9256
|
() => getObservedTerminalWidth(stdout.columns, process.stdout.columns)
|
|
9115
9257
|
);
|
|
@@ -10380,6 +10522,8 @@ function App({
|
|
|
10380
10522
|
const [statusLine, setStatusLine] = useState7("");
|
|
10381
10523
|
const [activeToolActivities, setActiveToolActivities] = useState7({});
|
|
10382
10524
|
const [turnToolCount, setTurnToolCount] = useState7(0);
|
|
10525
|
+
const [latchedToolActivity, setLatchedToolActivity] = useState7("");
|
|
10526
|
+
const [latchedToolCount, setLatchedToolCount] = useState7(0);
|
|
10383
10527
|
const [subAgentProgress, setSubAgentProgress] = useState7({});
|
|
10384
10528
|
const [toolActivityExpanded, setToolActivityExpanded] = useState7(false);
|
|
10385
10529
|
const [taskPanelVisible, setTaskPanelVisible] = useState7(true);
|
|
@@ -10400,7 +10544,7 @@ function App({
|
|
|
10400
10544
|
const [screen, setScreen] = useState7("main");
|
|
10401
10545
|
const [resumeSessions, setResumeSessions] = useState7([]);
|
|
10402
10546
|
const [ctrlCPending, setCtrlCPending] = useState7(false);
|
|
10403
|
-
const sessionId2 =
|
|
10547
|
+
const [sessionId2, setSessionId] = useState7(() => crypto.randomUUID());
|
|
10404
10548
|
const deferredPendingUpdates = useDeferredValue(pendingUpdates);
|
|
10405
10549
|
const visiblePendingUpdates = deferredPendingUpdates.length > 0 ? deferredPendingUpdates : pendingUpdates;
|
|
10406
10550
|
const activityFrame = useAnimatedFrame(busy);
|
|
@@ -10441,6 +10585,18 @@ function App({
|
|
|
10441
10585
|
}
|
|
10442
10586
|
return `Running ${activeToolDescriptions.length} tools in parallel`;
|
|
10443
10587
|
}, [activeToolDescriptions, turnToolCount]);
|
|
10588
|
+
useEffect4(() => {
|
|
10589
|
+
if (!busy) {
|
|
10590
|
+
setLatchedToolActivity("");
|
|
10591
|
+
setLatchedToolCount(0);
|
|
10592
|
+
return;
|
|
10593
|
+
}
|
|
10594
|
+
if (!currentToolActivity) {
|
|
10595
|
+
return;
|
|
10596
|
+
}
|
|
10597
|
+
setLatchedToolActivity(currentToolActivity);
|
|
10598
|
+
setLatchedToolCount(turnToolCount);
|
|
10599
|
+
}, [busy, currentToolActivity, turnToolCount]);
|
|
10444
10600
|
const visibleSubAgents = useMemo4(
|
|
10445
10601
|
() => Object.values(subAgentProgress),
|
|
10446
10602
|
[subAgentProgress]
|
|
@@ -10485,13 +10641,13 @@ function App({
|
|
|
10485
10641
|
if (cancelled) return;
|
|
10486
10642
|
startTransition2(() => setWorkspaceFiles(result.files));
|
|
10487
10643
|
});
|
|
10488
|
-
void initTaskStore(workspacePath).then(() => {
|
|
10644
|
+
void initTaskStore(workspacePath, sessionId2).then(() => {
|
|
10489
10645
|
if (!cancelled) setTaskVersion((v) => v + 1);
|
|
10490
10646
|
});
|
|
10491
10647
|
return () => {
|
|
10492
10648
|
cancelled = true;
|
|
10493
10649
|
};
|
|
10494
|
-
}, [workspacePath]);
|
|
10650
|
+
}, [workspacePath, sessionId2]);
|
|
10495
10651
|
useEffect4(() => {
|
|
10496
10652
|
let cancelled = false;
|
|
10497
10653
|
void listAvailableSkills({ homeDir }).then((available) => {
|
|
@@ -10747,7 +10903,6 @@ function App({
|
|
|
10747
10903
|
clearCtrlCPending();
|
|
10748
10904
|
if (abortRef.current) {
|
|
10749
10905
|
abortRef.current.abort();
|
|
10750
|
-
addSystemMessage("Interrupting agent...");
|
|
10751
10906
|
}
|
|
10752
10907
|
return;
|
|
10753
10908
|
}
|
|
@@ -10793,7 +10948,6 @@ function App({
|
|
|
10793
10948
|
if (inputKey.escape) {
|
|
10794
10949
|
if (busy && abortRef.current) {
|
|
10795
10950
|
abortRef.current.abort();
|
|
10796
|
-
addSystemMessage("Interrupting agent...");
|
|
10797
10951
|
} else if (planningState.status === "charter-review") {
|
|
10798
10952
|
rejectCharter();
|
|
10799
10953
|
} else {
|
|
@@ -10871,11 +11025,14 @@ function App({
|
|
|
10871
11025
|
if (!workspacePath) return;
|
|
10872
11026
|
turnToolLogRef.current = [];
|
|
10873
11027
|
setTurnToolCount(0);
|
|
11028
|
+
setLatchedToolActivity("");
|
|
11029
|
+
setLatchedToolCount(0);
|
|
10874
11030
|
setActiveToolActivities({});
|
|
10875
11031
|
setSubAgentProgress({});
|
|
10876
11032
|
const controller = new AbortController();
|
|
10877
11033
|
let streamBuffer = null;
|
|
10878
11034
|
let focusPendingReviewOnComplete = false;
|
|
11035
|
+
const postTurnNotices = [];
|
|
10879
11036
|
abortRef.current = controller;
|
|
10880
11037
|
setBusy(true);
|
|
10881
11038
|
startTransition2(() => {
|
|
@@ -10911,6 +11068,8 @@ function App({
|
|
|
10911
11068
|
onTextDelta: (chunk) => {
|
|
10912
11069
|
assistantText += chunk;
|
|
10913
11070
|
streamBuffer?.push(chunk);
|
|
11071
|
+
setLatchedToolActivity("");
|
|
11072
|
+
setLatchedToolCount(0);
|
|
10914
11073
|
},
|
|
10915
11074
|
onToolActivity: (activity) => {
|
|
10916
11075
|
streamBuffer?.flush();
|
|
@@ -10969,7 +11128,9 @@ function App({
|
|
|
10969
11128
|
streamBuffer.flush();
|
|
10970
11129
|
if (turnToolLogRef.current.length > 0) {
|
|
10971
11130
|
const summary = buildToolSummary(turnToolLogRef.current);
|
|
10972
|
-
|
|
11131
|
+
postTurnNotices.push(
|
|
11132
|
+
`__tool_summary__${JSON.stringify({ summary, tools: turnToolLogRef.current })}`
|
|
11133
|
+
);
|
|
10973
11134
|
turnToolLogRef.current = [];
|
|
10974
11135
|
}
|
|
10975
11136
|
startTransition2(() => {
|
|
@@ -11032,7 +11193,13 @@ ${error.stack}` : String(error)}` }
|
|
|
11032
11193
|
setBusy(false);
|
|
11033
11194
|
setComposerFocused(!focusPendingReviewOnComplete);
|
|
11034
11195
|
if (controller.signal.aborted) {
|
|
11035
|
-
|
|
11196
|
+
postTurnNotices.push("Interrupting agent...\nAgent interrupted.");
|
|
11197
|
+
}
|
|
11198
|
+
for (const notice of postTurnNotices) {
|
|
11199
|
+
addSystemMessage(notice);
|
|
11200
|
+
}
|
|
11201
|
+
if (controller.signal.aborted) {
|
|
11202
|
+
return;
|
|
11036
11203
|
}
|
|
11037
11204
|
}
|
|
11038
11205
|
}
|
|
@@ -11174,6 +11341,9 @@ ${error.stack}` : String(error)}` }
|
|
|
11174
11341
|
onSelect: async (session) => {
|
|
11175
11342
|
try {
|
|
11176
11343
|
const restored = await loadSessionHistory(workspacePath, session.id);
|
|
11344
|
+
setSessionId(session.id);
|
|
11345
|
+
await initTaskStore(workspacePath, session.id);
|
|
11346
|
+
setTaskVersion((version) => version + 1);
|
|
11177
11347
|
replaceMessages(restored.messages);
|
|
11178
11348
|
startTransition2(() => setHistory(restored.llmHistory));
|
|
11179
11349
|
addSystemMessage(`Resumed session (${session.turnCount} turns). Continue where you left off.`);
|
|
@@ -11351,8 +11521,8 @@ ${error.stack}` : String(error)}` }
|
|
|
11351
11521
|
width: contentWidth,
|
|
11352
11522
|
busy,
|
|
11353
11523
|
frame: activityFrame,
|
|
11354
|
-
toolActivity: currentToolActivity,
|
|
11355
|
-
toolCount: turnToolCount,
|
|
11524
|
+
toolActivity: currentToolActivity || latchedToolActivity,
|
|
11525
|
+
toolCount: currentToolActivity ? turnToolCount : latchedToolCount,
|
|
11356
11526
|
statusParts,
|
|
11357
11527
|
statusColor,
|
|
11358
11528
|
tokenDisplay,
|
|
@@ -11364,35 +11534,6 @@ ${error.stack}` : String(error)}` }
|
|
|
11364
11534
|
] }) });
|
|
11365
11535
|
}
|
|
11366
11536
|
|
|
11367
|
-
// src/tui/ink-stdout.ts
|
|
11368
|
-
function getStableDimension(current, fallback) {
|
|
11369
|
-
return typeof current === "number" && current > 0 ? current : fallback;
|
|
11370
|
-
}
|
|
11371
|
-
function createStableInkStdout(stdout, options) {
|
|
11372
|
-
const forceFullRedraw = options?.forceFullRedraw ?? process.env.OPEN_RESEARCH_FORCE_FULL_REDRAW === "1";
|
|
11373
|
-
let lastRows = getStableDimension(stdout.rows, 24);
|
|
11374
|
-
let lastColumns = getStableDimension(stdout.columns, 80);
|
|
11375
|
-
return new Proxy(stdout, {
|
|
11376
|
-
get(target, prop, receiver) {
|
|
11377
|
-
if (prop === "rows") {
|
|
11378
|
-
if (forceFullRedraw) {
|
|
11379
|
-
return 0;
|
|
11380
|
-
}
|
|
11381
|
-
const rows = Reflect.get(target, prop, receiver);
|
|
11382
|
-
lastRows = getStableDimension(rows, lastRows);
|
|
11383
|
-
return lastRows;
|
|
11384
|
-
}
|
|
11385
|
-
if (prop === "columns") {
|
|
11386
|
-
const columns = Reflect.get(target, prop, receiver);
|
|
11387
|
-
lastColumns = getStableDimension(columns, lastColumns);
|
|
11388
|
-
return lastColumns;
|
|
11389
|
-
}
|
|
11390
|
-
const value = Reflect.get(target, prop, receiver);
|
|
11391
|
-
return typeof value === "function" ? value.bind(target) : value;
|
|
11392
|
-
}
|
|
11393
|
-
});
|
|
11394
|
-
}
|
|
11395
|
-
|
|
11396
11537
|
// src/cli.ts
|
|
11397
11538
|
var program = new Command();
|
|
11398
11539
|
program.name("open-research").version(getPackageVersion()).description("Local-first research CLI powered by OpenAI account auth or API keys.").argument("[workspacePath]", "Optional workspace path to open").action(async (workspacePath) => {
|
package/package.json
CHANGED