@comfanion/usethis_search 4.3.0-dev.0 → 4.3.0-dev.1

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.
@@ -57,7 +57,9 @@ export function createWorkspaceInjectionHandler(state: SessionState) {
57
57
  // Don't inject or prune for sub-agents (title generation, etc.)
58
58
  if (state.isSubAgent) return
59
59
 
60
- // ── Prune: replace old tool outputs with compact summaries ────────────
60
+ // ── Prune & Compact: optimize chat history ────────────────────────────
61
+ // 1. Prune: replace old tool outputs with compact summaries
62
+ // 2. Compact: remove old tool calls entirely (keep last N turns)
61
63
  // Files are already in workspace injection — no need for big outputs
62
64
  // in chat history. This runs even when workspace is empty
63
65
  // (handles case where workspace was cleared but old outputs remain).
@@ -65,6 +67,7 @@ export function createWorkspaceInjectionHandler(state: SessionState) {
65
67
  if (wsConfig.autoPruneSearch !== false) {
66
68
  pruneSearchToolOutputs(output.messages)
67
69
  pruneReadToolOutputs(output.messages)
70
+ compactOldToolCalls(output.messages)
68
71
  }
69
72
 
70
73
  let entries = workspaceCache.getAll()
@@ -427,3 +430,137 @@ function extractFilePathFromOutput(output: string): string | null {
427
430
 
428
431
  return null
429
432
  }
433
+
434
+ // ── Tool Call Compaction ────────────────────────────────────────────────────
435
+
436
+ /**
437
+ * Remove old tool calls (search/read) from chat history.
438
+ *
439
+ * Strategy:
440
+ * - Keep last N turns (default: 5) — agent may reference recent calls
441
+ * - Only compact search/read tools (not edit/write/grep/glob)
442
+ * - Only compact completed calls with pruned outputs
443
+ * - Remove both call + output parts
444
+ * - Add compact marker at start showing how many calls removed
445
+ *
446
+ * Why: Tool calls contain full args (200+ tokens). After pruning outputs,
447
+ * the calls themselves are redundant — chunks already in workspace.
448
+ *
449
+ * Savings: ~220 tokens per compacted call × N calls = 2K-10K tokens
450
+ */
451
+ const KEEP_LAST_N_TURNS = 5
452
+ const COMPACT_TOOLS = ['search', 'read', 'Read']
453
+
454
+ interface ToolCallPair {
455
+ msgIndex: number
456
+ callPart: MessagePart
457
+ outputPart?: MessagePart
458
+ tool: string
459
+ status: string
460
+ turnsSinceEnd: number
461
+ }
462
+
463
+ /**
464
+ * Compact old tool calls by removing them from chat history.
465
+ * Keeps last N turns intact.
466
+ */
467
+ export function compactOldToolCalls(messages: Message[]): void {
468
+ // Find all tool call pairs
469
+ const toolPairs = findToolCallPairs(messages)
470
+
471
+ if (toolPairs.length === 0) return
472
+
473
+ // Calculate turns from end for each pair
474
+ const totalTurns = messages.length
475
+
476
+ // Filter: only old, completed, search/read with pruned outputs
477
+ const toCompact = toolPairs.filter(pair => {
478
+ const turnsFromEnd = totalTurns - pair.msgIndex
479
+ return (
480
+ turnsFromEnd > KEEP_LAST_N_TURNS &&
481
+ pair.status === 'completed' &&
482
+ COMPACT_TOOLS.includes(pair.tool) &&
483
+ pair.outputPart &&
484
+ isPrunedOutput(pair.outputPart.state?.output || '')
485
+ )
486
+ })
487
+
488
+ if (toCompact.length === 0) return
489
+
490
+ // Remove tool parts from messages
491
+ const removedIds = new Set<string>()
492
+
493
+ for (const pair of toCompact) {
494
+ removedIds.add(pair.callPart.id)
495
+ if (pair.outputPart) {
496
+ removedIds.add(pair.outputPart.id)
497
+ }
498
+ }
499
+
500
+ // Filter out removed parts from messages
501
+ for (const msg of messages) {
502
+ if (!msg.parts || !Array.isArray(msg.parts)) continue
503
+ msg.parts = msg.parts.filter(part => !removedIds.has(part.id))
504
+ }
505
+
506
+ // Add compact marker to first user message
507
+ const firstUserMsg = messages.find(m => m?.info?.role === 'user')
508
+ if (firstUserMsg && firstUserMsg.parts) {
509
+ const marker = {
510
+ type: 'text',
511
+ text: `<!-- ${toCompact.length} tool calls compacted (search/read results in workspace) -->`,
512
+ id: 'compact-marker-' + Date.now(),
513
+ }
514
+ firstUserMsg.parts.unshift(marker)
515
+ }
516
+ }
517
+
518
+ /**
519
+ * Find all tool call + output pairs in messages.
520
+ */
521
+ function findToolCallPairs(messages: Message[]): ToolCallPair[] {
522
+ const pairs: ToolCallPair[] = []
523
+
524
+ for (let i = 0; i < messages.length; i++) {
525
+ const msg = messages[i]
526
+ if (!msg.parts || !Array.isArray(msg.parts)) continue
527
+
528
+ for (const part of msg.parts) {
529
+ if (part.type === 'tool' && part.tool) {
530
+ const status = part.state?.status || 'unknown'
531
+
532
+ // Find matching output part (usually in same message or next)
533
+ let outputPart: MessagePart | undefined
534
+
535
+ // Check same message first
536
+ for (const p of msg.parts) {
537
+ if (p.type === 'tool' && p.tool === part.tool && p.state?.output && p.id !== part.id) {
538
+ outputPart = p
539
+ break
540
+ }
541
+ }
542
+
543
+ pairs.push({
544
+ msgIndex: i,
545
+ callPart: part,
546
+ outputPart,
547
+ tool: part.tool,
548
+ status,
549
+ turnsSinceEnd: 0, // Will be calculated in compactOldToolCalls
550
+ })
551
+ }
552
+ }
553
+ }
554
+
555
+ return pairs
556
+ }
557
+
558
+ /**
559
+ * Check if output is pruned (compact format).
560
+ */
561
+ function isPrunedOutput(output: string): boolean {
562
+ if (!output) return false
563
+
564
+ // Pruned outputs start with [ or ✓
565
+ return output.startsWith('[') || output.startsWith('✓')
566
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@comfanion/usethis_search",
3
- "version": "4.3.0-dev.0",
4
- "description": "OpenCode plugin: semantic search with auto-attach, line numbers in workspace, simplified API (v4.3: auto-detect modes, read() caching, 99% token reduction, no grep needed)",
3
+ "version": "4.3.0-dev.1",
4
+ "description": "OpenCode plugin: semantic search with auto-attach, line numbers in workspace, simplified API (v4.3: auto-detect modes, read() caching, tool call compaction, 99% token reduction, no grep needed)",
5
5
  "type": "module",
6
6
  "main": "./index.ts",
7
7
  "exports": {
package/vectorizer.yaml CHANGED
@@ -26,6 +26,7 @@ vectorizer:
26
26
  min_chunk_size: 1000 # Merge small sections (avoid header-only chunks)
27
27
  max_chunk_size: 8000 # Large chunks for docs (SQL schemas, API specs, etc.)
28
28
  preserve_heading_hierarchy: true
29
+ skip_low_priority: true # Skip SQL schemas, continuous aggregates (default: true)
29
30
  code:
30
31
  split_by_functions: true
31
32
  include_function_signature: true