@tuanhung303/opencode-acp 2.1.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 (167) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +288 -0
  3. package/dist/index.d.ts +4 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +71 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/lib/commands/context.d.ts +49 -0
  8. package/dist/lib/commands/context.d.ts.map +1 -0
  9. package/dist/lib/commands/context.js +191 -0
  10. package/dist/lib/commands/context.js.map +1 -0
  11. package/dist/lib/commands/help.d.ts +15 -0
  12. package/dist/lib/commands/help.d.ts.map +1 -0
  13. package/dist/lib/commands/help.js +26 -0
  14. package/dist/lib/commands/help.js.map +1 -0
  15. package/dist/lib/commands/stats.d.ts +15 -0
  16. package/dist/lib/commands/stats.d.ts.map +1 -0
  17. package/dist/lib/commands/stats.js +44 -0
  18. package/dist/lib/commands/stats.js.map +1 -0
  19. package/dist/lib/commands/sweep.d.ts +23 -0
  20. package/dist/lib/commands/sweep.d.ts.map +1 -0
  21. package/dist/lib/commands/sweep.js +191 -0
  22. package/dist/lib/commands/sweep.js.map +1 -0
  23. package/dist/lib/config.d.ts +82 -0
  24. package/dist/lib/config.d.ts.map +1 -0
  25. package/dist/lib/config.js +864 -0
  26. package/dist/lib/config.js.map +1 -0
  27. package/dist/lib/hooks.d.ts +17 -0
  28. package/dist/lib/hooks.d.ts.map +1 -0
  29. package/dist/lib/hooks.js +123 -0
  30. package/dist/lib/hooks.js.map +1 -0
  31. package/dist/lib/logger.d.ts +31 -0
  32. package/dist/lib/logger.d.ts.map +1 -0
  33. package/dist/lib/logger.js +189 -0
  34. package/dist/lib/logger.js.map +1 -0
  35. package/dist/lib/messages/index.d.ts +3 -0
  36. package/dist/lib/messages/index.d.ts.map +1 -0
  37. package/dist/lib/messages/index.js +3 -0
  38. package/dist/lib/messages/index.js.map +1 -0
  39. package/dist/lib/messages/inject.d.ts +5 -0
  40. package/dist/lib/messages/inject.d.ts.map +1 -0
  41. package/dist/lib/messages/inject.js +127 -0
  42. package/dist/lib/messages/inject.js.map +1 -0
  43. package/dist/lib/messages/prune.d.ts +5 -0
  44. package/dist/lib/messages/prune.d.ts.map +1 -0
  45. package/dist/lib/messages/prune.js +86 -0
  46. package/dist/lib/messages/prune.js.map +1 -0
  47. package/dist/lib/messages/utils.d.ts +31 -0
  48. package/dist/lib/messages/utils.d.ts.map +1 -0
  49. package/dist/lib/messages/utils.js +228 -0
  50. package/dist/lib/messages/utils.js.map +1 -0
  51. package/dist/lib/prompts/discard-tool-spec.d.ts +2 -0
  52. package/dist/lib/prompts/discard-tool-spec.d.ts.map +1 -0
  53. package/dist/lib/prompts/discard-tool-spec.js +41 -0
  54. package/dist/lib/prompts/discard-tool-spec.js.map +1 -0
  55. package/dist/lib/prompts/extract-tool-spec.d.ts +2 -0
  56. package/dist/lib/prompts/extract-tool-spec.d.ts.map +1 -0
  57. package/dist/lib/prompts/extract-tool-spec.js +48 -0
  58. package/dist/lib/prompts/extract-tool-spec.js.map +1 -0
  59. package/dist/lib/prompts/index.d.ts +2 -0
  60. package/dist/lib/prompts/index.d.ts.map +1 -0
  61. package/dist/lib/prompts/index.js +34 -0
  62. package/dist/lib/prompts/index.js.map +1 -0
  63. package/dist/lib/prompts/nudge/both.d.ts +2 -0
  64. package/dist/lib/prompts/nudge/both.d.ts.map +1 -0
  65. package/dist/lib/prompts/nudge/both.js +11 -0
  66. package/dist/lib/prompts/nudge/both.js.map +1 -0
  67. package/dist/lib/prompts/nudge/discard.d.ts +2 -0
  68. package/dist/lib/prompts/nudge/discard.d.ts.map +1 -0
  69. package/dist/lib/prompts/nudge/discard.js +10 -0
  70. package/dist/lib/prompts/nudge/discard.js.map +1 -0
  71. package/dist/lib/prompts/nudge/extract.d.ts +2 -0
  72. package/dist/lib/prompts/nudge/extract.d.ts.map +1 -0
  73. package/dist/lib/prompts/nudge/extract.js +10 -0
  74. package/dist/lib/prompts/nudge/extract.js.map +1 -0
  75. package/dist/lib/prompts/system/both.d.ts +2 -0
  76. package/dist/lib/prompts/system/both.d.ts.map +1 -0
  77. package/dist/lib/prompts/system/both.js +61 -0
  78. package/dist/lib/prompts/system/both.js.map +1 -0
  79. package/dist/lib/prompts/system/discard.d.ts +2 -0
  80. package/dist/lib/prompts/system/discard.d.ts.map +1 -0
  81. package/dist/lib/prompts/system/discard.js +52 -0
  82. package/dist/lib/prompts/system/discard.js.map +1 -0
  83. package/dist/lib/prompts/system/extract.d.ts +2 -0
  84. package/dist/lib/prompts/system/extract.d.ts.map +1 -0
  85. package/dist/lib/prompts/system/extract.js +52 -0
  86. package/dist/lib/prompts/system/extract.js.map +1 -0
  87. package/dist/lib/protected-file-patterns.d.ts +12 -0
  88. package/dist/lib/protected-file-patterns.d.ts.map +1 -0
  89. package/dist/lib/protected-file-patterns.js +69 -0
  90. package/dist/lib/protected-file-patterns.js.map +1 -0
  91. package/dist/lib/shared-utils.d.ts +4 -0
  92. package/dist/lib/shared-utils.d.ts.map +1 -0
  93. package/dist/lib/shared-utils.js +14 -0
  94. package/dist/lib/shared-utils.js.map +1 -0
  95. package/dist/lib/state/index.d.ts +4 -0
  96. package/dist/lib/state/index.d.ts.map +1 -0
  97. package/dist/lib/state/index.js +4 -0
  98. package/dist/lib/state/index.js.map +1 -0
  99. package/dist/lib/state/persistence.d.ts +22 -0
  100. package/dist/lib/state/persistence.d.ts.map +1 -0
  101. package/dist/lib/state/persistence.js +107 -0
  102. package/dist/lib/state/persistence.js.map +1 -0
  103. package/dist/lib/state/state.d.ts +8 -0
  104. package/dist/lib/state/state.d.ts.map +1 -0
  105. package/dist/lib/state/state.js +115 -0
  106. package/dist/lib/state/state.js.map +1 -0
  107. package/dist/lib/state/tool-cache.d.ts +13 -0
  108. package/dist/lib/state/tool-cache.d.ts.map +1 -0
  109. package/dist/lib/state/tool-cache.js +77 -0
  110. package/dist/lib/state/tool-cache.js.map +1 -0
  111. package/dist/lib/state/types.d.ts +38 -0
  112. package/dist/lib/state/types.d.ts.map +1 -0
  113. package/dist/lib/state/types.js +2 -0
  114. package/dist/lib/state/types.js.map +1 -0
  115. package/dist/lib/state/utils.d.ts +2 -0
  116. package/dist/lib/state/utils.d.ts.map +1 -0
  117. package/dist/lib/state/utils.js +10 -0
  118. package/dist/lib/state/utils.js.map +1 -0
  119. package/dist/lib/strategies/deduplication.d.ts +10 -0
  120. package/dist/lib/strategies/deduplication.d.ts.map +1 -0
  121. package/dist/lib/strategies/deduplication.js +94 -0
  122. package/dist/lib/strategies/deduplication.js.map +1 -0
  123. package/dist/lib/strategies/head-tail-truncation.d.ts +15 -0
  124. package/dist/lib/strategies/head-tail-truncation.d.ts.map +1 -0
  125. package/dist/lib/strategies/head-tail-truncation.js +144 -0
  126. package/dist/lib/strategies/head-tail-truncation.js.map +1 -0
  127. package/dist/lib/strategies/index.d.ts +9 -0
  128. package/dist/lib/strategies/index.d.ts.map +1 -0
  129. package/dist/lib/strategies/index.js +9 -0
  130. package/dist/lib/strategies/index.js.map +1 -0
  131. package/dist/lib/strategies/placeholder-compression.d.ts +5 -0
  132. package/dist/lib/strategies/placeholder-compression.d.ts.map +1 -0
  133. package/dist/lib/strategies/placeholder-compression.js +148 -0
  134. package/dist/lib/strategies/placeholder-compression.js.map +1 -0
  135. package/dist/lib/strategies/prune-thinking.d.ts +15 -0
  136. package/dist/lib/strategies/prune-thinking.d.ts.map +1 -0
  137. package/dist/lib/strategies/prune-thinking.js +79 -0
  138. package/dist/lib/strategies/prune-thinking.js.map +1 -0
  139. package/dist/lib/strategies/purge-errors.d.ts +13 -0
  140. package/dist/lib/strategies/purge-errors.d.ts.map +1 -0
  141. package/dist/lib/strategies/purge-errors.js +59 -0
  142. package/dist/lib/strategies/purge-errors.js.map +1 -0
  143. package/dist/lib/strategies/read-consolidation.d.ts +21 -0
  144. package/dist/lib/strategies/read-consolidation.d.ts.map +1 -0
  145. package/dist/lib/strategies/read-consolidation.js +155 -0
  146. package/dist/lib/strategies/read-consolidation.js.map +1 -0
  147. package/dist/lib/strategies/supersede-writes.d.ts +13 -0
  148. package/dist/lib/strategies/supersede-writes.d.ts.map +1 -0
  149. package/dist/lib/strategies/supersede-writes.js +84 -0
  150. package/dist/lib/strategies/supersede-writes.js.map +1 -0
  151. package/dist/lib/strategies/tools.d.ts +14 -0
  152. package/dist/lib/strategies/tools.d.ts.map +1 -0
  153. package/dist/lib/strategies/tools.js +135 -0
  154. package/dist/lib/strategies/tools.js.map +1 -0
  155. package/dist/lib/strategies/utils.d.ts +11 -0
  156. package/dist/lib/strategies/utils.d.ts.map +1 -0
  157. package/dist/lib/strategies/utils.js +75 -0
  158. package/dist/lib/strategies/utils.js.map +1 -0
  159. package/dist/lib/ui/notification.d.ts +9 -0
  160. package/dist/lib/ui/notification.d.ts.map +1 -0
  161. package/dist/lib/ui/notification.js +77 -0
  162. package/dist/lib/ui/notification.js.map +1 -0
  163. package/dist/lib/ui/utils.d.ts +10 -0
  164. package/dist/lib/ui/utils.d.ts.map +1 -0
  165. package/dist/lib/ui/utils.js +87 -0
  166. package/dist/lib/ui/utils.js.map +1 -0
  167. package/package.json +67 -0
@@ -0,0 +1,148 @@
1
+ import { isMessageCompacted } from "../shared-utils";
2
+ import { countTokens } from "./utils";
3
+ /**
4
+ * Placeholder Compression Strategy
5
+ *
6
+ * Replaces verbose tool outputs with actionable placeholder hints while preserving
7
+ * the tool call structure (name + input) as breadcrumbs for the agent.
8
+ *
9
+ * This allows the agent to see what actions were taken and re-execute if needed,
10
+ * while dramatically reducing context size.
11
+ *
12
+ * Based on: https://www.hadijaveed.me/2025/11/26/escaping-context-amnesia-ai-agents/
13
+ */
14
+ // Tool-specific placeholder templates
15
+ const PLACEHOLDER_TEMPLATES = {
16
+ read: (input) => `[File read previously. Read again if needed: ${input?.filePath || input?.path || 'unknown'}]`,
17
+ glob: (input) => `[Glob search completed for pattern: ${input?.pattern || 'unknown'}. Search again if needed]`,
18
+ grep: (input) => `[Content search completed for: ${input?.pattern || 'unknown'}. Search again if needed]`,
19
+ bash: (input) => `[Command executed: ${truncate(input?.command || input?.description || 'unknown', 80)}. Re-run if needed]`,
20
+ webfetch: (input) => `[URL fetched: ${truncate(input?.url || 'unknown', 60)}. Fetch again if needed]`,
21
+ "ddg-search_search": (input) => `[Search completed for: ${input?.query || 'unknown'}. Search again for current results]`,
22
+ "ddg-search_fetch_content": (input) => `[Content fetched from: ${truncate(input?.url || 'unknown', 60)}]`,
23
+ "google_search": (input) => `[Google search completed for: ${input?.query || 'unknown'}. Search again if needed]`,
24
+ "context7_query-docs": (input) => `[Docs queried for: ${input?.query || 'unknown'}. Query again if needed]`,
25
+ "context7_resolve-library-id": (input) => `[Library resolved: ${input?.libraryName || 'unknown'}. Resolve again if needed]`,
26
+ "excel_read_data_from_excel": (input) => `[Excel data read from: ${input?.filepath || 'unknown'}. Read again if needed]`,
27
+ "pdf-reader_read_pdf": () => `[PDF content read. Read again if needed]`,
28
+ "url-context-mcp_analyze_urls": (input) => `[URLs analyzed. Analyze again if needed: ${truncate(JSON.stringify(input?.urls || []), 60)}]`,
29
+ "url-context-mcp_google_search": (input) => `[Search completed for: ${input?.query || 'unknown'}. Search again if needed]`,
30
+ list: (input) => `[Directory listed: ${input?.path || 'current'}. List again if needed]`,
31
+ codesearch: (input) => `[Code search completed for: ${input?.query || 'unknown'}. Search again if needed]`,
32
+ websearch: (input) => `[Web search completed for: ${input?.query || 'unknown'}. Search again if needed]`,
33
+ };
34
+ // Default placeholder for tools not in the template list
35
+ const DEFAULT_PLACEHOLDER = (toolName, input) => {
36
+ const inputHint = input ? ` with: ${truncate(JSON.stringify(input), 80)}` : '';
37
+ return `[${toolName} executed${inputHint}. Re-run if needed]`;
38
+ };
39
+ // Tools that should NEVER be compressed (their output is critical)
40
+ const PROTECTED_TOOLS = new Set([
41
+ 'write',
42
+ 'edit',
43
+ 'todowrite',
44
+ 'todoread',
45
+ 'discard',
46
+ 'extract',
47
+ 'task',
48
+ 'question',
49
+ 'batch',
50
+ 'plan_enter',
51
+ 'plan_exit',
52
+ 'skill',
53
+ ]);
54
+ // Tools whose output should be preserved for schema/structure understanding
55
+ const SCHEMA_TOOLS = new Set([
56
+ 'excel_get_workbook_metadata',
57
+ 'excel_get_data_validation_info',
58
+ 'word-document-server_get_document_outline',
59
+ 'word-document-server_get_document_info',
60
+ ]);
61
+ function truncate(str, maxLen) {
62
+ if (!str)
63
+ return 'unknown';
64
+ if (str.length <= maxLen)
65
+ return str;
66
+ return str.slice(0, maxLen - 3) + '...';
67
+ }
68
+ export const placeholderCompression = (state, logger, config, messages) => {
69
+ const compressionConfig = config.strategies.placeholderCompression;
70
+ if (!compressionConfig?.enabled) {
71
+ return;
72
+ }
73
+ const delayTurns = compressionConfig.delayTurns ?? 2;
74
+ const minOutputTokens = compressionConfig.minOutputTokens ?? 100;
75
+ const additionalProtected = new Set(compressionConfig.protectedTools ?? []);
76
+ let compressedCount = 0;
77
+ let tokensSaved = 0;
78
+ // Calculate current turn from messages
79
+ const totalMessages = messages.length;
80
+ for (let i = 0; i < messages.length; i++) {
81
+ const msg = messages[i];
82
+ // Skip compacted messages
83
+ if (isMessageCompacted(state, msg)) {
84
+ continue;
85
+ }
86
+ // Calculate turn age (0 = most recent)
87
+ const turnAge = totalMessages - 1 - i;
88
+ // Skip recent turns to preserve cache and allow agent to reference
89
+ if (turnAge < delayTurns) {
90
+ continue;
91
+ }
92
+ // Process tool parts
93
+ const parts = Array.isArray(msg.parts) ? msg.parts : [];
94
+ for (const part of parts) {
95
+ // Only process tool parts with completed status
96
+ if (part.type !== "tool") {
97
+ continue;
98
+ }
99
+ const toolName = part.tool;
100
+ const toolState = part.state;
101
+ if (!toolName || !toolState)
102
+ continue;
103
+ // Skip if not completed (errors handled by purgeErrors)
104
+ if (toolState.status !== "completed") {
105
+ continue;
106
+ }
107
+ // Skip protected tools
108
+ if (PROTECTED_TOOLS.has(toolName) || additionalProtected.has(toolName)) {
109
+ continue;
110
+ }
111
+ // Skip schema tools
112
+ if (SCHEMA_TOOLS.has(toolName)) {
113
+ continue;
114
+ }
115
+ // Get the output
116
+ const output = toolState.output;
117
+ if (output === undefined || output === null) {
118
+ continue;
119
+ }
120
+ // Calculate output size
121
+ const outputStr = typeof output === 'string' ? output : JSON.stringify(output);
122
+ const outputTokens = countTokens(outputStr);
123
+ // Only compress if output is large enough
124
+ if (outputTokens < minOutputTokens) {
125
+ continue;
126
+ }
127
+ // Generate placeholder
128
+ const template = PLACEHOLDER_TEMPLATES[toolName];
129
+ const placeholder = template
130
+ ? template(toolState.input)
131
+ : DEFAULT_PLACEHOLDER(toolName, toolState.input);
132
+ // Calculate savings
133
+ const placeholderTokens = countTokens(placeholder);
134
+ const saved = outputTokens - placeholderTokens;
135
+ if (saved > 0) {
136
+ // Replace output with placeholder
137
+ toolState.output = placeholder;
138
+ tokensSaved += saved;
139
+ compressedCount++;
140
+ }
141
+ }
142
+ }
143
+ if (compressedCount > 0) {
144
+ state.stats.totalPruneTokens += tokensSaved;
145
+ logger.debug(`Placeholder compression: compressed ${compressedCount} tool outputs (estimated ${tokensSaved} tokens saved)`);
146
+ }
147
+ };
148
+ //# sourceMappingURL=placeholder-compression.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"placeholder-compression.js","sourceRoot":"","sources":["../../../lib/strategies/placeholder-compression.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { PluginConfig } from "../config";
2
+ import { Logger } from "../logger";
3
+ import type { SessionState, WithParts } from "../state";
4
+ /**
5
+ * Prune Thinking strategy - removes extended thinking tokens from assistant messages
6
+ * after they are older than a configurable number of turns.
7
+ *
8
+ * Thinking tokens (Claude's <thinking> blocks, OpenAI's reasoning field) consume
9
+ * significant context but provide no utility after the response is generated.
10
+ *
11
+ * This strategy strips thinking content from older messages to save context space.
12
+ * Recent turns are preserved to maintain cache efficiency.
13
+ */
14
+ export declare const pruneThinking: (state: SessionState, logger: Logger, config: PluginConfig, messages: WithParts[]) => void;
15
+ //# sourceMappingURL=prune-thinking.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prune-thinking.d.ts","sourceRoot":"","sources":["../../../lib/strategies/prune-thinking.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AAClC,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAEvD;;;;;;;;;GASG;AACH,eAAO,MAAM,aAAa,GACtB,OAAO,YAAY,EACnB,QAAQ,MAAM,EACd,QAAQ,YAAY,EACpB,UAAU,SAAS,EAAE,KACtB,IA2EF,CAAA"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Prune Thinking strategy - removes extended thinking tokens from assistant messages
3
+ * after they are older than a configurable number of turns.
4
+ *
5
+ * Thinking tokens (Claude's <thinking> blocks, OpenAI's reasoning field) consume
6
+ * significant context but provide no utility after the response is generated.
7
+ *
8
+ * This strategy strips thinking content from older messages to save context space.
9
+ * Recent turns are preserved to maintain cache efficiency.
10
+ */
11
+ export const pruneThinking = (state, logger, config, messages) => {
12
+ if (!config.strategies.pruneThinking?.enabled) {
13
+ return;
14
+ }
15
+ const delayTurns = config.strategies.pruneThinking.delayTurns ?? 1;
16
+ let prunedCount = 0;
17
+ let tokensSaved = 0;
18
+ for (let i = 0; i < messages.length; i++) {
19
+ const msg = messages[i];
20
+ // Only process assistant messages
21
+ if (msg.info.role !== "assistant") {
22
+ continue;
23
+ }
24
+ // Calculate turn age (messages are in chronological order)
25
+ // Use message index as a proxy for turn age
26
+ const turnAge = messages.length - 1 - i;
27
+ // Skip recent turns to preserve cache
28
+ if (turnAge < delayTurns) {
29
+ continue;
30
+ }
31
+ // Handle array content (parts array)
32
+ if (Array.isArray(msg.parts)) {
33
+ const originalLength = msg.parts.length;
34
+ // Filter out thinking blocks
35
+ msg.parts = msg.parts.filter((part) => {
36
+ // Anthropic thinking block format
37
+ if (part.type === "thinking") {
38
+ tokensSaved += estimateTokens(part.thinking || "");
39
+ return false;
40
+ }
41
+ // Text part that contains thinking tags
42
+ if (part.type === "text" && typeof part.text === "string") {
43
+ const thinkingMatch = part.text.match(/<thinking>[\s\S]*?<\/thinking>/g);
44
+ if (thinkingMatch) {
45
+ for (const match of thinkingMatch) {
46
+ tokensSaved += estimateTokens(match);
47
+ }
48
+ part.text = part.text.replace(/<thinking>[\s\S]*?<\/thinking>/g, "").trim();
49
+ // Remove empty text parts
50
+ if (!part.text) {
51
+ return false;
52
+ }
53
+ }
54
+ }
55
+ return true;
56
+ });
57
+ if (msg.parts.length < originalLength) {
58
+ prunedCount++;
59
+ }
60
+ }
61
+ // Handle OpenAI reasoning field on the info object
62
+ if (msg.info.reasoning) {
63
+ tokensSaved += estimateTokens(msg.info.reasoning);
64
+ delete msg.info.reasoning;
65
+ prunedCount++;
66
+ }
67
+ }
68
+ if (prunedCount > 0) {
69
+ state.stats.totalPruneTokens += tokensSaved;
70
+ logger.debug(`Pruned thinking tokens from ${prunedCount} messages (estimated ${tokensSaved} tokens saved)`);
71
+ }
72
+ };
73
+ /**
74
+ * Rough token estimation (1 token ≈ 4 characters for English text)
75
+ */
76
+ function estimateTokens(text) {
77
+ return Math.ceil(text.length / 4);
78
+ }
79
+ //# sourceMappingURL=prune-thinking.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prune-thinking.js","sourceRoot":"","sources":["../../../lib/strategies/prune-thinking.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { PluginConfig } from "../config";
2
+ import { Logger } from "../logger";
3
+ import type { SessionState, WithParts } from "../state";
4
+ /**
5
+ * Purge Errors strategy - prunes tool inputs for tools that errored
6
+ * after they are older than a configurable number of turns.
7
+ * The error message is preserved, but the (potentially large) inputs
8
+ * are removed to save context.
9
+ *
10
+ * Modifies the session state in place to add pruned tool call IDs.
11
+ */
12
+ export declare const purgeErrors: (state: SessionState, logger: Logger, config: PluginConfig, messages: WithParts[]) => void;
13
+ //# sourceMappingURL=purge-errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"purge-errors.d.ts","sourceRoot":"","sources":["../../../lib/strategies/purge-errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AAClC,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAKvD;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,GACpB,OAAO,YAAY,EACnB,QAAQ,MAAM,EACd,QAAQ,YAAY,EACpB,UAAU,SAAS,EAAE,KACtB,IA2DF,CAAA"}
@@ -0,0 +1,59 @@
1
+ import { buildToolIdList } from "../messages/utils";
2
+ import { getFilePathFromParameters, isProtectedFilePath } from "../protected-file-patterns";
3
+ import { calculateTokensSaved } from "./utils";
4
+ /**
5
+ * Purge Errors strategy - prunes tool inputs for tools that errored
6
+ * after they are older than a configurable number of turns.
7
+ * The error message is preserved, but the (potentially large) inputs
8
+ * are removed to save context.
9
+ *
10
+ * Modifies the session state in place to add pruned tool call IDs.
11
+ */
12
+ export const purgeErrors = (state, logger, config, messages) => {
13
+ if (!config.strategies.purgeErrors.enabled) {
14
+ return;
15
+ }
16
+ // Build list of all tool call IDs from messages (chronological order)
17
+ const allToolIds = buildToolIdList(state, messages, logger);
18
+ if (allToolIds.length === 0) {
19
+ return;
20
+ }
21
+ // Filter out IDs already pruned
22
+ const alreadyPruned = new Set(state.prune.toolIds);
23
+ const unprunedIds = allToolIds.filter((id) => !alreadyPruned.has(id));
24
+ if (unprunedIds.length === 0) {
25
+ return;
26
+ }
27
+ const protectedTools = config.strategies.purgeErrors.protectedTools;
28
+ const turnThreshold = config.strategies.purgeErrors.turns;
29
+ const newPruneIds = [];
30
+ for (const id of unprunedIds) {
31
+ const metadata = state.toolParameters.get(id);
32
+ if (!metadata) {
33
+ continue;
34
+ }
35
+ // Skip protected tools
36
+ if (protectedTools.includes(metadata.tool)) {
37
+ continue;
38
+ }
39
+ const filePath = getFilePathFromParameters(metadata.parameters);
40
+ if (isProtectedFilePath(filePath, config.protectedFilePatterns)) {
41
+ continue;
42
+ }
43
+ // Only process error tools
44
+ if (metadata.status !== "error") {
45
+ continue;
46
+ }
47
+ // Check if the tool is old enough to prune
48
+ const turnAge = state.currentTurn - metadata.turn;
49
+ if (turnAge >= turnThreshold) {
50
+ newPruneIds.push(id);
51
+ }
52
+ }
53
+ if (newPruneIds.length > 0) {
54
+ state.stats.totalPruneTokens += calculateTokensSaved(state, messages, newPruneIds);
55
+ state.prune.toolIds.push(...newPruneIds);
56
+ logger.debug(`Marked ${newPruneIds.length} error tool calls for pruning (older than ${turnThreshold} turns)`);
57
+ }
58
+ };
59
+ //# sourceMappingURL=purge-errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"purge-errors.js","sourceRoot":"","sources":["../../../lib/strategies/purge-errors.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAC3F,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AAE9C;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CACvB,KAAmB,EACnB,MAAc,EACd,MAAoB,EACpB,QAAqB,EACjB,EAAE;IACN,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;QACzC,OAAM;IACV,CAAC;IAED,sEAAsE;IACtE,MAAM,UAAU,GAAG,eAAe,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAA;IAC3D,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAM;IACV,CAAC;IAED,gCAAgC;IAChC,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAClD,MAAM,WAAW,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAA;IAErE,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAM;IACV,CAAC;IAED,MAAM,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,cAAc,CAAA;IACnE,MAAM,aAAa,GAAG,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,KAAK,CAAA;IAEzD,MAAM,WAAW,GAAa,EAAE,CAAA;IAEhC,KAAK,MAAM,EAAE,IAAI,WAAW,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QAC7C,IAAI,CAAC,QAAQ,EAAE,CAAC;YACZ,SAAQ;QACZ,CAAC;QAED,uBAAuB;QACvB,IAAI,cAAc,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YACzC,SAAQ;QACZ,CAAC;QAED,MAAM,QAAQ,GAAG,yBAAyB,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAA;QAC/D,IAAI,mBAAmB,CAAC,QAAQ,EAAE,MAAM,CAAC,qBAAqB,CAAC,EAAE,CAAC;YAC9D,SAAQ;QACZ,CAAC;QAED,2BAA2B;QAC3B,IAAI,QAAQ,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;YAC9B,SAAQ;QACZ,CAAC;QAED,2CAA2C;QAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,WAAW,GAAG,QAAQ,CAAC,IAAI,CAAA;QACjD,IAAI,OAAO,IAAI,aAAa,EAAE,CAAC;YAC3B,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QACxB,CAAC;IACL,CAAC;IAED,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,KAAK,CAAC,KAAK,CAAC,gBAAgB,IAAI,oBAAoB,CAAC,KAAK,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAA;QAClF,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,CAAA;QACxC,MAAM,CAAC,KAAK,CACR,UAAU,WAAW,CAAC,MAAM,6CAA6C,aAAa,SAAS,CAClG,CAAA;IACL,CAAC;AACL,CAAC,CAAA"}
@@ -0,0 +1,21 @@
1
+ import { PluginConfig } from "../config";
2
+ import { Logger } from "../logger";
3
+ import type { SessionState, WithParts } from "../state";
4
+ /**
5
+ * Read Consolidation Strategy
6
+ *
7
+ * When the same tool+parameter is called multiple times, older outputs
8
+ * are replaced with pointers to the newer output (which has current state).
9
+ *
10
+ * This is different from deduplication:
11
+ * - Deduplication removes exact duplicate calls entirely
12
+ * - Consolidation keeps both calls visible as breadcrumbs, but only the
13
+ * newest one has the full output
14
+ *
15
+ * Rationale:
16
+ * - When same file is read multiple times, older reads become stale
17
+ * - Newer read has current file state (may have been modified)
18
+ * - Keep full output on newer read, pointer on older read
19
+ */
20
+ export declare function readConsolidation(state: SessionState, logger: Logger, config: PluginConfig, messages: WithParts[]): void;
21
+ //# sourceMappingURL=read-consolidation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-consolidation.d.ts","sourceRoot":"","sources":["../../../lib/strategies/read-consolidation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AAClC,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAsB,MAAM,UAAU,CAAA;AAe3E;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAC7B,KAAK,EAAE,YAAY,EACnB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,SAAS,EAAE,GACtB,IAAI,CAgKN"}
@@ -0,0 +1,155 @@
1
+ import { isMessageCompacted } from "../shared-utils";
2
+ import { countTokens } from "./utils";
3
+ /**
4
+ * Primary parameter to use as key for each trackable tool
5
+ */
6
+ const PRIMARY_PARAMS = {
7
+ read: "filePath",
8
+ glob: "pattern",
9
+ grep: "pattern",
10
+ webfetch: "url",
11
+ bash: "command",
12
+ };
13
+ /**
14
+ * Read Consolidation Strategy
15
+ *
16
+ * When the same tool+parameter is called multiple times, older outputs
17
+ * are replaced with pointers to the newer output (which has current state).
18
+ *
19
+ * This is different from deduplication:
20
+ * - Deduplication removes exact duplicate calls entirely
21
+ * - Consolidation keeps both calls visible as breadcrumbs, but only the
22
+ * newest one has the full output
23
+ *
24
+ * Rationale:
25
+ * - When same file is read multiple times, older reads become stale
26
+ * - Newer read has current file state (may have been modified)
27
+ * - Keep full output on newer read, pointer on older read
28
+ */
29
+ export function readConsolidation(state, logger, config, messages) {
30
+ const strategyConfig = config.strategies.readConsolidation;
31
+ if (!strategyConfig?.enabled) {
32
+ return;
33
+ }
34
+ const trackableTools = new Set(strategyConfig.tools ?? ["read", "glob", "grep", "webfetch", "bash"]);
35
+ // Initialize consolidation tracker if not present
36
+ if (!state.consolidationTracker) {
37
+ state.consolidationTracker = new Map();
38
+ }
39
+ // Track: key -> { messageIndex, callId } for the NEWEST occurrence
40
+ const newestOccurrences = new Map();
41
+ // First pass: Find all tool calls and track the newest occurrence per key
42
+ for (let i = 0; i < messages.length; i++) {
43
+ const msg = messages[i];
44
+ // Skip compacted messages
45
+ if (isMessageCompacted(state, msg)) {
46
+ continue;
47
+ }
48
+ const parts = Array.isArray(msg.parts) ? msg.parts : [];
49
+ for (const part of parts) {
50
+ if (part.type !== "tool") {
51
+ continue;
52
+ }
53
+ const toolName = part.tool;
54
+ const toolState = part.state;
55
+ if (!toolName || !toolState)
56
+ continue;
57
+ // Only track configured tools
58
+ if (!trackableTools.has(toolName)) {
59
+ continue;
60
+ }
61
+ // Skip if not completed
62
+ if (toolState.status !== "completed") {
63
+ continue;
64
+ }
65
+ // Get the primary parameter for this tool
66
+ const primaryParam = PRIMARY_PARAMS[toolName];
67
+ if (!primaryParam)
68
+ continue;
69
+ const input = toolState.input;
70
+ if (!input || typeof input !== "object")
71
+ continue;
72
+ const paramValue = input[primaryParam];
73
+ if (!paramValue)
74
+ continue;
75
+ // Create tracker key
76
+ const trackerKey = `${toolName}:${paramValue}`;
77
+ // Always update to the latest (higher index = newer)
78
+ newestOccurrences.set(trackerKey, {
79
+ messageIndex: i,
80
+ callId: part.callID || part.id,
81
+ toolName,
82
+ });
83
+ }
84
+ }
85
+ // Second pass: Replace older occurrences with pointers
86
+ let totalTokensSaved = 0;
87
+ let consolidatedCount = 0;
88
+ for (let i = 0; i < messages.length; i++) {
89
+ const msg = messages[i];
90
+ // Skip compacted messages
91
+ if (isMessageCompacted(state, msg)) {
92
+ continue;
93
+ }
94
+ const parts = Array.isArray(msg.parts) ? msg.parts : [];
95
+ for (const part of parts) {
96
+ if (part.type !== "tool") {
97
+ continue;
98
+ }
99
+ const toolName = part.tool;
100
+ const toolState = part.state;
101
+ if (!toolName || !toolState)
102
+ continue;
103
+ // Only track configured tools
104
+ if (!trackableTools.has(toolName)) {
105
+ continue;
106
+ }
107
+ // Skip if not completed
108
+ if (toolState.status !== "completed") {
109
+ continue;
110
+ }
111
+ // Get the primary parameter for this tool
112
+ const primaryParam = PRIMARY_PARAMS[toolName];
113
+ if (!primaryParam)
114
+ continue;
115
+ const input = toolState.input;
116
+ if (!input || typeof input !== "object")
117
+ continue;
118
+ const paramValue = input[primaryParam];
119
+ if (!paramValue)
120
+ continue;
121
+ // Create tracker key
122
+ const trackerKey = `${toolName}:${paramValue}`;
123
+ const newest = newestOccurrences.get(trackerKey);
124
+ if (!newest)
125
+ continue;
126
+ // If this is NOT the newest occurrence, replace with pointer
127
+ const currentCallId = part.callID || part.id;
128
+ if (newest.messageIndex !== i || newest.callId !== currentCallId) {
129
+ // This is an older occurrence
130
+ const output = toolState.output;
131
+ if (output === undefined || output === null)
132
+ continue;
133
+ const outputStr = typeof output === "string" ? output : JSON.stringify(output);
134
+ // Skip if already consolidated
135
+ if (outputStr.includes("[📍 See later")) {
136
+ continue;
137
+ }
138
+ const outputTokens = countTokens(outputStr);
139
+ // Create pointer to newer occurrence
140
+ const pointer = `[📍 See later ${toolName} at message #${newest.messageIndex + 1}. This output is stale.]`;
141
+ toolState.output = pointer;
142
+ const saved = outputTokens - countTokens(pointer);
143
+ if (saved > 0) {
144
+ totalTokensSaved += saved;
145
+ consolidatedCount++;
146
+ }
147
+ }
148
+ }
149
+ }
150
+ if (consolidatedCount > 0) {
151
+ state.stats.totalPruneTokens += totalTokensSaved;
152
+ logger.debug(`Read consolidation: consolidated ${consolidatedCount} older tool outputs (estimated ${totalTokensSaved} tokens saved)`);
153
+ }
154
+ }
155
+ //# sourceMappingURL=read-consolidation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-consolidation.js","sourceRoot":"","sources":["../../../lib/strategies/read-consolidation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { PluginConfig } from "../config";
2
+ import { Logger } from "../logger";
3
+ import type { SessionState, WithParts } from "../state";
4
+ /**
5
+ * Supersede Writes strategy - prunes write tool inputs for files that have
6
+ * subsequently been read. When a file is written and later read, the original
7
+ * write content becomes redundant since the current file state is captured
8
+ * in the read result.
9
+ *
10
+ * Modifies the session state in place to add pruned tool call IDs.
11
+ */
12
+ export declare const supersedeWrites: (state: SessionState, logger: Logger, config: PluginConfig, messages: WithParts[]) => void;
13
+ //# sourceMappingURL=supersede-writes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"supersede-writes.d.ts","sourceRoot":"","sources":["../../../lib/strategies/supersede-writes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AAClC,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAKvD;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,GACxB,OAAO,YAAY,EACnB,QAAQ,MAAM,EACd,QAAQ,YAAY,EACpB,UAAU,SAAS,EAAE,KACtB,IAoFF,CAAA"}
@@ -0,0 +1,84 @@
1
+ import { buildToolIdList } from "../messages/utils";
2
+ import { getFilePathFromParameters, isProtectedFilePath } from "../protected-file-patterns";
3
+ import { calculateTokensSaved } from "./utils";
4
+ /**
5
+ * Supersede Writes strategy - prunes write tool inputs for files that have
6
+ * subsequently been read. When a file is written and later read, the original
7
+ * write content becomes redundant since the current file state is captured
8
+ * in the read result.
9
+ *
10
+ * Modifies the session state in place to add pruned tool call IDs.
11
+ */
12
+ export const supersedeWrites = (state, logger, config, messages) => {
13
+ if (!config.strategies.supersedeWrites.enabled) {
14
+ return;
15
+ }
16
+ // Build list of all tool call IDs from messages (chronological order)
17
+ const allToolIds = buildToolIdList(state, messages, logger);
18
+ if (allToolIds.length === 0) {
19
+ return;
20
+ }
21
+ // Filter out IDs already pruned
22
+ const alreadyPruned = new Set(state.prune.toolIds);
23
+ const unprunedIds = allToolIds.filter((id) => !alreadyPruned.has(id));
24
+ if (unprunedIds.length === 0) {
25
+ return;
26
+ }
27
+ // Track write tools by file path: filePath -> [{ id, index }]
28
+ // We track index to determine chronological order
29
+ const writesByFile = new Map();
30
+ // Track read file paths with their index
31
+ const readsByFile = new Map();
32
+ for (let i = 0; i < allToolIds.length; i++) {
33
+ const id = allToolIds[i];
34
+ const metadata = state.toolParameters.get(id);
35
+ if (!metadata) {
36
+ continue;
37
+ }
38
+ const filePath = getFilePathFromParameters(metadata.parameters);
39
+ if (!filePath) {
40
+ continue;
41
+ }
42
+ if (isProtectedFilePath(filePath, config.protectedFilePatterns)) {
43
+ continue;
44
+ }
45
+ if (metadata.tool === "write") {
46
+ if (!writesByFile.has(filePath)) {
47
+ writesByFile.set(filePath, []);
48
+ }
49
+ writesByFile.get(filePath).push({ id, index: i });
50
+ }
51
+ else if (metadata.tool === "read") {
52
+ if (!readsByFile.has(filePath)) {
53
+ readsByFile.set(filePath, []);
54
+ }
55
+ readsByFile.get(filePath).push(i);
56
+ }
57
+ }
58
+ // Find writes that are superseded by subsequent reads
59
+ const newPruneIds = [];
60
+ for (const [filePath, writes] of writesByFile.entries()) {
61
+ const reads = readsByFile.get(filePath);
62
+ if (!reads || reads.length === 0) {
63
+ continue;
64
+ }
65
+ // For each write, check if there's a read that comes after it
66
+ for (const write of writes) {
67
+ // Skip if already pruned
68
+ if (alreadyPruned.has(write.id)) {
69
+ continue;
70
+ }
71
+ // Check if any read comes after this write
72
+ const hasSubsequentRead = reads.some((readIndex) => readIndex > write.index);
73
+ if (hasSubsequentRead) {
74
+ newPruneIds.push(write.id);
75
+ }
76
+ }
77
+ }
78
+ if (newPruneIds.length > 0) {
79
+ state.stats.totalPruneTokens += calculateTokensSaved(state, messages, newPruneIds);
80
+ state.prune.toolIds.push(...newPruneIds);
81
+ logger.debug(`Marked ${newPruneIds.length} superseded write tool calls for pruning`);
82
+ }
83
+ };
84
+ //# sourceMappingURL=supersede-writes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"supersede-writes.js","sourceRoot":"","sources":["../../../lib/strategies/supersede-writes.ts"],"names":[],"mappings":"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"}