@lincoln504/pi-research 1.6.8 → 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.
@@ -17,9 +17,9 @@ import type { ToolUsageTracker } from '../utils/tool-usage-tracker.ts';
17
17
  import type { SystemResearchState } from '../orchestration/deep-research-types.ts';
18
18
  import { deduplicateUrls, normalizeUrl, getCachedScrapedContent, registerResearcherScrapes, buildSessionPoolFooter } from '../utils/shared-links.ts';
19
19
  import {
20
- MAX_SCRAPE_URLS,
21
20
  BATCH_2_DEFAULT_CONCURRENCY,
22
21
  getMaxScrapeBatches,
22
+ getMaxScrapeUrls,
23
23
  DEFAULT_MODEL_CONTEXT_WINDOW,
24
24
  MAX_SCRAPE_CONTENT_CHARS_PER_DOC,
25
25
  } from '../constants.ts';
@@ -57,6 +57,10 @@ export function createScrapeTool(options: {
57
57
  // enforced — a real, if harmless, missed-opportunity: the model would stop one
58
58
  // batch short of what it was actually allowed to use.
59
59
  const maxScrapeBatches = options.tracker?.getToolLimit('scrape') ?? getMaxScrapeBatches(config);
60
+ // One resolved value feeds the tool schema text, the batch-protocol text, the
61
+ // per-call slice, and the over-cap report — the enforced cap and every string
62
+ // the model sees are the same number by construction.
63
+ const maxScrapeUrls = getMaxScrapeUrls(config);
60
64
  const container = tryGetServiceContainerFromCtx(options.ctx);
61
65
 
62
66
  // Fallback global state when no orchestration context is provided
@@ -78,7 +82,7 @@ export function createScrapeTool(options: {
78
82
  });
79
83
 
80
84
  const ScrapeParamsSchema = Type.Object({
81
- urls: Type.Array(Type.String({ description: `The URLs to scrape (only the first ${MAX_SCRAPE_URLS} are fetched per call; extras are reported under "Not Fetched" rather than silently dropped)` }), { minItems: 1, maxItems: 20 }),
85
+ urls: Type.Array(Type.String({ description: `The URLs to scrape (only the first ${maxScrapeUrls} are fetched per call; extras are reported under "Not Fetched" rather than silently dropped)` }), { minItems: 1, maxItems: 20 }),
82
86
  maxConcurrency: Type.Optional(Type.Number({ default: config.MAX_CONCURRENT_SCRAPES, minimum: 1, maximum: 20 })),
83
87
  });
84
88
 
@@ -92,7 +96,7 @@ export function createScrapeTool(options: {
92
96
  batchProtocolText = `PROTOCOL: Batch 1 → Batch 2 → ... (up to ${effectiveLimit} batches)`;
93
97
  } else {
94
98
  const batchNumbers = Array.from({ length: effectiveLimit }, (_, i) => `Batch ${i + 1}`).join(' → ');
95
- batchProtocolText = `PROTOCOL: ${batchNumbers} (up to ${MAX_SCRAPE_URLS} URLs each).`;
99
+ batchProtocolText = `PROTOCOL: ${batchNumbers} (up to ${maxScrapeUrls} URLs each).`;
96
100
  }
97
101
 
98
102
  return {
@@ -102,7 +106,7 @@ export function createScrapeTool(options: {
102
106
  promptSnippet: `Read full content of web pages or PDFs (up to ${maxScrapeBatches} batches)`,
103
107
  promptGuidelines: [
104
108
  batchProtocolText,
105
- `Up to ${MAX_SCRAPE_URLS} URLs per batch.`,
109
+ `Up to ${maxScrapeUrls} URLs per batch.`,
106
110
  'Handshake is ELIMINATED. Start scraping immediately.',
107
111
  'PDFs are auto-detected and extracted with high fidelity.',
108
112
  ],
@@ -175,8 +179,8 @@ export function createScrapeTool(options: {
175
179
  // in results nor in Failed-to-Scrape, and the session-pool footer excludes the
176
180
  // current batch).
177
181
  const fetchCandidates = [...dedupedUrls, ...evictedDuplicates];
178
- const finalUrls = fetchCandidates.slice(0, MAX_SCRAPE_URLS);
179
- const skippedUrls = fetchCandidates.slice(MAX_SCRAPE_URLS);
182
+ const finalUrls = fetchCandidates.slice(0, maxScrapeUrls);
183
+ const skippedUrls = fetchCandidates.slice(maxScrapeUrls);
180
184
 
181
185
  // Context gate: block if projected token usage would exceed threshold
182
186
  if (options.getTokensUsed) {
@@ -364,7 +368,7 @@ export function createScrapeTool(options: {
364
368
 
365
369
  if (skippedUrls.length > 0) {
366
370
  markdown += `## Not Fetched — Over Batch Cap (${skippedUrls.length})\n\n`;
367
- markdown += `Only the first ${MAX_SCRAPE_URLS} URLs of a batch are fetched. The following URLs were NOT fetched — no content for them appears above. Request them in a later scrape batch if still needed:\n`;
371
+ markdown += `Only the first ${maxScrapeUrls} URLs of a batch are fetched. The following URLs were NOT fetched — no content for them appears above. Request them in a later scrape batch if still needed:\n`;
368
372
  for (const url of skippedUrls) {
369
373
  markdown += `- ${url}\n`;
370
374
  }
@@ -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