@lincoln504/pi-research 1.6.9 → 1.6.10
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 +4 -0
- package/agent-skill/pi-research/SKILL.md +1 -1
- package/docs/CHANGELOG.md +19 -0
- package/package.json +1 -1
- package/src/research-config.ts +31 -7
- package/src/tui/tui-controller.ts +17 -0
package/README.md
CHANGED
|
@@ -53,6 +53,10 @@ tries to compile from source; upgrading npm fixes it.
|
|
|
53
53
|
|
|
54
54
|
In pi it works out of the box on the session's model and pi's configuration. Standalone use ([agent skill](docs/AGENT-SKILL.md) or [SDK](docs/SDK.md)) needs a model configured. See [Configuration](docs/CONFIGURATION.md).
|
|
55
55
|
|
|
56
|
+
### Uninstall
|
|
57
|
+
|
|
58
|
+
`pi remove npm:@lincoln504/pi-research` removes the extension, and `npm uninstall -g @lincoln504/pi-research` the standalone engine. npm 7+ no longer runs `preuninstall`, so **nothing else is removed on its own**: skill links into other agents (Claude Code, Codex, …), the state directory (`~/.pi/research/state`), and the cache (`~/.cache/pi-research`, including any downloaded embedding models) stay in place. Remove the skill links first with `pi-research skill uninstall` (or `/research-config` → Remove from External Agents); the shared stealth-browser cache (`~/.cache/camoufox`) is preserved unless `PI_RESEARCH_PURGE_BROWSERS=1`. See [AGENT-SKILL.md](docs/AGENT-SKILL.md#installation-flow) for the full picture.
|
|
59
|
+
|
|
56
60
|
### How it works
|
|
57
61
|
|
|
58
62
|
A research run loops through agent teams: a coordinator plans and starts with a search, researcher agents scrape and read in parallel, and a research lead decides whether to go another round, then writes the report from every report collected. The result is one cited Markdown report with findings optionally saved to the knowledge store.
|
|
@@ -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.6.
|
|
5
|
+
metadata: { "author": "Lincoln Deen", "version": "1.6.10", "package": "@lincoln504/pi-research", "engine": "pi-research-sdk", "openclaw": { "requires": { "bins": ["node"] } } }
|
|
6
6
|
allowed-tools: Bash(node:*)
|
|
7
7
|
---
|
|
8
8
|
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.6.10] - 2026-09-02
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- **`/research-config` now behaves properly on every pi host — TUI, RPC/web-hub GUIs, print/json, and the SDK (`guis` is a family of frontends, not one client).** The interactive settings menu is now gated on `ctx.mode === 'tui'` (not just `ctx.hasUI`, which is true in RPC too): on non-TUI hosts the command replies with a clear non-interactive guidance message instead of silently entering a menu that cannot render. Those hosts get working headless diagnostics instead — `/research-config health` and `/research-config knowledge-status` now run on any host (previously they were reachable only through the menu, even though the tool failure text points users at health). Every destructive `ctx.ui.confirm` now carries a 60s timeout, so no host can wedge on a dialog whose client never responds.
|
|
13
|
+
|
|
14
|
+
- **The knowledge store's Arrow/LanceDB boundary is now verified in the actual consumer layout on every CI run.** The dev tree resolves a single apache-arrow 21.1.0 (forced by `overrides`), but a fresh npm consumer install resolves two copies — 21.1.0 nested under the package for the direct pin and 18.1.0 hoisted for `@lancedb/lancedb`'s declared peer ceiling. docs/ARCHITECTURE.md pins 21.1.0 for a measured reason and warns mismatched Arrow copies may not interoperate, and this divergence is invisible to the dev-tree tests. The fresh-install CI job now runs `scripts/consumer-interop-probe.cjs`, which replicates `createStoreTable` (same fields, FTS/BTree index set, reopen-from-disk) with the consumer's exact resolution. README gains an Uninstall section documenting that npm 7+ runs no `preuninstall`, so skill links / `~/.pi/research/state` / caches persist unless removed first.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- **Ctrl+C during a research run no longer cancels the run while the editor still holds typed text — it clears the editor first, and only a subsequent Ctrl+C (editor now empty) cancels the research.** The global TUI controller (`src/tui/tui-controller.ts`) treated Ctrl+C and Esc identically as cancel keys, aborting the session's active research runs on every press of either. That fought pi's own keybinding contract: Ctrl+C is `app.clear` ("Clear editor" — pi's `handleCtrlC()` clears the editor text and never interrupts the agent, doubling as the two-press-within-500ms exit), while Esc is `app.interrupt` ("Cancel or abort"), so a user discarding a drafted follow-up mid-run got the whole run killed instead of an empty editor. The controller now reads the editor via `ctx.ui.getEditorText()`: with text present, Ctrl+C falls through untouched (pi clears the text; the run keeps going); with an empty editor it is an explicit cancel gesture and aborts, still scoped to the current pi session as before; Esc keeps its unconditional panic-cancel role; and a throwing `getEditorText()` (ctx gone stale mid-run) is treated as empty — cancel — the safe default. The two gestures coexist with pi's native double-press exit in practice: the clear-then-decide rhythm is naturally slower than the 500ms window, so the human cancel path never trips pi's shutdown. Seven new tests pin the policy (`test/unit/tui/tui-controller.test.ts`).
|
|
19
|
+
|
|
20
|
+
- **`/research-config` could silently do nothing on RPC hosts.** `ctx.hasUI` is true in RPC, so the old guard passed and `ctx.ui.custom(...)` was entered even though no non-TUI host can render or answer the menu. The mode gate in the change above makes the menu reachable only on a real TUI and hands every other host the guidance message plus the headless `health` / `knowledge-status` diagnostics.
|
|
21
|
+
|
|
22
|
+
### Verified
|
|
23
|
+
|
|
24
|
+
- 3,027 unit tests over 240 files (14 new across this Unreleased cycle: 7 Ctrl+C-policy + 7 host-mode), ESLint clean on the touched source, all four type-checks (TS 6 src + tests, TS 7 native src + tests), and dependency-cruiser clean (238 modules / 1,218 dependencies).
|
|
25
|
+
|
|
26
|
+
|
|
8
27
|
## [1.6.9] - 2026-09-01
|
|
9
28
|
|
|
10
29
|
### Changed
|
package/package.json
CHANGED
package/src/research-config.ts
CHANGED
|
@@ -89,12 +89,34 @@ const ENV_KEY_BY_SETTING_ID: Record<string, string> = {
|
|
|
89
89
|
* Main command handler for /research-config
|
|
90
90
|
*/
|
|
91
91
|
export async function handleResearchConfigCommand(
|
|
92
|
-
|
|
92
|
+
args: string,
|
|
93
93
|
ctx: ExtensionContext,
|
|
94
94
|
pi: ExtensionAPI
|
|
95
95
|
): Promise<void> {
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
const verb = args.trim().split(/\s+/)[0]?.toLowerCase() ?? '';
|
|
97
|
+
if (verb === 'health') {
|
|
98
|
+
await runHealthCheckAction(ctx, pi);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (verb === 'knowledge-status' || verb === 'knowledge_status') {
|
|
102
|
+
await showKnowledgeStatusAction(ctx, pi);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (ctx.mode !== 'tui' || !ctx.hasUI) {
|
|
106
|
+
// The interactive menu needs pi's real TUI. In RPC/web-hub/print/json/SDK hosts
|
|
107
|
+
// ctx.hasUI is true (RPC) and the menu cannot render — awaiting it silently
|
|
108
|
+
// nudges the host toward a hang and at best does nothing visible. Give a clear
|
|
109
|
+
// non-interactive answer with the headless diagnostics that DO work here.
|
|
110
|
+
pi.sendMessage({
|
|
111
|
+
customType: 'research-config',
|
|
112
|
+
content: 'The interactive `/research-config` menu requires pi\'s terminal UI (TUI).\n\n' +
|
|
113
|
+
'In this host, use the headless diagnostics instead:\n' +
|
|
114
|
+
'- `/research-config health` — system health status\n' +
|
|
115
|
+
'- `/research-config knowledge-status` — knowledge store status\n\n' +
|
|
116
|
+
'Configuration is read from the environment and config files; set `PI_RESEARCH_*` ' +
|
|
117
|
+
'environment variables to change it.',
|
|
118
|
+
display: true,
|
|
119
|
+
});
|
|
98
120
|
return;
|
|
99
121
|
}
|
|
100
122
|
await showInteractiveMenu(ctx, pi);
|
|
@@ -498,6 +520,7 @@ async function showInteractiveMenu(ctx: ExtensionContext, pi: ExtensionAPI): Pro
|
|
|
498
520
|
modelChangeConfirmed = !cancelled && await ctx.ui.confirm(
|
|
499
521
|
'Change Embedding Model',
|
|
500
522
|
`Switch the embedding model to ${config.EMBEDDING_MODEL.split('/').pop()}? This permanently clears the current knowledge store.`,
|
|
523
|
+
{ timeout: 60_000 },
|
|
501
524
|
);
|
|
502
525
|
if (modelChangeConfirmed) {
|
|
503
526
|
logger.info(`[research-config] Embedding model changed from ${initialConfig.EMBEDDING_MODEL} to ${config.EMBEDDING_MODEL}. Clearing knowledge store.`);
|
|
@@ -550,7 +573,7 @@ async function showInteractiveMenu(ctx: ExtensionContext, pi: ExtensionAPI): Pro
|
|
|
550
573
|
await showKnowledgeStatusAction(ctx, pi);
|
|
551
574
|
break;
|
|
552
575
|
case 'knowledge_clear_global': {
|
|
553
|
-
const confirmed = await ctx.ui.confirm('Clear User Store', 'Permanently delete all user-scoped store data?');
|
|
576
|
+
const confirmed = await ctx.ui.confirm('Clear User Store', 'Permanently delete all user-scoped store data?', { timeout: 60_000 });
|
|
554
577
|
if (confirmed) {
|
|
555
578
|
try {
|
|
556
579
|
const service = await getService<IKnowledgeStoreService>(ServiceNames.KNOWLEDGE_STORE, ctx, container);
|
|
@@ -563,7 +586,7 @@ async function showInteractiveMenu(ctx: ExtensionContext, pi: ExtensionAPI): Pro
|
|
|
563
586
|
break;
|
|
564
587
|
}
|
|
565
588
|
case 'knowledge_clear_local': {
|
|
566
|
-
const confirmed = await ctx.ui.confirm('Clear Project Store', 'Permanently delete all project-scoped store data?');
|
|
589
|
+
const confirmed = await ctx.ui.confirm('Clear Project Store', 'Permanently delete all project-scoped store data?', { timeout: 60_000 });
|
|
567
590
|
if (confirmed) {
|
|
568
591
|
try {
|
|
569
592
|
const service = await getService<IKnowledgeStoreService>(ServiceNames.KNOWLEDGE_STORE, ctx, container);
|
|
@@ -577,13 +600,13 @@ async function showInteractiveMenu(ctx: ExtensionContext, pi: ExtensionAPI): Pro
|
|
|
577
600
|
}
|
|
578
601
|
case 'metrics':
|
|
579
602
|
await showMetricsAction(ctx, pi);
|
|
580
|
-
if (await ctx.ui.confirm('Session Metrics', 'Reset all session counters?')) {
|
|
603
|
+
if (await ctx.ui.confirm('Session Metrics', 'Reset all session counters?', { timeout: 60_000 })) {
|
|
581
604
|
metrics.clearSession();
|
|
582
605
|
ctx.ui.notify('Session metrics reset.', 'info');
|
|
583
606
|
}
|
|
584
607
|
break;
|
|
585
608
|
case 'logs_clear': {
|
|
586
|
-
const confirmed = await ctx.ui.confirm('Clear Logs', 'Delete the main diagnostic log file and all archived rotation files?');
|
|
609
|
+
const confirmed = await ctx.ui.confirm('Clear Logs', 'Delete the main diagnostic log file and all archived rotation files?', { timeout: 60_000 });
|
|
587
610
|
if (confirmed) {
|
|
588
611
|
logger.clear();
|
|
589
612
|
ctx.ui.notify('Diagnostic logs cleared.', 'info');
|
|
@@ -704,6 +727,7 @@ async function uninstallSkillAction(ctx: ExtensionContext, pi: ExtensionAPI): Pr
|
|
|
704
727
|
const confirmed = await ctx.ui.confirm(
|
|
705
728
|
'Uninstall Skill',
|
|
706
729
|
`Remove the pi-research skill from ${names}? (Only symlinks this extension created are removed.)`,
|
|
730
|
+
{ timeout: 60_000 },
|
|
707
731
|
);
|
|
708
732
|
if (!confirmed) return;
|
|
709
733
|
try {
|
|
@@ -66,6 +66,23 @@ export function initGlobalTuiController(ui: ExtensionUIContext, piSessionId?: st
|
|
|
66
66
|
// Check for specific global cancel keys (Escape and Ctrl+C)
|
|
67
67
|
// We only trigger abort if research is actually running.
|
|
68
68
|
if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c')) {
|
|
69
|
+
// Ctrl+C is pi's app.clear ("Clear editor"), NOT its cancel key — pi's own
|
|
70
|
+
// handleCtrlC() clears the editor text and never interrupts the agent (Esc
|
|
71
|
+
// is app.interrupt). Aborting the run on every Ctrl+C stole the key from
|
|
72
|
+
// users who merely wanted to discard typed text mid-run. Follow pi's
|
|
73
|
+
// convention: with text in the editor, Ctrl+C only clears (fall through and
|
|
74
|
+
// let pi do the clearing); an EMPTY editor is an explicit cancel gesture and
|
|
75
|
+
// aborts the active research.
|
|
76
|
+
if (matchesKey(data, 'ctrl+c')) {
|
|
77
|
+
let editorText = '';
|
|
78
|
+
try {
|
|
79
|
+
editorText = typeof ui.getEditorText === 'function' ? (ui.getEditorText() ?? '') : '';
|
|
80
|
+
} catch { /* editor gone mid-run — treat as empty (cancel) */ }
|
|
81
|
+
if (editorText.length > 0) {
|
|
82
|
+
logger.debug('[TUI] Ctrl+C with editor text — clearing only, research keeps running.');
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
69
86
|
const activeCount = getActiveSessionCount();
|
|
70
87
|
if (activeCount > 0) {
|
|
71
88
|
// FIX (New Issues B & E): Use abortAllSessions scoped to the current Pi session
|