@lincoln504/pi-research 1.0.1 → 1.0.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/dist/cli.mjs
CHANGED
|
@@ -20166,7 +20166,8 @@ ${prompt}`);
|
|
|
20166
20166
|
const msg2 = `Researcher ${id} (${researcherConfig.name}) timed out after ${config.RESEARCHER_TIMEOUT_MS}ms`;
|
|
20167
20167
|
session.abort().catch((err) => {
|
|
20168
20168
|
logger.warn("[ResearcherExecutor] Failed to abort timed-out researcher session:", err);
|
|
20169
|
-
})
|
|
20169
|
+
});
|
|
20170
|
+
reject(new Error(msg2));
|
|
20170
20171
|
}, config.RESEARCHER_TIMEOUT_MS);
|
|
20171
20172
|
});
|
|
20172
20173
|
let abortCleanup;
|
|
@@ -20248,9 +20249,18 @@ ${responseText}`);
|
|
|
20248
20249
|
}
|
|
20249
20250
|
} finally {
|
|
20250
20251
|
subscription();
|
|
20251
|
-
await
|
|
20252
|
-
|
|
20253
|
-
|
|
20252
|
+
await Promise.race([
|
|
20253
|
+
session.abort().catch((err) => {
|
|
20254
|
+
logger.warn(`[ResearcherExecutor] Failed to abort researcher session ${id}:`, err);
|
|
20255
|
+
}),
|
|
20256
|
+
new Promise((resolve8) => {
|
|
20257
|
+
const t = setTimeout(() => {
|
|
20258
|
+
logger.warn(`[ResearcherExecutor] Researcher ${id} session abort did not settle within 10s; continuing cleanup`);
|
|
20259
|
+
resolve8();
|
|
20260
|
+
}, 1e4);
|
|
20261
|
+
t.unref?.();
|
|
20262
|
+
})
|
|
20263
|
+
]);
|
|
20254
20264
|
sessionService?.unregisterSession(researchId, id);
|
|
20255
20265
|
if (ctx.mode !== "tui" && ctx.hasUI && typeof ctx.ui.setHiddenThinkingLabel === "function") {
|
|
20256
20266
|
ctx.ui.setHiddenThinkingLabel();
|
|
@@ -20526,7 +20536,8 @@ ${prompt}`);
|
|
|
20526
20536
|
const msg2 = `Quick research timed out after ${this.config.RESEARCHER_TIMEOUT_MS}ms`;
|
|
20527
20537
|
session.abort().catch((err) => {
|
|
20528
20538
|
logger.warn("[QuickOrchestrator] Failed to abort timed-out session:", err);
|
|
20529
|
-
})
|
|
20539
|
+
});
|
|
20540
|
+
reject(new Error(msg2));
|
|
20530
20541
|
}, this.config.RESEARCHER_TIMEOUT_MS);
|
|
20531
20542
|
});
|
|
20532
20543
|
let abortCleanup;
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -301,7 +301,7 @@ Browser & scraping
|
|
|
301
301
|
|
|
302
302
|
- [Camoufox](https://camoufox.com) — stealth Firefox (driven via [Playwright](https://playwright.dev)) for undetected search and scraping
|
|
303
303
|
- [poolifier](https://github.com/poolifier/poolifier) — the worker-process pool behind the browser workers
|
|
304
|
-
- [html-to-markdown](https://github.com/kreuzberg-dev/html-to-markdown)
|
|
304
|
+
- [html-to-markdown](https://github.com/kreuzberg-dev/html-to-markdown) — converts scraped HTML to Markdown (node-html-markdown serves as the pure-JS fallback)
|
|
305
305
|
- `pdf-oxide-wasm` — PDF text extraction (Rust/WASM)
|
|
306
306
|
|
|
307
307
|
Knowledge store & embeddings
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: pi-research
|
|
3
3
|
description: "Multi-agent web research for any coding agent. Use for anything requiring current information from the internet — news, trends, product/service/company facts, statistics, people, places, events, APIs, library behavior, or 'what is X / how does X work' questions. Also use to search a local knowledge store of previously researched findings before doing live research. Powered by the pi-research engine (search, stealth scraping, security databases, Stack Exchange). Not for reading local files, running project commands, or analyzing this repository's own source."
|
|
4
4
|
license: MIT
|
|
5
|
-
metadata: { "author": "Lincoln Deen", "version": "1.0.
|
|
5
|
+
metadata: { "author": "Lincoln Deen", "version": "1.0.2", "package": "@lincoln504/pi-research", "engine": "pi-research-sdk", "openclaw": { "requires": { "bins": ["node"] } } }
|
|
6
6
|
allowed-tools: Bash(node:*)
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -269,9 +269,13 @@ export class QuickResearchOrchestrator {
|
|
|
269
269
|
const timeoutPromise = new Promise<void>((_, reject) => {
|
|
270
270
|
timeoutId = setTimeout(() => {
|
|
271
271
|
const msg = `Quick research timed out after ${this.config.RESEARCHER_TIMEOUT_MS}ms`;
|
|
272
|
+
// Reject FIRST, abort as fire-and-forget cleanup — see researcher-executor:
|
|
273
|
+
// abort() awaits the stuck in-flight request, so gating the rejection on it
|
|
274
|
+
// settling disables the timeout exactly when it is needed.
|
|
272
275
|
session.abort().catch((err) => {
|
|
273
276
|
logger.warn('[QuickOrchestrator] Failed to abort timed-out session:', err);
|
|
274
|
-
})
|
|
277
|
+
});
|
|
278
|
+
reject(new Error(msg));
|
|
275
279
|
}, this.config.RESEARCHER_TIMEOUT_MS);
|
|
276
280
|
});
|
|
277
281
|
|
|
@@ -264,9 +264,14 @@ export async function runResearcher(options: RunResearcherOptions): Promise<void
|
|
|
264
264
|
const timeoutPromise = new Promise<void>((_, reject) => {
|
|
265
265
|
timeoutId = setTimeout(() => {
|
|
266
266
|
const msg = `Researcher ${id} (${researcherConfig.name}) timed out after ${config.RESEARCHER_TIMEOUT_MS}ms`;
|
|
267
|
+
// Reject FIRST, abort as fire-and-forget cleanup: session.abort() awaits the
|
|
268
|
+
// in-flight request, which is the very thing that is stuck (e.g. a provider
|
|
269
|
+
// retry loop against an exhausted quota) — gating the rejection on it settling
|
|
270
|
+
// makes the timeout ineffective exactly when it is needed.
|
|
267
271
|
session.abort().catch((err) => {
|
|
268
272
|
logger.warn('[ResearcherExecutor] Failed to abort timed-out researcher session:', err);
|
|
269
|
-
})
|
|
273
|
+
});
|
|
274
|
+
reject(new Error(msg));
|
|
270
275
|
}, config.RESEARCHER_TIMEOUT_MS);
|
|
271
276
|
});
|
|
272
277
|
|
|
@@ -375,9 +380,23 @@ export async function runResearcher(options: RunResearcherOptions): Promise<void
|
|
|
375
380
|
}
|
|
376
381
|
} finally {
|
|
377
382
|
subscription();
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
383
|
+
// Bounded: after a timeout, session.abort() awaits the very in-flight request
|
|
384
|
+
// that is stuck (e.g. a provider retry loop against an exhausted quota) and may
|
|
385
|
+
// never settle — never let per-attempt cleanup hang the retry loop on it. The
|
|
386
|
+
// retry builds a fresh session; the abandoned abort keeps draining in the
|
|
387
|
+
// background and its session dies with the run's teardown.
|
|
388
|
+
await Promise.race([
|
|
389
|
+
session.abort().catch((err) => {
|
|
390
|
+
logger.warn(`[ResearcherExecutor] Failed to abort researcher session ${id}:`, err);
|
|
391
|
+
}),
|
|
392
|
+
new Promise<void>((resolve) => {
|
|
393
|
+
const t = setTimeout(() => {
|
|
394
|
+
logger.warn(`[ResearcherExecutor] Researcher ${id} session abort did not settle within 10s; continuing cleanup`);
|
|
395
|
+
resolve();
|
|
396
|
+
}, 10_000);
|
|
397
|
+
t.unref?.();
|
|
398
|
+
}),
|
|
399
|
+
]);
|
|
381
400
|
sessionService?.unregisterSession(researchId, id);
|
|
382
401
|
|
|
383
402
|
// Restore the default thinking label now that the researcher is done.
|