@compilr-dev/agents 0.0.1 → 0.2.0

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.
Files changed (44) hide show
  1. package/dist/agent.d.ts +188 -1
  2. package/dist/agent.js +284 -14
  3. package/dist/context/file-tracker.d.ts +156 -0
  4. package/dist/context/file-tracker.js +358 -0
  5. package/dist/context/file-tracking-hook.d.ts +29 -0
  6. package/dist/context/file-tracking-hook.js +103 -0
  7. package/dist/context/index.d.ts +5 -1
  8. package/dist/context/index.js +3 -0
  9. package/dist/context/manager.d.ts +69 -1
  10. package/dist/context/manager.js +304 -0
  11. package/dist/context/types.d.ts +95 -0
  12. package/dist/index.d.ts +13 -5
  13. package/dist/index.js +11 -3
  14. package/dist/messages/index.d.ts +13 -0
  15. package/dist/messages/index.js +51 -0
  16. package/dist/permissions/manager.js +6 -1
  17. package/dist/providers/gemini.d.ts +91 -0
  18. package/dist/providers/gemini.js +138 -0
  19. package/dist/providers/index.d.ts +8 -0
  20. package/dist/providers/index.js +7 -3
  21. package/dist/providers/mock.js +8 -0
  22. package/dist/providers/ollama.d.ts +87 -0
  23. package/dist/providers/ollama.js +133 -0
  24. package/dist/providers/openai-compatible.d.ts +182 -0
  25. package/dist/providers/openai-compatible.js +357 -0
  26. package/dist/providers/openai.d.ts +93 -0
  27. package/dist/providers/openai.js +133 -0
  28. package/dist/skills/index.js +691 -0
  29. package/dist/tools/builtin/glob.d.ts +11 -0
  30. package/dist/tools/builtin/glob.js +44 -2
  31. package/dist/tools/builtin/grep.d.ts +11 -1
  32. package/dist/tools/builtin/grep.js +38 -2
  33. package/dist/tools/builtin/index.d.ts +6 -1
  34. package/dist/tools/builtin/index.js +7 -0
  35. package/dist/tools/builtin/suggest.d.ts +57 -0
  36. package/dist/tools/builtin/suggest.js +99 -0
  37. package/dist/tools/builtin/task.js +13 -8
  38. package/dist/tools/builtin/tool-names.d.ts +44 -0
  39. package/dist/tools/builtin/tool-names.js +51 -0
  40. package/dist/tools/index.d.ts +2 -2
  41. package/dist/tools/index.js +5 -1
  42. package/dist/tools/registry.d.ts +4 -0
  43. package/dist/tools/registry.js +9 -0
  44. package/package.json +2 -2
package/dist/agent.js CHANGED
@@ -7,11 +7,14 @@ import { ProjectMemoryLoader } from './memory/loader.js';
7
7
  import { UsageTracker } from './costs/tracker.js';
8
8
  import { DefaultToolRegistry } from './tools/registry.js';
9
9
  import { ContextManager } from './context/manager.js';
10
+ import { FileAccessTracker } from './context/file-tracker.js';
11
+ import { createFileTrackingHook } from './context/file-tracking-hook.js';
10
12
  import { AnchorManager } from './anchors/manager.js';
11
13
  import { GuardrailManager } from './guardrails/manager.js';
12
14
  import { MaxIterationsError, ToolLoopError } from './errors.js';
13
15
  import { generateSessionId, createAgentState, deserializeTodos } from './state/agent-state.js';
14
16
  import { getDefaultTodoStore, createIsolatedTodoStore } from './tools/builtin/todo.js';
17
+ import { repairToolPairing } from './messages/index.js';
15
18
  /**
16
19
  * Agent class - orchestrates LLM interactions with tool use
17
20
  *
@@ -40,6 +43,7 @@ export class Agent {
40
43
  contextManager;
41
44
  autoContextManagement;
42
45
  onEvent;
46
+ onIterationLimitReached;
43
47
  // State management
44
48
  checkpointer;
45
49
  _sessionId;
@@ -80,6 +84,10 @@ export class Agent {
80
84
  * Hooks manager for lifecycle hooks
81
85
  */
82
86
  hooksManager;
87
+ /**
88
+ * File access tracker for context restoration hints
89
+ */
90
+ fileTracker;
83
91
  constructor(config) {
84
92
  this.provider = config.provider;
85
93
  this.systemPrompt = config.systemPrompt ?? '';
@@ -87,11 +95,16 @@ export class Agent {
87
95
  this.maxConsecutiveToolCalls = config.maxConsecutiveToolCalls ?? 3;
88
96
  this.iterationLimitBehavior = config.iterationLimitBehavior ?? 'error';
89
97
  this.chatOptions = config.chatOptions ?? {};
90
- this.toolRegistry = config.toolRegistry ?? new DefaultToolRegistry();
98
+ this.toolRegistry =
99
+ config.toolRegistry ??
100
+ new DefaultToolRegistry({
101
+ defaultTimeoutMs: config.toolTimeoutMs,
102
+ });
91
103
  this.contextManager = config.contextManager;
92
104
  this.autoContextManagement =
93
105
  config.autoContextManagement ?? config.contextManager !== undefined;
94
106
  this.onEvent = config.onEvent;
107
+ this.onIterationLimitReached = config.onIterationLimitReached;
95
108
  // State management
96
109
  this.checkpointer = config.checkpointer;
97
110
  this._sessionId = config.sessionId ?? generateSessionId();
@@ -156,8 +169,18 @@ export class Agent {
156
169
  }
157
170
  });
158
171
  }
159
- // Hooks manager
160
- if (config.hooks !== undefined) {
172
+ // File tracking for context restoration hints
173
+ if (config.enableFileTracking && config.contextManager) {
174
+ this.fileTracker = new FileAccessTracker();
175
+ const trackingHook = createFileTrackingHook(this.fileTracker);
176
+ // Merge with existing hooks or create new hooks config
177
+ const hooksConfig = config.hooks ?? {};
178
+ hooksConfig.afterTool = hooksConfig.afterTool ?? [];
179
+ hooksConfig.afterTool.push(trackingHook);
180
+ this.hooksManager = new HooksManager({ hooks: hooksConfig });
181
+ }
182
+ else if (config.hooks !== undefined) {
183
+ // Hooks manager without file tracking
161
184
  this.hooksManager = new HooksManager({ hooks: config.hooks });
162
185
  }
163
186
  }
@@ -684,6 +707,17 @@ export class Agent {
684
707
  getHistory() {
685
708
  return [...this.conversationHistory];
686
709
  }
710
+ /**
711
+ * Set the conversation history (for manual compaction/restoration)
712
+ * Also updates the context manager's token count if configured.
713
+ */
714
+ async setHistory(messages) {
715
+ this.conversationHistory = [...messages];
716
+ if (this.contextManager) {
717
+ await this.contextManager.updateTokenCount(messages);
718
+ }
719
+ return this;
720
+ }
687
721
  /**
688
722
  * Get the context manager (if configured)
689
723
  */
@@ -702,6 +736,173 @@ export class Agent {
702
736
  getVerbosityLevel() {
703
737
  return this.contextManager?.getVerbosityLevel() ?? 'full';
704
738
  }
739
+ /**
740
+ * Get the file access tracker (if file tracking is enabled)
741
+ */
742
+ getFileTracker() {
743
+ return this.fileTracker;
744
+ }
745
+ /**
746
+ * Format context restoration hints based on tracked file accesses.
747
+ * Returns empty string if no files have been accessed or file tracking is disabled.
748
+ */
749
+ formatRestorationHints() {
750
+ return (this.fileTracker?.formatRestorationHints({
751
+ verbosityLevel: this.getVerbosityLevel(),
752
+ }) ?? '');
753
+ }
754
+ /**
755
+ * Inject context restoration hints into messages after compaction/summarization.
756
+ * Modifies messages array in place if hints are available.
757
+ *
758
+ * @internal
759
+ */
760
+ injectRestorationHints(messages) {
761
+ if (!this.fileTracker || this.fileTracker.size === 0) {
762
+ return;
763
+ }
764
+ const hints = this.formatRestorationHints();
765
+ if (!hints) {
766
+ return;
767
+ }
768
+ // Inject as a user message after the last system message
769
+ const systemIndex = messages.findIndex((m) => m.role === 'system');
770
+ const insertIndex = systemIndex >= 0 ? systemIndex + 1 : 0;
771
+ messages.splice(insertIndex, 0, {
772
+ role: 'user',
773
+ content: hints,
774
+ });
775
+ }
776
+ // ==========================================================================
777
+ // Context Compaction
778
+ // ==========================================================================
779
+ /**
780
+ * Compact the conversation context to reduce token usage.
781
+ *
782
+ * This is the recommended way to trigger context compaction externally.
783
+ * It handles:
784
+ * 1. Summarizing older messages
785
+ * 2. Repairing tool use/result pairing (prevents API errors)
786
+ * 3. Injecting context restoration hints (if file tracking is enabled)
787
+ * 4. Updating the conversation history
788
+ *
789
+ * @param options - Compaction options
790
+ * @returns Compaction result with statistics
791
+ *
792
+ * @example
793
+ * ```typescript
794
+ * // Basic compaction
795
+ * const result = await agent.compact();
796
+ * console.log(`Reduced from ${result.originalTokens} to ${result.summaryTokens} tokens`);
797
+ *
798
+ * // Compaction without restoration hints
799
+ * await agent.compact({ injectRestorationHints: false });
800
+ *
801
+ * // Emergency compaction (more aggressive)
802
+ * await agent.compact({ emergency: true });
803
+ * ```
804
+ */
805
+ async compact(options) {
806
+ // Check if context manager is available
807
+ if (!this.contextManager) {
808
+ return {
809
+ success: false,
810
+ originalTokens: 0,
811
+ summaryTokens: 0,
812
+ rounds: 0,
813
+ messagesPreserved: this.conversationHistory.length,
814
+ restorationHintsInjected: false,
815
+ toolResultsRepaired: 0,
816
+ summary: '',
817
+ };
818
+ }
819
+ const shouldInjectHints = options?.injectRestorationHints !== false && this.fileTracker;
820
+ const useSmartCompaction = options?.useSmartCompaction !== false; // Default to true
821
+ // Build full message list including system prompt
822
+ const messages = this.systemPrompt
823
+ ? [{ role: 'system', content: this.systemPrompt }, ...this.conversationHistory]
824
+ : [...this.conversationHistory];
825
+ let summarized;
826
+ let originalTokens;
827
+ let summaryTokens;
828
+ let rounds;
829
+ let messagesPreserved;
830
+ let summary;
831
+ let filesCreated;
832
+ let categoryStats;
833
+ if (useSmartCompaction) {
834
+ // Use smart category-aware compaction
835
+ const { messages: compactedMessages, result: smartResult } = await this.contextManager.smartCompact(messages, {
836
+ generateSummary: (msgs) => this.generateSummary(msgs),
837
+ saveToFile: async (content, index) => {
838
+ // Save to a temp file and return the path
839
+ const path = `/tmp/compacted-tool-result-${String(Date.now())}-${String(index)}.txt`;
840
+ const { writeFile } = await import('node:fs/promises');
841
+ await writeFile(path, content, 'utf-8');
842
+ return path;
843
+ },
844
+ emergency: options?.emergency,
845
+ targetUtilization: options?.targetUtilization,
846
+ });
847
+ summarized = compactedMessages;
848
+ originalTokens = smartResult.tokensBefore;
849
+ summaryTokens = smartResult.tokensAfter;
850
+ rounds = smartResult.summarizationRounds;
851
+ summary = smartResult.summary || '';
852
+ filesCreated = smartResult.filesCreated;
853
+ categoryStats = smartResult.categoryStats;
854
+ // Count preserved messages (system + recent)
855
+ messagesPreserved = compactedMessages.filter((m) => m.role === 'system' || categoryStats?.recentMessages.action === 'preserved').length;
856
+ }
857
+ else {
858
+ // Use simple summarization (legacy behavior)
859
+ const { messages: summarizedMessages, result } = await this.contextManager.summarizeWithRetry(messages, (msgs) => this.generateSummary(msgs), {
860
+ emergency: options.emergency,
861
+ targetUtilization: options.targetUtilization,
862
+ });
863
+ summarized = summarizedMessages;
864
+ originalTokens = result.originalTokens;
865
+ summaryTokens = result.summaryTokens;
866
+ rounds = result.rounds;
867
+ messagesPreserved = result.messagesPreserved;
868
+ summary = result.summary;
869
+ }
870
+ // Repair tool pairing issues (fixes Gemini API errors)
871
+ const repaired = repairToolPairing(summarized);
872
+ const toolResultsRepaired = summarized.length - repaired.length;
873
+ // Inject restoration hints if enabled
874
+ let restorationHintsInjected = false;
875
+ if (shouldInjectHints) {
876
+ const sizeBefore = repaired.length;
877
+ this.injectRestorationHints(repaired);
878
+ restorationHintsInjected = repaired.length > sizeBefore;
879
+ }
880
+ // Extract new history (skip system message)
881
+ const newHistory = repaired.filter((m) => m.role !== 'system');
882
+ // Update conversation history
883
+ this.conversationHistory = newHistory;
884
+ // Update context manager's token count
885
+ await this.contextManager.updateTokenCount(repaired);
886
+ // Emit event
887
+ this.onEvent?.({
888
+ type: 'context_summarized',
889
+ tokensBefore: originalTokens,
890
+ tokensAfter: summaryTokens,
891
+ rounds,
892
+ });
893
+ return {
894
+ success: true,
895
+ originalTokens,
896
+ summaryTokens,
897
+ rounds,
898
+ messagesPreserved,
899
+ restorationHintsInjected,
900
+ toolResultsRepaired,
901
+ summary,
902
+ filesCreated,
903
+ categoryStats,
904
+ };
905
+ }
705
906
  // ==========================================================================
706
907
  // State Management
707
908
  // ==========================================================================
@@ -902,8 +1103,9 @@ export class Agent {
902
1103
  maxContextTokens: subAgentMaxTokens,
903
1104
  },
904
1105
  });
905
- // Create tool registry for sub-agent
906
- const subAgentToolRegistry = new DefaultToolRegistry();
1106
+ // Create tool registry for sub-agent, inheriting timeout settings from parent
1107
+ const parentRegistryOptions = this.toolRegistry.getOptions();
1108
+ const subAgentToolRegistry = new DefaultToolRegistry(parentRegistryOptions);
907
1109
  // If tools specified, use those; otherwise inherit from parent
908
1110
  const toolsToRegister = config.tools ??
909
1111
  this.toolRegistry
@@ -1177,7 +1379,7 @@ export class Agent {
1177
1379
  * Run the agent with a user message
1178
1380
  */
1179
1381
  async run(userMessage, options) {
1180
- const maxIterations = options?.maxIterations ?? this.maxIterations;
1382
+ let maxIterations = options?.maxIterations ?? this.maxIterations;
1181
1383
  const chatOptions = { ...this.chatOptions, ...options?.chatOptions };
1182
1384
  const signal = options?.signal;
1183
1385
  // Combined event emitter
@@ -1232,6 +1434,8 @@ export class Agent {
1232
1434
  // Perform emergency summarization
1233
1435
  const { messages: summarized, result } = await this.contextManager.summarizeWithRetry(messages, (msgs) => this.generateSummary(msgs), { emergency: true });
1234
1436
  messages = summarized;
1437
+ // Inject restoration hints after summarization
1438
+ this.injectRestorationHints(messages);
1235
1439
  emit({
1236
1440
  type: 'context_summarized',
1237
1441
  tokensBefore: result.originalTokens,
@@ -1290,6 +1494,11 @@ export class Agent {
1290
1494
  }
1291
1495
  // Get tool definitions
1292
1496
  let tools = this.toolRegistry.getDefinitions();
1497
+ // Apply tool filter if specified (reduces token usage)
1498
+ if (options?.toolFilter && options.toolFilter.length > 0) {
1499
+ const filterSet = new Set(options.toolFilter);
1500
+ tools = tools.filter((tool) => filterSet.has(tool.name));
1501
+ }
1293
1502
  // Run beforeLLM hooks (can modify messages and tools)
1294
1503
  if (this.hooksManager) {
1295
1504
  const llmHookResult = await this.hooksManager.runBeforeLLM({
@@ -1374,12 +1583,15 @@ export class Agent {
1374
1583
  // If no tool uses, we're done
1375
1584
  if (toolUses.length === 0) {
1376
1585
  finalResponse = text;
1377
- // Add final assistant response to history
1378
- const finalAssistantMsg = {
1379
- role: 'assistant',
1380
- content: text,
1381
- };
1382
- newMessages.push(finalAssistantMsg);
1586
+ // Add final assistant response to history (only if non-empty)
1587
+ // Empty responses can occur after silent tools like 'suggest'
1588
+ if (text) {
1589
+ const finalAssistantMsg = {
1590
+ role: 'assistant',
1591
+ content: text,
1592
+ };
1593
+ newMessages.push(finalAssistantMsg);
1594
+ }
1383
1595
  // Run afterIteration hooks
1384
1596
  if (this.hooksManager) {
1385
1597
  await this.hooksManager.runAfterIteration({
@@ -1434,11 +1646,26 @@ export class Agent {
1434
1646
  success: false,
1435
1647
  error: `Permission denied: ${permResult.reason ?? 'Tool execution not allowed'}`,
1436
1648
  };
1437
- // Skip to tool_end and continue to next tool
1649
+ // Emit tool_end and record the tool call
1438
1650
  emit({ type: 'tool_end', name: toolUse.name, result });
1439
1651
  const toolCallEntry = { name: toolUse.name, input: toolUse.input, result };
1440
1652
  toolCalls.push(toolCallEntry);
1441
1653
  iterationToolCalls.push(toolCallEntry);
1654
+ // CRITICAL: Add tool_result message to messages array
1655
+ // Claude API requires every tool_use to have a corresponding tool_result
1656
+ const toolResultMsg = {
1657
+ role: 'user',
1658
+ content: [
1659
+ {
1660
+ type: 'tool_result',
1661
+ toolUseId: toolUse.id,
1662
+ content: `Error: ${result.error ?? 'Permission denied'}`,
1663
+ isError: true,
1664
+ },
1665
+ ],
1666
+ };
1667
+ messages.push(toolResultMsg);
1668
+ newMessages.push(toolResultMsg);
1442
1669
  continue;
1443
1670
  }
1444
1671
  emit({ type: 'permission_granted', toolName: toolUse.name, level: permResult.level });
@@ -1456,11 +1683,26 @@ export class Agent {
1456
1683
  success: false,
1457
1684
  error: `Guardrail blocked: ${message}`,
1458
1685
  };
1459
- // Skip to tool_end and continue to next tool
1686
+ // Emit tool_end and record the tool call
1460
1687
  emit({ type: 'tool_end', name: toolUse.name, result });
1461
1688
  const toolCallEntry = { name: toolUse.name, input: toolUse.input, result };
1462
1689
  toolCalls.push(toolCallEntry);
1463
1690
  iterationToolCalls.push(toolCallEntry);
1691
+ // CRITICAL: Add tool_result message to messages array
1692
+ // Claude API requires every tool_use to have a corresponding tool_result
1693
+ const toolResultMsg = {
1694
+ role: 'user',
1695
+ content: [
1696
+ {
1697
+ type: 'tool_result',
1698
+ toolUseId: toolUse.id,
1699
+ content: `Error: ${result.error ?? 'Blocked by guardrail'}`,
1700
+ isError: true,
1701
+ },
1702
+ ],
1703
+ };
1704
+ messages.push(toolResultMsg);
1705
+ newMessages.push(toolResultMsg);
1464
1706
  continue;
1465
1707
  }
1466
1708
  else if (guardrailResult.action === 'warn') {
@@ -1586,6 +1828,8 @@ export class Agent {
1586
1828
  const tokensBefore = this.contextManager.getTokenCount();
1587
1829
  const compactResult = await this.contextManager.compactCategory(messages, 'toolResults', (content, index) => Promise.resolve(`[compacted_tool_result_${String(index)}]`));
1588
1830
  messages = compactResult.messages;
1831
+ // Inject restoration hints after compaction
1832
+ this.injectRestorationHints(messages);
1589
1833
  emit({
1590
1834
  type: 'context_compacted',
1591
1835
  tokensBefore,
@@ -1596,6 +1840,8 @@ export class Agent {
1596
1840
  // Approaching context limit - summarize
1597
1841
  const { messages: summarized, result: sumResult } = await this.contextManager.summarizeWithRetry(messages, (msgs) => this.generateSummary(msgs));
1598
1842
  messages = summarized;
1843
+ // Inject restoration hints after summarization
1844
+ this.injectRestorationHints(messages);
1599
1845
  emit({
1600
1846
  type: 'context_summarized',
1601
1847
  tokensBefore: sumResult.originalTokens,
@@ -1636,6 +1882,30 @@ export class Agent {
1636
1882
  });
1637
1883
  }
1638
1884
  emit({ type: 'iteration_end', iteration: iterations });
1885
+ // Check if we're about to hit the iteration limit
1886
+ // If callback is defined, ask if we should continue
1887
+ if (iterations >= maxIterations && this.onIterationLimitReached) {
1888
+ emit({
1889
+ type: 'iteration_limit_reached',
1890
+ iteration: iterations,
1891
+ maxIterations,
1892
+ });
1893
+ const result = await this.onIterationLimitReached({
1894
+ iteration: iterations,
1895
+ maxIterations,
1896
+ toolCallCount: toolCalls.length,
1897
+ });
1898
+ if (typeof result === 'number' && result > 0) {
1899
+ // Extend the limit and continue
1900
+ maxIterations += result;
1901
+ emit({
1902
+ type: 'iteration_limit_extended',
1903
+ newMaxIterations: maxIterations,
1904
+ addedIterations: result,
1905
+ });
1906
+ }
1907
+ // If false or 0, loop will exit naturally on next condition check
1908
+ }
1639
1909
  }
1640
1910
  // Check if we hit max iterations without completing
1641
1911
  if (!aborted && iterations >= maxIterations && finalResponse === '') {
@@ -0,0 +1,156 @@
1
+ /**
2
+ * File Access Tracker
3
+ *
4
+ * Tracks file accesses during agent sessions to provide context restoration
5
+ * hints after compaction. Inspired by Claude Code's post-compaction file
6
+ * reference display.
7
+ *
8
+ * Features:
9
+ * - Track read, referenced, and modified files
10
+ * - Deduplication (read supersedes reference)
11
+ * - Max entries with LRU eviction
12
+ * - Verbosity-aware formatting
13
+ *
14
+ * @see /workspace/project-docs/00-requirements/compilr-dev-agents/context-restoration-hints.md
15
+ */
16
+ import type { VerbosityLevel } from './types.js';
17
+ /**
18
+ * Type of file access
19
+ */
20
+ export type FileAccessType = 'read' | 'referenced' | 'modified';
21
+ /**
22
+ * Record of a file access
23
+ */
24
+ export interface FileAccess {
25
+ /** Absolute file path */
26
+ path: string;
27
+ /** Type of access */
28
+ type: FileAccessType;
29
+ /** When accessed (timestamp) */
30
+ timestamp: number;
31
+ /** Number of lines (for 'read' type) */
32
+ lineCount?: number;
33
+ /** Optional summary of what was found/changed */
34
+ summary?: string;
35
+ }
36
+ /**
37
+ * Options for FileAccessTracker constructor
38
+ */
39
+ export interface FileAccessTrackerOptions {
40
+ /**
41
+ * Maximum number of entries to track
42
+ * @default 100
43
+ */
44
+ maxEntries?: number;
45
+ /**
46
+ * Deduplicate references (don't track same file twice as 'referenced')
47
+ * @default true
48
+ */
49
+ deduplicateReferences?: boolean;
50
+ }
51
+ /**
52
+ * Options for formatting restoration hints
53
+ */
54
+ export interface FormatHintsOptions {
55
+ /** Include line counts for read files @default true */
56
+ includeLineCount?: boolean;
57
+ /** Include summaries @default false */
58
+ includeSummary?: boolean;
59
+ /** Include timestamps @default false */
60
+ includeTimestamp?: boolean;
61
+ /** Group by access type @default true */
62
+ groupByType?: boolean;
63
+ /** Maximum files to include @default 20 */
64
+ maxFiles?: number;
65
+ /** Verbosity level (adjusts format automatically) */
66
+ verbosityLevel?: VerbosityLevel;
67
+ }
68
+ /**
69
+ * Statistics about file accesses
70
+ */
71
+ export interface FileAccessStats {
72
+ read: number;
73
+ referenced: number;
74
+ modified: number;
75
+ total: number;
76
+ }
77
+ /**
78
+ * Tracks file accesses during agent sessions for context restoration hints.
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * const tracker = new FileAccessTracker();
83
+ *
84
+ * // Track file accesses
85
+ * tracker.trackRead('/path/to/file.ts', 597);
86
+ * tracker.trackReference('/path/to/other.ts');
87
+ * tracker.trackModification('/path/to/file.ts', 'Added function');
88
+ *
89
+ * // Get restoration hints after compaction
90
+ * const hints = tracker.formatRestorationHints();
91
+ * console.log(hints);
92
+ * ```
93
+ */
94
+ export declare class FileAccessTracker {
95
+ private readonly accesses;
96
+ private readonly maxEntries;
97
+ private readonly deduplicateReferences;
98
+ constructor(options?: FileAccessTrackerOptions);
99
+ /**
100
+ * Track a file that was fully read
101
+ */
102
+ trackRead(filePath: string, lineCount: number, summary?: string): void;
103
+ /**
104
+ * Track a file that was referenced (e.g., appeared in grep/glob results)
105
+ */
106
+ trackReference(filePath: string): void;
107
+ /**
108
+ * Track a file that was modified (written or edited)
109
+ */
110
+ trackModification(filePath: string, summary?: string): void;
111
+ /**
112
+ * Get all file accesses, optionally filtered
113
+ */
114
+ getAccesses(options?: {
115
+ type?: FileAccessType;
116
+ since?: number;
117
+ }): FileAccess[];
118
+ /**
119
+ * Get most recent file accesses
120
+ */
121
+ getRecentAccesses(limit?: number): FileAccess[];
122
+ /**
123
+ * Check if a file has been accessed
124
+ */
125
+ hasAccessed(filePath: string): boolean;
126
+ /**
127
+ * Get access record for a specific file
128
+ */
129
+ getAccess(filePath: string): FileAccess | undefined;
130
+ /**
131
+ * Get statistics about file accesses
132
+ */
133
+ getStats(): FileAccessStats;
134
+ /**
135
+ * Format restoration hints for injection after compaction
136
+ */
137
+ formatRestorationHints(options?: FormatHintsOptions): string;
138
+ /**
139
+ * Clear all tracked accesses
140
+ */
141
+ clear(): void;
142
+ /**
143
+ * Get the number of tracked files
144
+ */
145
+ get size(): number;
146
+ private normalizePath;
147
+ private enforceMaxEntries;
148
+ private getEffectiveMaxFiles;
149
+ private formatCompact;
150
+ private formatGrouped;
151
+ private formatFlat;
152
+ private formatAccessLine;
153
+ private groupByType;
154
+ private getDisplayName;
155
+ private getDisplayPath;
156
+ }